树
讲二叉树前先大概介绍一下树,此处先放图,树这个数据结构就是来源于生活中的树,把生活中的树倒过来,就是数据结构中的树了。
和树有关的术语
各结点的名称和关系
- 根(root):下图1号
- 孩子(child):比如下图7号是4号的孩子(如果是二叉树,则可以再细化为左孩子)
- 父节点(parent):一个父节点可以有 ≥ 1 \ge 1 ≥1个子节点,比如4号是7号和8号的父结点
- 兄弟节点(siblings):具有相同父亲节点的是兄弟,比如7号和8号
- 叶节点(leaf):没有孩子的节点是叶节点,比如5,9,10,12,13
其他概念
- 路径(path):节点 n 1 , n 2 , … , n k n_1,n_2,\dots, n_k n1,n2,…,nk的一个序列
- 长(length):路径的长为该路径上的边数,即 k − 1 k-1 k−1
- 高度(height):某一结点到自己下面的叶节点的最长路径,叶节点的高度为0,树的高度为根节点的高度
- 深度(depth):对任意节点 n i n_i ni, n i n_i ni的深度为从根节点到 n i n_i ni的唯一路径的长,树的根节点的深度为0,树的深度等于它最深的叶结点的深度
- 层(level):层数即为节点的深度+1
树和图的区别
从树的根到每个叶子节点有且只有一条路径。意味着子节点之间不能有路径,同一个子节点不能有两个以上包括两个的父节点,树中不能有环。
二叉树
最常用的树就是二叉树了,顾名思义,二叉树只有两个分叉,也就是一个父节点最多只能有两个子节点,左孩子和右孩子。
完全二叉树
定义
完全二叉树的叶子只在最底层或次底层,最底层的叶子节点从左往右排列。
满二叉树
一种特殊的完全二叉树,满足完全二叉树的定义且每个叶子必须有左右两个节点
如何表示二叉树呢?
有两种方式,链式存储(基于链表)和顺序存储(基于数组),有没有发现绕来绕去,几乎后面所有看起来高端的数据结构的底层实现方式竟然还是最开始的链表和数组或它们的结合体。构造链式存储的二叉树就是在左孩子和右孩子再次创建BinaryTreeNode这样的节点,并不断重复这个操作。
链式存储
最简单的二叉树的每个Node都是下面这样的结构
//代码都是C语言
typedef struct BinaryTreeNode
{
int data;
struct BinaryTreeNode* lchild; // 左孩子
struct BinaryTreeNode* rchild; // 右孩子
}BinaryTree;
顺序存储
使用数组储存,这里可以发现,如果这棵树不是完全二叉树,那么在数组中间会有很多空洞,从而很浪费空间。下面是总的图片,我们以一个随机的二叉树举例子。一般下标为0的位置是空着的,浪费这一个空间可以很好的把后面的下标理顺。下标为1的位置是root的位置,假设一个节点的下标为
n
n
n,那么它的左孩子的下标即为
2
n
2n
2n,右孩子的下标即为
2
n
+
1
2n+1
2n+1。
二叉树的遍历
假设我们已经有了一棵二叉树,我们的有三种遍历方式,分别是前序遍历,中序遍历和后序遍历。这三种方式都是使用的递归的思想:
- 遍历函数的用途:遍历就是先打印每一个节点的值
- 分解的方式:打印自己这个节点本身的值,然后再继续遍历左孩子和右孩子。
- 结束条件:如果当前节点为空,那么就无法遍历,于是返回
void preOrder(BinaryTreeNode *tree) {
if (tree == NULL)
return;
printf("%d", tree->data);
preOrder(tree->lchild); // 遍历左子树
preOrder(tree->rchild); // 遍历右子树
}
void inOrder(BinaryTreeNode *tree) {
if (tree == NULL)
return;
inOrder(tree->lchild);
printf("%d", tree->data);
inOrder(tree->rchild);
}
void postOrder(BinaryTreeNode *tree) {
if (tree == NULL)
return;
postOrder(tree->lchild);
postOrder(tree->rchild);
printf("%d", tree->data);
}
普通二叉树的查找/修改/删除
查找,修改和删除的操作都需要我们找到那个节点,而我们的二叉树没有任何的其他特点,所以,和链表一样,我们只能一个一个节点遍历来判断该节点是不是我们要找的对象,这一段的时间复杂度是O(n);找到之后再进行修改或删除,因为我们的树没有任何特点,所以我们没有办法在这里开始讲删除操作,这里先跳过,在后面讲二叉树的时候会具体的根据规则讲删除。
// 这段代码可能有点瑕疵,但不影响功能,找到了就打印出来。
void search(BinaryTreeNode *tree) {
if (tree == NULL)
return;
if (tree->data == x){
printf("找到了");
return;
}
search(tree->lchild);
search(tree->rchild);
}
觉得写得好记得点个赞鼓励一下我!