树&二叉树

​ 树是 n(n≥0)个结点的有限集。当 n = 0时,称为空树。在任意一颗非空树中应满足:

(1)有且仅有一个特定的称为根的结点。

(2)当 n > 1时,其余结点可分为 m(m > 0)个互不相交的有限集T~1~, T~2~, ..., T~m~,其中每个集合本身又是一棵树,并且称为根的子树。

​ 树作为一种逻辑结构,同时也是一种分层结构,具有以下两个特点:

(1)树的根结点没有前驱,除根结点外的所有结点有且只有一个前驱。

(2)树中所有结点可以有零个或多个后继。

二叉树

​ 二叉树是另一种树形结构,其特点是每个结点至多只有两颗字数(即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。

​ 与树相似,二叉树也以递归的形式定义,二叉树是 n(n ≥ 0)个结点的有限集合:

(1)或者为空二叉树,即 n = 0。

(2)或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树分别是一颗二叉树。

满二叉树:

如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树 。
1 2 3 4 5 6 7

完全二叉树:

深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树。
1 2 3 4 5 6

普通二叉树:
1 2 3 4 5 6 7 8 9 10 A B D C

二叉查找树:

二叉查找树又称二叉排序树或者二叉搜索树。
1 2 3 4 5 6 7 8 9 10 A B D C

特点:

  • 每个节点上最多有两个子节点。
  • 左子树上所有节点的值都小于根节点的值。
  • 右子树上所有节点的值都大于根节点的值。

目的:

  • 提高检索数据的性能。

二茶树的链式存储

c 复制代码
typedef char BiElemType;
typedef struct BiTNode {
    BiElemType data;
    struct BiTNode *lchild;
    struct BiTNode *rchild;
}

二叉树建树

c 复制代码
#include <stdio.h>
#include <stdlib.h>


typedef char BiElemType;
// 树结点
typedef struct BiTNode {
    // 数据
    BiElemType data;
    // 左子树
    struct BiTNode *lchild;
    // 右子树
    struct BiTNode *rchild;

} BiTNode, *BiTree;

// 用于构建辅助链队结点
typedef struct LinkNode {
    // 用于存储树的某一个结点的地址
    BiTree p_tree;
    struct LinkNode *next;
} LinkNode;


int main() {

    // 创建树根
    BiTree Tree = NULL;

    // 用来指向新申请的树结点
    BiTree tree_new_node;

    // 队列头 队列尾 队列新结点 当前树的父结点(要插入结点的位置)
    LinkNode *q_head = NULL, *q_tail = NULL, *q_new_node = NULL, *q_cur = NULL;

    char c;
    while (scanf("%c", &c)) {
        if ('\n' == c) {
            break;
        }

        // 申请树结点
        // calloc 申请空间并对空间进行初始化为0
        tree_new_node = (BiTree) calloc(1, sizeof(BiTNode));
        tree_new_node->data = c;

        // 队列新结点
        q_new_node = (LinkNode *) calloc(1, sizeof(LinkNode));
        // 新结点p_tree存储新的树结点地址
        q_new_node->p_tree = tree_new_node;
        q_new_node->next = NULL;

        // 如果树为空
        if (NULL == Tree) {
            // 树根
            Tree = tree_new_node;

            // 队首
            q_head = q_new_node;
            // 队尾
            q_tail = q_new_node;
            // 当前树的父结点(要插入结点的位置)
            q_cur = q_new_node;

            // 结束本次循环
            continue;
        } else {
            // 新结点放入队列中
            q_tail->next = q_new_node;
            // 新结点成为新尾部
            q_tail = q_new_node;
        }

        // 当前父结点插入左子树
        if (NULL == q_cur->p_tree->lchild) {
            q_cur->p_tree->lchild = tree_new_node;
        } else if (NULL == q_cur->p_tree->rchild) {
            q_cur->p_tree->rchild = tree_new_node;

            //当前父结点左右子树都有了 队列下一个结点作为树的父结点
            q_cur = q_cur->next;
        }
    }

    return 0;
}

二叉树遍历

深度优先遍历

二叉树的深度优先遍历有三种方式,前序遍历、中序遍历、后序遍历。

  • 前序遍历是先打印自身,再打印左子树,再打印右子树。
  • 中序遍历是先打印左子树,再打印自身,再打印右子树,中序遍历相当于把树压扁。
  • 后序遍历是先打印左子树,再打印右子树,最后打印当前结点。
c 复制代码
#include <stdio.h>
#include <stdlib.h>


typedef char BiElemType;
// 树结点
typedef struct BiTNode {
    // 数据
    BiElemType data;
    // 左子树
    struct BiTNode *lchild;
    // 右子树
    struct BiTNode *rchild;

} BiTNode, *BiTree;

// 用于构建辅助链队结点
typedef struct LinkNode {
    // 用于存储树的某一个结点的地址
    BiTree p_tree;
    struct LinkNode *next;
} LinkNode;


/*
 * 先序遍历
 * 用递归思想解决 把每个结点看作一棵树
 */
void pre_order(BiTree p) {
    if (NULL != p) {
        // 本身
        printf("%c", p->data);
        // 左子树
        pre_order(p->lchild);
        // 右子树
        pre_order(p->rchild);
    }
}

/*
 * 中序遍历
 * 用递归思想解决 把每个结点看作一棵树
 */
void in_order(BiTree p) {
    if (NULL != p) {
        // 左子树
        in_order(p->lchild);
        // 本身
        printf("%c", p->data);
        // 右子树
        in_order(p->rchild);
    }
}


/*
 * 后续遍历
 * 用递归思想解决 把每个结点看作一棵树
 */
void post_order(BiTree p) {
    if (NULL != p) {
        // 左子树
        post_order(p->lchild);
        // 右子树
        post_order(p->rchild);
        // 本身
        printf("%c", p->data);
    }
}


int main() {

    // 创建树根
    BiTree Tree = NULL;

    // 用来指向新申请的树结点
    BiTree tree_new_node;

    // 队列头 队列尾 队列新结点 当前树的父结点(要插入结点的位置)
    LinkNode *q_head = NULL, *q_tail = NULL, *q_new_node = NULL, *q_cur = NULL;

    char c;
    while (scanf("%c", &c)) {
        if ('\n' == c) {
            break;
        }

        // 申请树结点
        // calloc 申请空间并对空间进行初始化为0
        tree_new_node = (BiTree) calloc(1, sizeof(BiTNode));
        tree_new_node->data = c;

        // 队列新结点
        q_new_node = (LinkNode *) calloc(1, sizeof(LinkNode));
        // 新结点p_tree存储新的树结点地址
        q_new_node->p_tree = tree_new_node;
        q_new_node->next = NULL;

        // 如果树为空
        if (NULL == Tree) {
            // 树根
            Tree = tree_new_node;

            // 队首
            q_head = q_new_node;
            // 队尾
            q_tail = q_new_node;
            // 当前树的父结点(要插入结点的位置)
            q_cur = q_new_node;

            // 结束本次循环
            continue;
        } else {
            // 新结点放入队列中
            q_tail->next = q_new_node;
            // 新结点成为新尾部
            q_tail = q_new_node;
        }

        // 当前父结点插入左子树
        if (NULL == q_cur->p_tree->lchild) {
            q_cur->p_tree->lchild = tree_new_node;
        } else if (NULL == q_cur->p_tree->rchild) {
            q_cur->p_tree->rchild = tree_new_node;

            //当前父结点左右子树都有了 队列下一个结点作为树的父结点
            q_cur = q_cur->next;
        }
    }

    // 先序遍历
    printf("Pre Order: ");
    pre_order(Tree);
    printf("\n");

    // 中序遍历
    printf("In Order: ");
    in_order(Tree);
    printf("\n");

    // 后序遍历
    printf("Post Order: ");
    post_order(Tree);
    printf("\n");

    return 0;
}

输入结果:

bash 复制代码
abcdefghi
Pre Order: abdhiecfg
In Order: hdibeafcg
Post Order: hidebfgca

Process finished with exit code 0

广度优先遍历

​ 层次遍历是一种广度优先遍历,层次遍历与层次建树的原理非常相似。层次遍历必须试用辅助队列。

c 复制代码
#include <stdio.h>
#include <stdlib.h>


typedef char BiElemType;
// 树结点
typedef struct BiTNode {
    // 数据
    BiElemType data;
    // 左子树
    struct BiTNode *lchild;
    // 右子树
    struct BiTNode *rchild;

} BiTNode, *BiTree;

// 用于构建辅助链队结点
typedef BiTNode ElemType;
typedef struct LinkNode {
    ElemType *data;
    struct LinkNode *next;
} LinkNode;
typedef struct LinkQueue {
    LinkNode *front, *rear;
} LinkQueue;


/*
 * 队列初始化
 */
void init_queue(LinkQueue &Q) {
    Q.front = Q.rear = (LinkNode *) malloc(sizeof(LinkNode));
    Q.front->next = NULL;
}


/*
 * 队列是否为空
 */
bool queue_empty(LinkQueue Q) {
    return Q.front == Q.rear;
}


/*
 * 入队
 */
void enqueue(LinkQueue &Q, BiTree T) {
    LinkNode *s = (LinkNode *) malloc(sizeof(LinkNode));
    s->data = T;
    s->next = NULL;
    Q.rear->next = s;
    Q.rear = s;
}

/*
 * 出队
 */
bool dequeue(LinkQueue &Q, BiTree &T) {
    // 队列为空
    if (queue_empty(Q)) {
        return false;
    }

    LinkNode *p = Q.front->next;
    T = p->data;

    Q.front->next = p->next;
    if (Q.rear == p) {
        Q.rear = Q.front;
    }
    free(p);
    return true;
}


/*
 * 层序遍历
 *
 * 跟层次建树几乎一样 用辅助队列实现
 */
void level_order(BiTree T) {
    LinkQueue Q;
    init_queue(Q);

    // 树根入队
    enqueue(Q, T);
    // 从队列出队的树结点
    BiTree p;

    // 队列不为空
    while (!queue_empty(Q)) {
        // 出队
        dequeue(Q, p);
        putchar(p->data);

        // 判断当前结点是否有左孩子
        if (NULL != p->lchild) {
            // 左孩子入队
            enqueue(Q, p->lchild);
        }

        // 判断当前结点是否有右孩子
        if (NULL != p->rchild) {
            // 右孩子入队
            enqueue(Q, p->rchild);
        }
    }

}


int main() {

    // 创建树根
    BiTree Tree = NULL;

    // 用来指向新申请的树结点
    BiTree tree_new_node;

    // 队列头 队列尾 队列新结点 当前树的父结点(要插入结点的位置)
    LinkNode *q_head = NULL, *q_tail = NULL, *q_new_node = NULL, *q_cur = NULL;

    char c;
    while (scanf("%c", &c)) {
        if ('\n' == c) {
            break;
        }

        // 申请树结点
        // calloc 申请空间并对空间进行初始化为0
        tree_new_node = (BiTree) calloc(1, sizeof(BiTNode));
        tree_new_node->data = c;

        // 队列新结点
        q_new_node = (LinkNode *) calloc(1, sizeof(LinkNode));
        // 新结点p_tree存储新的树结点地址
        q_new_node->data = tree_new_node;
        q_new_node->next = NULL;

        // 如果树为空
        if (NULL == Tree) {
            // 树根
            Tree = tree_new_node;

            // 队首
            q_head = q_new_node;
            // 队尾
            q_tail = q_new_node;
            // 当前树的父结点(要插入结点的位置)
            q_cur = q_new_node;

            // 结束本次循环
            continue;
        } else {
            // 新结点放入队列中
            q_tail->next = q_new_node;
            // 新结点成为新尾部
            q_tail = q_new_node;
        }

        // 当前父结点插入左子树
        if (NULL == q_cur->data->lchild) {
            q_cur->data->lchild = tree_new_node;
        } else if (NULL == q_cur->data->rchild) {
            q_cur->data->rchild = tree_new_node;

            //当前父结点左右子树都有了 队列下一个结点作为树的父结点
            q_cur = q_cur->next;
        }
    }

    // 层次遍历
    printf("Level Order: ");
    level_order(Tree);

    return 0;
}

输出结果:

bash 复制代码
abcdefghi
Level Order: abcdefghi

Process finished with exit code 0
相关推荐
ChoSeitaku21 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
娅娅梨23 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
DdddJMs__13527 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
兵哥工控28 分钟前
MFC工控项目实例二十九主对话框调用子对话框设定参数值
c++·mfc
我爱工作&工作love我35 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
lexusv8ls600h1 小时前
探索 C++20:C++ 的新纪元
c++·c++20
lexusv8ls600h1 小时前
C++20 中最优雅的那个小特性 - Ranges
c++·c++20
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower1 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归