【数据结构】二叉树的概念
01 概念
定义:二叉树既然叫二叉树,顾名思义即度最大为2的树称为二叉树。 它的度可以为 1 也可以为 0,但是度最大为 2 。
一颗二叉树是节点的一个有限集合,该集合:
① 由一个根节点加上两棵被称为左子树和右子树的二叉树组成
② 或者为空
观察上图我们可以得出如下结论:
① 二叉树不存在度大于 2 的节点,换言之二叉树最多也只能有两个孩子。
② 二叉树的子树有左右之分,分别为左孩子和右孩子。次序不可颠倒,因此二叉树是有序树。
注意:对于任意的二叉树都是由以下几种情况复合而成的:
02 满二叉树
定义:一个二叉树,如果每一层的结点数都达到了最大值(均为2),则这个二叉树就可以被称作为 "满二叉树" 。
换言之,如果一个二叉树的层数为 h,且总结点数为 2的 h 次方 - 1,那么它就是一个满二叉树。
计算公式:
① 已知层数求总数:
② 已知总数求层数:
03 完全二叉树
定义:对于深度为 h 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 h 的满二叉树中编号从 1 至 n 的结点一一对应时称之为完全二叉树。
前 h - 1 层是满的,最后一层不满,但最后一层从左到右是连续的。
完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。所以,满二叉树是一种特殊的完全二叉树(每一层节点均为2)。
常识:
① 完全二叉树中,度为 1 的最多只有 1 个。
② 高度为 的完全二叉树节点范围是
04 二叉树的性质
四点规则:
① 若规定根节点的层数为 1 ,则一颗非空二叉树的第 i 层上最多有 2 的 i - 1 次方个节点。
② 若规定根节点的层数为 1 ,则深度为 h 的二叉树最大节点数是 2 的 h 次方 - 1。
③ 对任何一棵二叉树,如果度为 0 其叶子结点个数为 n0,度为 2 的分支节点个数为 n2,则有n0 = n2 + 1。换言之,度为 0 的永远比度为 2 的多一个叶子结点。
假设二叉树有 n 个结点,从总结点数角度考虑:n = n0 + n1 + n2,从边的角度考虑,n 个结点的任意二叉树,总共有 n - 1 条边。因为度为 0 的结点没有孩子,故度为 0 的结点不产生边; 度为 1 的结点只有一个孩子,故每个度为 1 的结点产生一条边; 度为 2 的结点有 2 个孩子,故每个度为 2 的结点产生两条边,所以总边数为: n1+2*n2,故从边的角度考虑:n - 1 = n1 + 2*n2, n0 + n1 + n2 = n1 + 2*n2 - 1,即:n0 = n2 + 1
④ 若规定根节点的层数为 1 , 具有 n 个节点的满二叉树的深度 h = log(n + 1)。
对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
1. 若 i > 0,i 位置节点的双亲序号:(i - 1) / 2;i = 0,i 为根节点编号,无双亲节点。
2. 若2i + 1 < n,左孩子序号:2i + 1,2i + 1 >= n否则无左孩子。
3. 若2i + 2 < n,右孩子序号:2i + 2,2i + 2 >= n否则无右孩子。
05 二叉树的存储
1 数组存储
一般情况下使用数组来存储,只适合完全二叉树。
如果是非完全二叉树,会造成空间上的浪费。
2 链式存储
typedef struct BinaryTreeNode
{struct BinTreeNode* left; // 左孩子struct BinTreeNode* right; // 右孩子DataType data; // 当前节点值域
}BTree;
06 二叉树的遍历
二叉树的遍历一般有四种方法:前序遍历,中序遍历,后序遍历,层序遍历。
1 前序遍历
先遍历根节点,再依次遍历左子树,右子树。而遍历左子树,又要先遍历根节点,再依次遍历左子树,右子树…直至遍历到空树。
// 递归实现
void PreOrder(BTree*root)
{if (root == NULL)return;printf("%d ", root->data);//根节点PreOrder(root->left);//左子树PreOrder(root->right);//右子树
}
2 中序遍历
先遍历左子树,再依次遍历根节点,右子树。而遍历左子树,又要先遍历左子树,再依次遍历根节点,右子树…直至遍历到空树。
// 递归实现
void Inorder(BTree*root)
{if (root == NULL)return;PreOrder(root->left);//左子树printf("%d ", root->data);//根节点PreOrder(root->right);//右子树
}
3 后序遍历
先遍历左子树,再依次遍历右子树,根节点。而遍历左子树,又要先遍历左子树,再依次遍历右子树,根节点…直至遍历到空树。
// 递归实现
void Postorder(BTree*root)
{if (root == NULL)return;PreOrder(root->left);//左子树PreOrder(root->right);//右子树printf("%d ", root->data);//根节点
}
4 层序遍历
层序遍历顾名思义就是一层一层地遍历,这时就需要借助一个数据结构:队列来辅助实现。
void leverOrder(BTree* root, Queue* pq)
{if (root == NULL)return;QueuePush(pq, root);//插入第一个节点while (!QueueEmpty(pq))//队列不为空{BTree* p = QueueFront(pq);printf("%d ", p->val);QueuePop(pq);if (p->left != NULL)//带入左孩子{QueuePush(pq, p->left);}if (p->right != NULL)//带入右孩子{QueuePush(pq, p->right);}}
}