树&二叉树

​ 树是 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
相关推荐
**K4 分钟前
C++ 智能指针使用不当导致内存泄漏问题
开发语言·c++·算法
湫兮之风26 分钟前
C++:.front()函数作用
开发语言·c++
小老鼠不吃猫27 分钟前
力学笃行(四)Qt 线程与信号槽
c++·qt·信息可视化
小羊子说27 分钟前
Android 开发中 C++ 和Java 日志调试
android·java·c++
TechQuester32 分钟前
解决GPT-4o耗电难题!DeepMind新算法训练效率提升13倍,能耗降低10倍!
java·c++·人工智能·python·算法·chatgpt
流星白龙40 分钟前
【C语言题目】34.猜凶手
c语言·开发语言
XSTIT41 分钟前
数据结构--二叉树相关题2(OJ)
数据结构·算法
续亮~1 小时前
6、Redis系统-数据结构-06-跳表
java·数据结构·数据库·redis·后端·缓存
观鉴词recommend1 小时前
【c++刷题笔记-动态规划】day32: 509. 斐波那契数 、 70. 爬楼梯 、 746. 使用最小花费爬楼梯
c++·笔记·算法·leetcode·动态规划
DieSnowK1 小时前
[C++][ProtoBuf][初识ProtoBuf]详细讲解
开发语言·c++·google·协议·序列化·反序列化·protobuf