目录
二叉树定义
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分。
二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。
二叉树图解
特殊的二叉树
完全二叉树:
一棵深度为k的有n个结点的 二叉树 ,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与 满二叉树 中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。.
满二叉树:
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。. 也就是说,如果一个二叉树的层数为K,且结点总数是 (2^k) -1 ,则它就是满二叉树。
二叉搜索树:
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的值; 若它的右子树不空,则右子树上所有结点的值均大于它的值
二叉平衡树(AVL):
平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。
B树,B+树:
一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:根结点至少有两个子女;每个非根节点所包含的关键字个数 j 满足:[m/2] - 1 <= j <= m - 1;除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:[m/2] <= k <= m ;所有的叶子结点都位于同一层。
红黑树:
红黑树是每个结点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求: 1.结点是红色或黑色;2.根结点是黑色;3. 所有叶子都是黑色(叶子是NIL结点);4. 每个红色结点的两个子结点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色结点);5. 从任一结点到其每个叶子的所有路径都包含相同数目的黑色结点。
二叉树的性质
(1):一颗非空二叉树的第n层最多有2^(n-1)个节点
(2):若一颗非空二叉树的深度为h,那么它最多能有2^h-1个节点
(3):具有N个节点的完全二叉树,它的深度h=ceil(㏒₂(N+1))ceil表示向上取整,例如ceil(2.23)=ceil(2.64)=ceil(3)=3
★(4):一颗二叉树,2度节点和叶子节点(度为0)满足以下关系:D₀=D₂+1 也就是说叶子节点个数始终等于二度节点个数加一
★(5):一颗二叉树,若他的节点个数是2n个,那么他的叶子节点个数为n个
二叉树的基本代码实现
二叉数的定义
二叉树通常采用孩子表示法,因此定义的代码为:
//定义二叉树
typedef struct BTNode {
char data;//数据域
BTNode* left;//指向左孩子节点
BTNode* right;//指向右孩子节点
}BinaryTree;
/*该结构体有两个名字
分别翻译为节点(BTNode),二叉树(BinaryTree)加以区分
*/
二叉树的输入(创建)
由于二叉树是一对多结构,我们无法得知某某数据,它具体是谁的孩子节点,因此使得它不能像链表这样循环输入,而只能递归回溯输入。
所以涉及到回溯算法
★★★二叉树包含大量的回溯算法
回溯算法,又称为“试探法”。解决问题时,每进行一步,都是抱着试试看的态度,如果发现当前选择并不是最好的,或者这么走下去肯定达不到目标,立刻做回退操作重新选择。这种走不通就回退再走的方法就是回溯算法。
二叉树创建代码:
//创建二叉树
void BTCreate(BinaryTree* &root) {
BTNode *q; //创建新节点
q=new BTNode;//初始化节点,相当于malloc函数
cin>>q->data;//输入数据
if(q->data=='#') {//判断是否为'#"
root=q=NULL; //若是,则设置虚拟节点q,并接到二叉树上
return; //返回上一个节点
}
root=q; //若否,则将节点q接在二叉数上
BTCreate(root->left); //进入左孩子
BTCreate(root->right); //进入右孩子
return; //返回上一个节点
}
二叉树创建图解
以上就是二叉树:ABC##D###的创建过程。
主要还是回溯算法,当创建了空节点的时候,就回溯返回上一个节点,不再递归分治。
二叉树的遍历
二叉树的深度优先遍历
前序遍历,又叫先根遍历。遍历顺序:根 - > 左孩子 - >右孩子。
//前序遍历
void BTProOrder(BinaryTree* &root) {
if(root==NULL) return;
cout<<root->data<<" ";
BTProOrder(root->left);
BTProOrder(root->right);
}
中序遍历,又叫中根遍历。遍历顺序:左孩子 -> 根 -> 右孩子
//中序遍历
void BTInOrder(BinaryTree* &root) {
if(root==NULL) return;
BTInOrder(root->left);
cout<<root->data<<" ";
BTPostOrder(root->right);
}
后序遍历,又叫后根遍历。遍历顺序:左孩子 -> 右孩子 -> 根
//后序遍历
void BTPostOrder(BinaryTree* &root) {
if(root==NULL) return;
BTPostOrder(root->left);
BTPostOrder(root->right);
cout<<root->data<<" ";
}
二叉树的广度优先遍历
层序遍历。顾名思义,就是按照树的层次,从第1层到第n层,从上到下,从左到右进行遍历输出。
像这样,就是层序遍历。
//层序遍历
void BTLevelOrder(BinaryTree* &root) {
queue<BinaryTree*>Q;
Q.push(root);
BTNode *q;
while(!Q.empty()) {
cout<<Q.front()->data;
q=Q.front();
Q.pop();
if(q->left!=NULL) Q.push(q->left);
if(q->right!=NULL) Q.push(q->right);
}
}
二叉树的基本功能函数的实现
返回宽度和深度
深度
int Height(BinaryTree* &root) {
if(root==NULL) return 0;
else return max(Height(root->left),Height(root->right))+1;//左子树或者右子树的最大值
}//max(a,b):返回a,b的最大值
宽度
int a[10005];//数组a表示储存第key层有几个节点
//key表示第几层,刚开始key为1,也就是第一层
//ans表示最终答案
void Wide(BinaryTree* &root,int key,int &ans) {
if(root==NULL) return;
else {
a[key]++;
ans=max(ans,a[key]);
Wide(root->left,key+1,ans);
Wide(root->right,key+1,ans);
}
}
返回节点个数
返回二度节点
int node_two(BinaryTree* &root) {
if(root==NULL) return 0;
//同时搜索左右孩子节点
//当左右孩子节点都不为空,说明是2度节点,值加一
if(root->left!=NULL&&root->right!=NULL) return node_two(root->left)+node_two(root->right)+1;
//否则直接搜索
else return node_two(root->left)+node_two(root->right);
}
返回一度节点
int node_one(BinaryTree* &root) {
if(root==NULL) return 0;
//同时搜素左右孩子节点
//当左右孩子节点有一个不为空,一个为空,说明是1度节点,值加一
if(root->left!=NULL&&root->right==NULL || root->right!=NULL&&root->left==NULL) return node_one(root->left)+node_one(root->right)+1;
//否则直接搜索
else return node_one(root->left)+node_one(root->right);
}
返回叶子节点(0度)
int node_zero(BinaryTree* &root) {
if(root==NULL) return 0;
//同时搜素左右孩子节点
//当左右孩子节点都为空,说明是0度节点,返回1
if(root->left==NULL && root->right==NULL) return 1;
//否则直接搜索
else return node_zero(root->left)+node_zero(root->right);
}
返回空节点(NULL)
int node_NULL(BinaryTree* &root) {
//当该节点为空,返回1
if(root=NULL) return 1;
//否则搜索左右孩子
else return node_NULL(root->left)+node_NULL(root->right);
}
二叉树基本特点函数的实现
翻转二叉树
翻转二叉树可以先镜面先序遍历(根->右孩子->左孩子),并储存。然后再依次先序创建之前储存的镜面先序遍历。
//创建翻转二叉树
char node[10005];
int sum2=0;
void create(BinaryTree* &root) {
BTNode *q;
q=new BTNode;
q->data=node[sum2++];
if(q->data=='#') {
root=q=NULL;
return;
}
root=q;
create(root->left);
create(root->right);
}
//镜面先序遍历
int sum1=0;
void sy_BTProOrder(BinaryTree* &root) {
if(root==NULL) {
node[sum1++]='#';
return;
}
node[sum1++]=root->data;
sy_BTProOrder(root->right);
sy_BTProOrder(root->left);
}
二叉树性质的判断
完全二叉树
判断是否为完全二叉树,根据定义,和宽度优先遍历(层序遍历)有点类似
bool isCompleteTree(BinaryTree* &root) {
queue<BinaryTree*>Q;
Q.push(root);
BTNode *q;
while(!Q.empty()) {
if(Q.front()==NULL) break;
q=Q.front();
Q.pop();
Q.push(q->left);
Q.push(q->right);
}
while(!Q.empty()) {
if(Q.front()!=NULL) return false;
Q.pop();
}
return true;
}
平衡二叉树
根据定义,判断左右子树深度的绝对值之差,若不大于1,并且左右子树也为平衡二叉树,则该树为平衡二叉树。
//求二叉树深度
int depth(BinaryTree* &root) {
if(root==NULL) return 0;
else return max(depth(root->left),depth(root->right))+1;
}
//判断是否为平衡二叉树
bool isBalanced(BinaryTree* &root) {
if(root==NULL) return true;
int leftDepth=depth(root->left);
int rightDepth=depth(root->right);
if(abs(leftDepth-rightDepth)>1) return false;
//判断左右子树是否也为平衡二叉树
return isBalanced(root->left) && isBalanced(root->right);
}
对称二叉树
就像照镜子一样,从根开始。整两个遍历轴,一个往左遍历,一个往右遍历,形成镜面对称。
若其中有不同,那么结束遍历,返回false。
//两个遍历轴,遍历函数
bool symmetry(BinaryTree* &rootL,BinaryTree* &rootR) {
if(rootL==NULL&&rootR==NULL) return true;
if(rootL==NULL||rootR==NULL) return false;
if(rootL->data!=rootR->data) return false;
else return (rootL->left,rootR->right) && (rootL->right,rootR->left);
}
//主判断函数
bool isSymmetryTree(BinaryTree* &root) {
if(root==NULL) return true;
return symmetry(root->left,root->right);
}
单值二叉树
单值二叉树是指所有节点的data都一样
bool isUnivalTree(BinaryTree* &root) {
if(root==NULL) return true;
if(root->data != root->left->data) return false;
if(root->data != root->right->data) return false;
return isUnivalTree(root->left) && isUnivalTree(root->right);
}