相关概念
Ref:https://www.cnblogs.com/idorax/p/6441043.html(非常清楚)
https://blog.youkuaiyun.com/u013834525/article/details/80506126(面试笔试知识点!重)
https://www.jianshu.com/p/bf73c8d50dc2 (深入学习二叉树,概念及笔记!)
《大话数据结构》
1 树
树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构。没有结点的树称为空(null或empty)树。一棵非空的树包括一个根结点,还(很可能)有多个附加结点,所有结点构成一个多级分层结构。
1.1 度
结点拥有的子树数目称为结点的度。
1.2 树的深度
树中结点的最大层次数称为树的深度或高度。图所示树的深度为4
1.3 树的高度
从下至上,叶结点的高度是0(也有说1)
2 二叉树(Binary Tree)
每个结点至多拥有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分,其次序不能任意颠倒。
2.1 二叉树的性质
a.若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0)。
b.高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1)。 (空树的高度为-1)
c.对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n, 则m = n + 1。
2.3 完美二叉树(Perfect Binary Tree)
一个深度为k(>=-1)且有2^(k+1) - 1个结点的二叉树称为完美二叉树
换句话说:树是满的,还是二叉的
图是这样的:
2.3 完全二叉树(Complete Binary Tree)
完全二叉树从根结点到倒数第二层满足完美二叉树,最后一层可以不完全填充,其叶子结点都靠左对齐
下图就不是一棵完全(Complete)二叉树
如果将编号11(K)结点从编号6(E)的左儿子位置移动到编号5(E)的右儿子位置,则变成一棵完全(Complete)二叉树。
其实,理解完全(Complete)二叉树可以借助于栈(stack)的思想。 例如,把第一个图中的完美(Perfect)二叉树的所有结点按照编号1, 2, 3, …, 15依次入栈(push)。 那么,对栈的每一次出栈(pop)操作后,栈里保存的结点集对应到图上去都是一棵完全(Complete)二叉树
2.4 完满二叉树(Full Binary Tree)
所有非叶子结点的度都是2
换句话说:只要你有孩子,你就必然是有两个孩子。
例题:
例:
一个具有767个节点的完全二叉树,其叶子节点的个数为____
A . 383
B . 384
C . 385
D . 386
n = n2+n1+no
n0 = n2 + 1
可得方程:n0= (768-n1) / 2,又因完全二叉数节点为1的数要不为1要不为0,故选B。
n:总节点数
n2:度为2的节点数
n1:度为1的节点书
n0:度为0节点数
2.5 二叉树遍历
https://www.cnblogs.com/fanguangdexiaoyuer/p/10493104.html#_label0_6(递归非递归都有!非递归:栈和队列)
前序遍历(前根遍历):根——>左——>右
中序遍历(中根遍历):左——>根——>右
后序遍历(后根遍历):左——>右——>根
已知前序和中序,求后序问题, 前序 ABDGCEFH 中序 DGBAECHF
解法:根据前序、中序综合判断画出树的节点图,然后再写后序遍历:DGBEHFCA
(前序和中序的子树也满足前序或中序的规则)
二叉树的深度优先遍历(DFS)与广度优先遍历(BFS)
DFS深度优先遍历:从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止。利用数据结构“栈”,父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点。
DFS:ABDGCEFH
https://cuijiahua.com/blog/2017/12/basis_22.html(广度遍历讲解!)
BFS广度优先遍历:从根节点出发,在横向遍历二叉树层段节点的基础上纵向遍历二叉树的层次。利用数据结构“队列”,父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点。
BFS:ABCDGEFH
2.7 知二求一,还原二叉树
https://blog.youkuaiyun.com/qq_33396481/article/details/80657546
- 前序的第一个是整个树的根
- 后序的最后一个是整个树的根
- 中序用来判别左右子树的划分
- 前序序列中左子树部分的第一个节点是左子树的根节点(前序序列的左子树还是前序序列)
- 前序序列中右子树部分的第一个节点是右子树的根节点(前序序列的右子树还是前序序列)
- 注意:已知前序和后序遍历不能唯一确定一棵二叉树,已知前中可以唯一确定,已知后中也可以。(递归可以还原)
其实,只要知道其中任意两种遍历的顺序,我们就可以推断出剩下的一种遍历方式的顺序,这里我们只是以:知道前序遍历和中序遍历,推断后序遍历作为例子,其他组合方式原理是一样的。要完成这个任务,我们首先要利用以下几个特性:
- 特性A,对于前序遍历,第一个肯定是根节点;
- 特性B,对于后序遍历,最后一个肯定是根节点;
- 特性C,利用前序或后序遍历,确定根节点,在中序遍历中,根节点的两边就可以分出左子树和右子树;
- 特性D,对左子树和右子树分别做前面3点的分析和拆分,相当于做递归,我们就可以重建出完整的二叉树;
3. 二叉树的计算性质
a. 二叉树的第i层上最多有2i-1个节点
b.深度为k的二叉树最多有2k-1个节点
c.对任何一棵树,如果其终端节点数为n0,度为2的节点数为n2,则n0=n2+1。
d.具有n个节点的完全二叉树的深度为[log2n]+1,其中[x]表示不大于x的最大整数。
e.对有n个节点的完全二叉树,按层序遍历编号,对于第i个节点,其左子树是2i,如2i>n则不存在左子树, 是。叶节点。右子树是2i+1,如2i+1则不存在右子树。由此可得,双亲节点是[i/2]
4. 二叉树查找相关知识点
4.1 二叉树的应用和时间复杂度
二叉树是一种常见的数据结构,常常用于查找,也运用于unix等常见操作系统的文件系统中。c++STL(标准模板库)中的set和map也使用二叉树中的红黑树实现。
二叉树的查找思想基于:在二叉树中,对于任意节点N,左子树中的所有项的值不大于节点N中存储的值,左子树中的所有项的值不小于节点N中存储的值。
这样,在查找时,只需要不断比较需要查找的x与N的大小,若小于N中的值,只需要搜索左子树,若大于N中的值,只需要搜索右子树。这样每次就能缩小搜索的范围。经过证明,普通二叉树的平均时间复杂度是O(LogN)。
4.2 二叉查找树与二分查找
看到这里,我们发现其实二叉树的搜索思想和二分查找一致,每次不断的减少搜索范围。但是二者之间还是有区别的。
对于二分查找而言,每次的时间复杂度不会超过O(LogN)。但是对于二叉树,搜索时间的复杂度取决于树的形状。在最坏情况下可能达到O(N)。如下图,如果要找到10,则要查找5次。
这是因为,二分查找一般基于数组,如果需要插入或删除数据,则会带来很大的开销。因为每次插入或者删除数据需要将改变节点之后的数据往后挪或者往前挪。但是对于二叉树而言,只需要改变一下指向下一个节点的指针就可以很方便的实现插入或者删除。而且一些特殊的二叉树如红黑树可以保证查找的最坏复杂度不超过O(LogN)。
所以,如果是对于静态数据,不需要改变的数据而言,采用数组存储,使用二分查找比较好。而对于动态数据,需要频繁插入或者删除数据的,采取二叉树存储是较好的。
4.3 二叉查找树、平衡二叉树
二叉查找树(Binary Search Tree、BST):又叫二叉排序树,可以是空树,左子树比双亲小,右子树比双亲大。如果按照中序遍历,则刚好按从小到大的顺序排列。一般在结构中加一个数量来实现重复数的插入,具体的插入、删除、查找操作看参考。
二叉查找树参考(二叉树系列作者,不错,模板类例子)
※平衡二叉树:平衡的二叉查找树(AVL,发明者为Adel'son-Vel'skii 和 Landis),树的左子树和右子树高度之差不超过1层,超过一层需要旋转,否则失去平衡。平衡二叉树可以确保查找是严格的O(logN)(普通二叉查找树在极限环境下是O(N))。左子树和右子树的深度之差称为平衡因子,其只能为-1、1、0
与二叉查找树相比:
二叉查找树由于其在有序序列插入时就会退化成单链表(时间复杂度退化成 O(n)),AVL-tree就克服了上述困难。AVL-tree是一个“加上了平衡条件的”二叉搜索树,平衡条件确保整棵树的深度为O(log n)。
平衡二叉树参考一(AVL代码,旋转增加删除等操作,但有错误!)
平衡二叉树参考二
平衡二叉树参考三
平衡二叉树AVL(C++封装+模板)(似乎从书中源码而来,比较正确)
(以上网页的代码参照https://www.sanfoundry.com/cpp-program-implement-avl-trees/)
AVL旋转(其中使用了反转链表的相关算法!:交换头和下一个结点)
四种情况:
单旋转:
双旋转(由两次单旋转而成):
疑问:实际如下情况,也能通过单旋转得到平衡二叉树,只是多一层深度?(答:不过这不包含所有情况,所以从代码实现方面考虑,包含所有情况的两次单旋更合适!如下,就必须用两次单旋)
但如果单旋转后,新的根不合适(或依然不是平衡二叉树),就需要两次单旋(可以覆盖所有情况):
代码实现看后面:(2种方法)
节点的平衡因子是通过计算其左子树和右子树的差得来的,这里有两种考虑方式:
1. 每次都计算一次(递归求深度)。
2. 将平衡因子作为一个成员变量保存在节点中,平衡性发生变化的时候更新。
红黑树:
代码实现:
1.二叉树
https://blog.youkuaiyun.com/taotao1990228/article/details/27652679
https://blog.youkuaiyun.com/lingling_nice/article/details/80960439
https://blog.youkuaiyun.com/yujin753/article/details/43149803(2种建立及3种遍历,c/c++,非常好)
https://blog.youkuaiyun.com/qq_39290007/article/details/79515041(C++模板类实现,结点,高度等各种操作)
定义:
typedef char Elemtype; //对于C++可以使用string,更好
typedef struct BinaryTreeNode{
Elemtype value;
struct BinaryTreeNode *left;
struct BinaryTreeNode *right;
};
Code(三种创立方法,前中后序遍历)
#include <iostream>
#include <string>
using namespace std;
typedef string Elemtype;
struct BinaryTreeNode
{
Elemtype value;
BinaryTreeNode* lchild;
BinaryTreeNode* rchild;
};
//前序创建
void CreateBinaryTree(BinaryTreeNode** T)
{
string data;
cin >> data;
//结点不存在
if (data == "#")//此处,string是"",但是char是''
{
*T = nullptr;
}
else
{
*T = new BinaryTreeNode;
(*T)->value = data;
CreateBinaryTree(&(*T)->lchild);
CreateBinaryTree(&(*T)->rchild);
}
}
//形参使用引用,BinaryTreeNode* &T表示指针的引用,
//T也就是一个指针的别名,把他当作指针来直接操作就行
void CreateBinaryTree2(BinaryTreeNode* &T)
{
string data;
cin>>data;
if(data == "#")
{
T = nullptr;
return;
} else {
T = new BinaryTreeNode;//dont forget need new a memory!
T->value = data;
CreateBinaryTree2(T->lchild);
CreateBinaryTree2(T->rchild);
}
}
//通过获取函数返回指针(也就是指针所指的地址),来递归创建,不需要二级指针
BinaryTreeNode* CreateBinaryTree3()
{
BinaryTreeNode* T;
string data;
cin>>data;
if (data == "#")
{
T = nullptr;
} else {
T = new BinaryTreeNode;
T -> value = data;
T -> lchild = CreateBinaryTree3();
T -> rchild = CreateBinaryTree3();
}
return T;
}
// 先 序
void PreorderTree(BinaryTreeNode *T)
{
if(T==nullptr)
return;
cout<<T->value<<" ";//根
PreorderTree(T->lchild);//左
PreorderTree(T->rchild);//右
}
// 中序:实际为交换打印顺序
void InorderTree(BinaryTreeNode *T)
{
if(T!=nullptr)
{
InorderTree(T->lchild); //左
cout<<T->value<<" "; //根
InorderTree(T->rchild); //右
}
}
//后序
void PostorderTree(BinaryTreeNode *T)
{
if(T==nullptr)
return;
PostorderTree(T->lchild);
PostorderTree(T->rchild);
cout<<T->value<<" ";
}
//广度优先:层次遍历(深度优先也就是前序遍历)
public void levelTraverse(TreeNode root) {
if (root == null) {
return;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
System.out.print(node.val+" ");
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
//非递归,用栈
public void preOrderTraverse2(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode pNode = root;
while (pNode != null || !stack.isEmpty()) {
if (pNode != null) {
System.out.print(pNode.val+" ");
stack.push(pNode);
pNode = pNode.left;
} else { //pNode == null && !stack.isEmpty()
TreeNode node = stack.pop();
pNode = node.right;
}
}
}
int main() {
// your code goes here
BinaryTreeNode* head = new BinaryTreeNode;
//CreateBinaryTree(&head);
//CreateBinaryTree2(head);
head = CreateBinaryTree3();
cout<<"PreorderTree:"<<endl;
PreorderTree(head);
cout<<"\n"<<"InorderTree:"<<endl;
InorderTree(head);
cout<<"\n"<<"PostorderTree:"<<endl;
PostorderTree(head);
return 0;
}
二叉排序树创建、查找、插入、删除操作 《大话数据结构》 c++实现代码
//二叉排序树
#include<iostream>
using namespace std;
typedef int status;
#define true 1
#define false 0
//二叉链表结点结构定义
typedef struct Bitnode
{
int data;
struct Bitnode *left,*right;
}Bitnode,*Bitree;
//操作
status Searchbst(Bitree T,int key,Bitree f,Bitree *p);
status Insertbst(Bitree *T,int key);
status Delete(Bitree *p);
status Deletebst(Bitree *T,int key);
void Createbst(Bitree *T,int a[],int n);
void Showbst(Bitree T); //中序遍历输出二叉树
//二叉排序树的查找,查找T中是否存在Key
//f指向T的双亲,当T指向根节点时,因此f的初始调用值为Null
//查找成功,指针p指向该数据元素的结点,返回TRUE
//查找失败,P指向查找路径上访问的最后一个元素,返回false
status Searchbst(Bitree T,int key,Bitree f,Bitree *p)
{
if(!T) //查找不成功,用来判断当前二叉树是否到叶子结点
{
*p=f;
return false;
}
else if(key==T->data) //查找成功
{
*p=T;
return true;
}
else if(key<T->data)
return Searchbst(T->left,key,T,p);
else
return Searchbst(T->right,key,T,p);
}
//二叉排序树的插入操作
//当T中不存在关键字等于key的数据元素时,插入key并返回true,否则返回FALSE
status Insertbst(Bitree *T,int key)
{
Bitree p,s;
if(!Searchbst(*T,key,NULL,&p)) //如果查找不成功
{
s=(Bitree)malloc(sizeof(Bitnode));
s->data=key;
s->left=s->right=NULL;
if(!p) //此时的p指向查找路径上访问的最后一个元素,如果p为空,即p=Null
*T=s;
else if(key<p->data)
p->left=s;
else
p->right=s;
return true;
}
else //如果查找成功
return false;
}
//二叉排序树的删除操作
//当T中存在关键字等于key的数据元素时,删除该数据元素结点
status Deletebst(Bitree *T,int key)
{
if(!*T) //不存在关键字等于key的数据元素
return false;
else
{
if(key==(*T)->data)
return Delete(T);
else if(key<(*T)->data)
return Deletebst(&(*T)->left,key);
else
return Deletebst(&(*T)->right,key);
}
}
//从二叉排序树中删除结点p,并重接它的左子树或者右子树
status Delete(Bitree *p)
{
Bitree q,s;
if((*p)->right==NULL) //右子树为空
{
q=*p;
*p=(*p)->left;
free(q);
}
else if((*p)->left==NULL) //左子树为空
{
q=*p;
*p=(*p)->right;
free(q);
}
else //左右子树均不为空
{
q=*p;
s=(*p)->left;
while(s->right)
{
q=s;
s=s->right;
}
(*p)->data=s->data;
if(q!=*p)
q->right=s->left; //重接q的右子树
else
q->left=s->left; //重接q的左子树
free(s);
}
return false;
}
void Createbst(Bitree *T,int a[],int n)
{
int i;
for(i=0;i<n;i++)
{
Insertbst(T,a[i]);
}
}
void Showbst(Bitree T)
{
if(T)
{
Showbst(T->left);
cout<<T->data<<" ";
Showbst(T->right);
}
}
int main()
{
int a[]={62,88,58,47,35,73,51,99,37,93};
Bitree T=NULL;
//创建二叉排序树
Createbst(&T,a,10);
cout<<"中序遍历的结果为:"<<endl;
Showbst(T);
cout<<endl;
//在二叉排序树中查找结点
int b=58; //需要查找的值
Bitree p=NULL;
if(!Searchbst(T,b,NULL,&p))
cout<<"没有查找到!"<<endl;
else
cout<<"查找结果为:\n"<<"指针:"<<p<<endl
<<"指针的值为:"<<p->data<<endl;
//在二叉排序树中插入56
Insertbst(&T,56);
cout<<"中序遍历的结果为:"<<endl;
Showbst(T);
cout<<endl;
//在二叉排序树中删除56
Deletebst(&T,56);
cout<<"中序遍历的结果为:"<<endl;
Showbst(T);
cout<<endl;
system("pause");
return 0;
}
※删除(难点)
https://blog.youkuaiyun.com/zhangpinghao/article/details/8159876
二叉排序树删除节点的几种方法
1:用节点左子树的最右边的元素替换并删除该结点,相当于用前继节点替代
2:用节点右子树的最左边的元素替代并删除该结点,相当于用后继节点替代
以上两种都不改变中序遍历二叉树所得的顺序(简洁,后续二叉平衡树删除代码用此方法)
3:设要删除的节点是B,节点B是节点A的左子树。删除节点B以后,令B的左子树为A的左子树,B的右子树加到B的左子树的最右边。
4:http://www.cppblog.com/guogangj/archive/2009/10/26/99502.html,假设要删除节点A,则(考虑节点A的左子树,找出左子树中最大的节点并与A替换,若此时A为叶子节点,则直接删除,否则重复括号内的过程)。
自己的代码(删除后似乎不是必然为平衡二叉树。。可能有bug,最好用rebuildBST来遍历平衡,然后在最后调用)
#include <iostream>
#include <string>
#include<algorithm>
using namespace std;
typedef int Elemtype;
struct BinaryTreeNode
{
Elemtype value;
BinaryTreeNode* lchild;
BinaryTreeNode* rchild;
};
BinaryTreeNode* llRotation(BinaryTreeNode* T)
{
BinaryTreeNode* temp = T->lchild;
T->lchild = temp -> rchild;
temp->rchild = T;
return temp;
//为何需要临时变量的原因如下:lchild用了两次,第二次是在改变指向地址之后
//T->lchild = T->lchild ->rchild;
//T->lchild->rchild = T;
}
BinaryTreeNode* rrRotation(BinaryTreeNode* T)
{
BinaryTreeNode* temp = T->rchild;
T->rchild = temp -> lchild;
temp ->lchild = T;
return temp;
}
//左子树右结点:子树先左旋,父母树再右旋(调用rr和ll)
BinaryTreeNode* lrRotation(BinaryTreeNode* T)
{
T->lchild = rrRotation(T->lchild);
return llRotation(T);
}
//右子树左结点:子树先右旋,父母树再左旋(调用ll和rr)
BinaryTreeNode* rlRotation(BinaryTreeNode* T)
{
T->rchild = llRotation(T->rchild);
return rrRotation(T);
}
int getHigh(BinaryTreeNode* T)
{
int high = 0;
if(T != nullptr)
{
int lhigh = getHigh(T->lchild);
int rhigh = getHigh(T->rchild);
//int maxLayer = (lhigh>rhigh?lhigh:rhigh);
int maxLayer = max(lhigh,rhigh);
high = maxLayer +1;
}
return high;
}
int getHighDiff(BinaryTreeNode* T)
{
int lhigh = getHigh(T->lchild);
int rhigh = getHigh(T->rchild);
return (lhigh-rhigh);
}
BinaryTreeNode* balance(BinaryTreeNode* T)
{
int highDiff = getHighDiff(T);
if(highDiff>1)
{
if(getHighDiff(T->lchild)>0)
{
T = llRotation(T);
}
else if(getHighDiff(T->lchild)<0)
{
T = lrRotation(T);
}
}
if(highDiff<-1)
{
if(getHighDiff(T->rchild)<0)
{
T = rrRotation(T);
}
else if(getHighDiff(T->rchild)>0)
{
T = rlRotation(T);
}
}
return T;
}
BinaryTreeNode* insertBST(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
T = new BinaryTreeNode();
T->value = data;
T->lchild = nullptr;
T->rchild = nullptr;
return T;
}
if(data < T->value)
{
T->lchild = insertBST(T->lchild,data);
T = balance(T);
}
else if(data > T->value)
{
T->rchild = insertBST(T->rchild,data);
T = balance(T);
}
else
{
//here mean if same value,just return same point!dont do recursion
cout<<"same data,not do recursion!"<<endl;
}
return T;
}
BinaryTreeNode* CreateTree()
{
BinaryTreeNode* result = nullptr;
int data;
for(int i = 0;i<10;i++)
{
cin>>data;
result = insertBST(result,data);
}
return result;
}
//function2 can insert same value!
BinaryTreeNode* insertBST2(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
T = new BinaryTreeNode();
T->value = data;
T->lchild = nullptr;
T->rchild = nullptr;
return T;
}
if(data < T->value)
{
T->lchild = insertBST2(T->lchild,data);
}
else if(data >= T->value)
{
T->rchild = insertBST2(T->rchild,data);
}
return T;
}
// 先 序
void PreorderTree(BinaryTreeNode *T)
{
if(T==nullptr)
return;
cout<<T->value<<" ";//根
PreorderTree(T->lchild);//左
PreorderTree(T->rchild);//右
}
/*
* Display AVL Tree by pic
*/
void display(BinaryTreeNode *ptr, int level)
{
int i;
if (ptr!=NULL)
{
display(ptr->rchild, level + 1);
printf("\n");
for (i = 0; i < level; i++)
cout<<" ";
cout<<ptr->value;
display(ptr->lchild, level + 1);
}
}
//return nullptr mean not find,return the Node if find the data
//can change return to true or false
BinaryTreeNode* SearchNode(BinaryTreeNode* T,int data)
{
if (T == nullptr)
{
cout<<"not find data,return last search nullptr Node?"<<endl;
return T;
}
else if(T->value == data)
{
cout<<"find the data,return the Node"<<endl;
return T;
} else {
if(T->value<data)
{
return SearchNode(T->rchild,data);
} else {
return SearchNode(T->lchild,data);
}
}
}
//return 指针的方法,用于避免了传入二级指针来修改指针地址的方法!
//(因为操作形参指针指向的地址,实参指针指向的地址实际未改变,所以2种方法,二级指针或返回地址来改变实参指针)
BinaryTreeNode* deleteNode(BinaryTreeNode* T,int data)
{
if(T == nullptr)
{
cout<<"have not data,dont need delete!"<<endl;
return T;
}
if(T->value == data)
{
//一边为null和叶子结点情况合并
if(T->lchild == nullptr || T->rchild == nullptr)
{
BinaryTreeNode* deleteNode = T;//此处容易忽略,用于删除以前指向的地址内存
T = (T ->lchild?T->lchild:T->rchild);
delete deleteNode;
deleteNode = nullptr;
}
else
{
if(getHighDiff(T)>0)
{
//找左子树的最右结点(最大值)赋值给该结点,然后删除该叶子结点
BinaryTreeNode* pmax = T->lchild;
while(pmax->rchild != nullptr)
{
pmax = pmax ->rchild;
}
T->value = pmax -> value;
//delete pmax;
//pmax = nullptr;
T->lchild = deleteNode(T->lchild,T->value);//必须使用递归删除,因为可能找到的最右结点,还有左子叶
T = balance(T);
}
else if(getHighDiff(T)<0)
{
//找右子树的最左结点(最小值)赋值给该结点,然后删除该叶子结点
BinaryTreeNode* pmin = T->rchild;
while(pmin->lchild != nullptr)
{
pmin = pmin ->lchild;
}
T->value = pmin -> value;
//delete pmax;
//pmax = nullptr;
T->rchild = deleteNode(T->rchild,T->value);//必须使用递归删除,因为可能找到的最右结点,还有左子叶
T = balance(T);
}
}
}
else if(T->value < data)
{
T->rchild = deleteNode(T->rchild,data);
T = balance(T);
}else{
T->lchild = deleteNode(T->lchild,data);
T = balance(T);
}
return T;
}
int main() {
//here without new ,init as nullptr
BinaryTreeNode* head;
BinaryTreeNode* temp = nullptr;
if(head == nullptr)
cout<<"without new,the point is nullptr!"<<endl;
head = CreateTree();
head = insertBST(head, 1);
head = insertBST(head, 10);
head = insertBST(head, 11);
head = insertBST(head, 17);//if insert same value,skip it!
head = insertBST(head, 16);
head = insertBST(head, 15);
head = insertBST(head, 18);
display(head,0);
head = deleteNode(head, 11);
head = deleteNode(head, 8);
head = deleteNode(head, 10);
head = deleteNode(head, 14);
head = deleteNode(head, 15);
display(head,0);
/*
temp = insertBST2(head,4);//function 2 can insert same value
cout<<"\nAfter insert 2,PreorderTree:"<<endl;
PreorderTree(temp);
*/
/*
cout<<"\nSearch specify value:"<<endl;
BinaryTreeNode* searchResult1 = SearchNode(head,7);
cout<<"Node value:"<<searchResult1->value<<endl;
BinaryTreeNode* searchResult2 = SearchNode(head,6);
cout<<"Node is nullptr when not find"<<searchResult2<<endl;
*/
/*
cout<<"Delete test:"<<endl;
display(head,0);
BinaryTreeNode* deleteResult;
// deleteResult = deleteNode(head,3);
deleteResult = deleteNode(head,7);
deleteResult = deleteNode(head,4);
deleteResult = deleteNode(head,17);//最复杂情况,两边都有子叶,并且找到最大值后,仍然需要递归删除
cout<<"Delete result:"<<endl;
display(head,0);
PreorderTree(head);
*/
/*
cout<<"Single Rotation test:"<<endl;
BinaryTreeNode* rotation;
rotation = insertBST2(rotation, 5);
rotation = insertBST2(rotation, 2);
rotation = insertBST2(rotation, 1);
rotation = insertBST2(rotation, 4);
rotation = insertBST2(rotation, 6);
display(rotation,0);
rotation = llRotation(rotation);
display(rotation,0);
rotation = rrRotation(rotation);
display(rotation,0);
cout<<"\nDouble Rotation test:"<<endl;
rotation = insertBST2(rotation, 3);
display(rotation,0);
cout<<"\nhign:"<<getHigh(rotation)<<" diff: "<<getHighDiff(rotation)<<endl;
rotation = lrRotation(rotation);
cout<<"\nhign:"<<getHigh(rotation)<<" diff: "<<getHighDiff(rotation)<<endl;
display(rotation,0);
*/
return 0;
}
2.平衡二叉树
对比查找二叉树代码,仅仅是在插入后,新增了一步判断四种旋转情况的函数(需要通过高度差判断)
节点的平衡因子是通过计算其左子树和右子树的差得来的,这里有两种考虑方式:
1. 每次都计算一次(递归求深度)。
2. 将平衡因子作为一个成员变量保存在节点中,平衡性发生变化的时候更新。
https://blog.youkuaiyun.com/u013149325/article/details/41381607(包含很多图示!也是递归删除,删除方法与http://blog.youkuaiyun.com/xiajun07061225/article/details/8292505相同)-----balance方法与下面代码略有区别,结构体包含了高度(封装性比不上下面方法)
2.1 第1种方式
https://www.sanfoundry.com/cpp-program-implement-avl-trees/(除了没有删除,其余完美!)
/*
* C++ program to Implement AVL Tree
*/
#include<iostream>
#include<cstdio>
#include<sstream>
#include<algorithm>
#define pow2(n) (1 << (n))
using namespace std;
/*
* Node Declaration
*/
struct avl_node
{
int data;
struct avl_node *left;
struct avl_node *right;
}*root;
/*
* Class Declaration
*/
class avlTree
{
public:
int height(avl_node *);
int diff(avl_node *);
avl_node *rr_rotation(avl_node *);
avl_node *ll_rotation(avl_node *);
avl_node *lr_rotation(avl_node *);
avl_node *rl_rotation(avl_node *);
avl_node* balance(avl_node *);
avl_node* insert(avl_node *, int );
void display(avl_node *, int);
void inorder(avl_node *);
void preorder(avl_node *);
void postorder(avl_node *);
avlTree()
{
root = NULL;
}
};
/*
* Main Contains Menu
*/
int main()
{
int choice, item;
avlTree avl;
while (1)
{
cout<<"\n---------------------"<<endl;
cout<<"AVL Tree Implementation"<<endl;
cout<<"\n---------------------"<<endl;
cout<<"1.Insert Element into the tree"<<endl;
cout<<"2.Display Balanced AVL Tree"<<endl;
cout<<"3.InOrder traversal"<<endl;
cout<<"4.PreOrder traversal"<<endl;
cout<<"5.PostOrder traversal"<<endl;
cout<<"6.Exit"<<endl;
cout<<"Enter your Choice: ";
cin>>choice;
switch(choice)
{
case 1:
cout<<"Enter value to be inserted: ";
cin>>item;
root = avl.insert(root, item);
break;
case 2:
if (root == NULL)
{
cout<<"Tree is Empty"<<endl;
continue;
}
cout<<"Balanced AVL Tree:"<<endl;
avl.display(root, 1);
break;
case 3:
cout<<"Inorder Traversal:"<<endl;
avl.inorder(root);
cout<<endl;
break;
case 4:
cout<<"Preorder Traversal:"<<endl;
avl.preorder(root);
cout<<endl;
break;
case 5:
cout<<"Postorder Traversal:"<<endl;
avl.postorder(root);
cout<<endl;
break;
case 6:
exit(1);
break;
default:
cout<<"Wrong Choice"<<endl;
}
}
return 0;
}
/*
* Height of AVL Tree
*/
int avlTree::height(avl_node *temp)
{
int h = 0;
if (temp != NULL)
{
int l_height = height (temp->left);
int r_height = height (temp->right);
int max_height = max (l_height, r_height);
h = max_height + 1;
}
return h;
}
/*
* Height Difference
*/
int avlTree::diff(avl_node *temp)
{
int l_height = height (temp->left);
int r_height = height (temp->right);
int b_factor= l_height - r_height;
return b_factor;
}
/*
* Right- Right Rotation
*/
avl_node *avlTree::rr_rotation(avl_node *parent)
{
avl_node *temp;
temp = parent->right;
parent->right = temp->left;
temp->left = parent;
return temp;
}
/*
* Left- Left Rotation
*/
avl_node *avlTree::ll_rotation(avl_node *parent)
{
avl_node *temp;
temp = parent->left;
parent->left = temp->right;
temp->right = parent;
return temp;
}
/*
* Left - Right Rotation
*/
avl_node *avlTree::lr_rotation(avl_node *parent)
{
avl_node *temp;
temp = parent->left;
parent->left = rr_rotation (temp);
return ll_rotation (parent);
}
/*
* Right- Left Rotation
*/
avl_node *avlTree::rl_rotation(avl_node *parent)
{
avl_node *temp;
temp = parent->right;
parent->right = ll_rotation (temp);
return rr_rotation (parent);
}
/*
* Balancing AVL Tree
*/
avl_node *avlTree::balance(avl_node *temp)
{
int bal_factor = diff (temp);
if (bal_factor > 1)
{
if (diff (temp->left) > 0)
temp = ll_rotation (temp);
else
temp = lr_rotation (temp);
}
else if (bal_factor < -1)
{
if (diff (temp->right) > 0)
temp = rl_rotation (temp);
else
temp = rr_rotation (temp);
}
return temp;
}
/*
* Insert Element into the tree
*/
avl_node *avlTree::insert(avl_node *root, int value)
{
if (root == NULL)
{
root = new avl_node;
root->data = value;
root->left = NULL;
root->right = NULL;
return root;
}
else if (value < root->data)
{
root->left = insert(root->left, value);
root = balance (root);
}
else if (value >= root->data)
{
root->right = insert(root->right, value);
root = balance (root);
}
return root;
}
/*
* Display AVL Tree
*/
void avlTree::display(avl_node *ptr, int level)
{
int i;
if (ptr!=NULL)
{
display(ptr->right, level + 1);
printf("\n");
if (ptr == root)
cout<<"Root -> ";
for (i = 0; i < level && ptr != root; i++)
cout<<" ";
cout<<ptr->data;
display(ptr->left, level + 1);
}
}
/*
* Inorder Traversal of AVL Tree
*/
void avlTree::inorder(avl_node *tree)
{
if (tree == NULL)
return;
inorder (tree->left);
cout<<tree->data<<" ";
inorder (tree->right);
}
/*
* Preorder Traversal of AVL Tree
*/
void avlTree::preorder(avl_node *tree)
{
if (tree == NULL)
return;
cout<<tree->data<<" ";
preorder (tree->left);
preorder (tree->right);
}
/*
* Postorder Traversal of AVL Tree
*/
void avlTree::postorder(avl_node *tree)
{
if (tree == NULL)
return;
postorder ( tree ->left );
postorder ( tree ->right );
cout<<tree->data<<" ";
}
https://blog.youkuaiyun.com/xiajun07061225/article/details/8292505(递归,包含删除)
https://tfetimes.com/c-avl-tree/(模板类非递归!,如下)
#include <algorithm>
#include <iostream>
/* AVL node */
template <class T>
class AVLnode {
public:
T key;
int balance;
AVLnode *left, *right, *parent;
AVLnode(T k, AVLnode *p) : key(k), balance(0), parent(p),
left(NULL), right(NULL) {}
~AVLnode() {
delete left;
delete right;
}
};
/* AVL tree */
template <class T>
class AVLtree {
public:
AVLtree(void);
~AVLtree(void);
bool insert(T key);
void deleteKey(const T key);
void printBalance();
private:
AVLnode<T> *root;
AVLnode<T>* rotateLeft ( AVLnode<T> *a );
AVLnode<T>* rotateRight ( AVLnode<T> *a );
AVLnode<T>* rotateLeftThenRight ( AVLnode<T> *n );
AVLnode<T>* rotateRightThenLeft ( AVLnode<T> *n );
void rebalance ( AVLnode<T> *n );
int height ( AVLnode<T> *n );
void setBalance ( AVLnode<T> *n );
void printBalance ( AVLnode<T> *n );
void clearNode ( AVLnode<T> *n );
};
/* AVL class definition */
template <class T>
void AVLtree<T>::rebalance(AVLnode<T> *n) {
setBalance(n);
if (n->balance == -2) {
if (height(n->left->left) >= height(n->left->right))
n = rotateRight(n);
else
n = rotateLeftThenRight(n);
}
else if (n->balance == 2) {
if (height(n->right->right) >= height(n->right->left))
n = rotateLeft(n);
else
n = rotateRightThenLeft(n);
}
if (n->parent != NULL) {
rebalance(n->parent);
}
else {
root = n;
}
}
template <class T>
AVLnode<T>* AVLtree<T>::rotateLeft(AVLnode<T> *a) {
AVLnode<T> *b = a->right;
b->parent = a->parent;
a->right = b->left;
if (a->right != NULL)
a->right->parent = a;
b->left = a;
a->parent = b;
if (b->parent != NULL) {
if (b->parent->right == a) {
b->parent->right = b;
}
else {
b->parent->left = b;
}
}
setBalance(a);
setBalance(b);
return b;
}
template <class T>
AVLnode<T>* AVLtree<T>::rotateRight(AVLnode<T> *a) {
AVLnode<T> *b = a->left;
b->parent = a->parent;
a->left = b->right;
if (a->left != NULL)
a->left->parent = a;
b->right = a;
a->parent = b;
if (b->parent != NULL) {
if (b->parent->right == a) {
b->parent->right = b;
}
else {
b->parent->left = b;
}
}
setBalance(a);
setBalance(b);
return b;
}
template <class T>
AVLnode<T>* AVLtree<T>::rotateLeftThenRight(AVLnode<T> *n) {
n->left = rotateLeft(n->left);
return rotateRight(n);
}
template <class T>
AVLnode<T>* AVLtree<T>::rotateRightThenLeft(AVLnode<T> *n) {
n->right = rotateRight(n->right);
return rotateLeft(n);
}
template <class T>
int AVLtree<T>::height(AVLnode<T> *n) {
if (n == NULL)
return -1;
return 1 + std::max(height(n->left), height(n->right));
}
template <class T>
void AVLtree<T>::setBalance(AVLnode<T> *n) {
n->balance = height(n->right) - height(n->left);
}
template <class T>
void AVLtree<T>::printBalance(AVLnode<T> *n) {
if (n != NULL) {
printBalance(n->left);
std::cout << n->balance << " ";
printBalance(n->right);
}
}
template <class T>
AVLtree<T>::AVLtree(void) : root(NULL) {}
template <class T>
AVLtree<T>::~AVLtree(void) {
delete root;
}
template <class T>
bool AVLtree<T>::insert(T key) {
if (root == NULL) {
root = new AVLnode<T>(key, NULL);
}
else {
AVLnode<T>
*n = root,
*parent;
while (true) {
if (n->key == key)
return false;
parent = n;
bool goLeft = n->key > key;
n = goLeft ? n->left : n->right;
if (n == NULL) {
if (goLeft) {
parent->left = new AVLnode<T>(key, parent);
}
else {
parent->right = new AVLnode<T>(key, parent);
}
rebalance(parent);
break;
}
}
}
return true;
}
template <class T>
void AVLtree<T>::deleteKey(const T delKey) {
if (root == NULL)
return;
AVLnode<T>
*n = root,
*parent = root,
*delNode = NULL,
*child = root;
while (child != NULL) {
parent = n;
n = child;
child = delKey >= n->key ? n->right : n->left;
if (delKey == n->key)
delNode = n;
}
if (delNode != NULL) {
delNode->key = n->key;
child = n->left != NULL ? n->left : n->right;
if (root->key == delKey) {
root = child;
}
else {
if (parent->left == n) {
parent->left = child;
}
else {
parent->right = child;
}
rebalance(parent);
}
}
}
template <class T>
void AVLtree<T>::printBalance() {
printBalance(root);
std::cout << std::endl;
}
int main(void)
{
AVLtree<int> t;
std::cout << "Inserting integer values 1 to 10" << std::endl;
for (int i = 1; i <= 10; ++i)
t.insert(i);
std::cout << "Printing balance: ";
t.printBalance();
}