二叉树
二叉树定义
二叉树是每个结点最多有两个子结点的数结构。
二叉树的性质
一、度数为0的结点个数等于度数为2的结点个数加一,即n0=n2+1。
解释:只有一个根结点时 n0=1, n1=0, n2=0;(初始状态)。每添加一个结点a于结点b之下,b结点只存在两种情况:1、结点b的度数为0;2、结点b的度数为1。
在第1种情况下,n0不变,n1++, n2不变。
在第2中情况下,n0++,n1不变,n2++。
可以发现n0和n2是要么同时加一,要么都不加。
所以n0=n2+1;
二、在非空二叉树中,第n层的结点个数不超过 2n−1 (n>=1)。
解释:n=1时 结点个数为1(初始状态)。下一层的结点个数为上一层结点个数的两倍。因此可以看成为n1=1,q=2。的等比数列。
三、深度为n的二叉树,其总结点个数不超过 2n−1 (n>=0)。
解释:由等比数列求和得总结点个数为 2n−1。
四、有n个结点的完全二叉树各结点如果用顺序方式编号,则结点之间的编号关系如下:
若I为该结点编号,其父结点编号为(int)I/2。
若存在左孩子,其编号为
2⋅I
。
若存在右孩子,其编号为
2⋅I+1
。
思路:
父结点和子结点之间的关系可以通过树的高度建立起,所以通过高度寻找关系。(模棱两可 @.@)
解释:
令:编号为I的结点的高度为h,Left为I的左结点的编号,Right为I的右结点的编号。
∵Left=(2h−1)+与I结点同高度却在I结点前面的个数×2+1
又∵与I结点同高度却在I结点前面的个数=I−(2h−1−1)−1=I−2h−1
∴Left=(2h−1)+2×(I−2h−1)+1
即Left=2⋅I
又∵Right=Left+1
∴Right=2⋅I+1
AVL树
要求:任一结点的左子树的深度和右子树的深度差的绝对值不超过1。
|LeftNodeDepth−RightNodeDepth|≤1
,本文中称LeftNodeDepth - RightNodeDepth为平衡因子
这个要求的目的是使二叉搜索树实现平衡
因为一个二叉树的搜索算法复杂度与数的高度有关为
O(log2h)
,所以控制二叉树的平衡型是重要的。
我们在每次插入结点时判断新加入的结点是否会破坏整颗树的平衡,如果会破坏就要进行相应的调整。(左旋,右旋等调整)
理解左旋和右旋
左结点偏重时进行左旋,使左结点的深度至少减一,右结点的深度至少加一。反之进行右旋,使左结点的深度至少加一,右结点的深度至少减一。从而起到大致控制左右结点深度的作用。
图片:
过程:
对5结点进行左旋
1、5结点变为3结点的右结点
2、3结点的右结点B变为5结点的左结点
对3结点进行右旋
1、3结点变为5结点的左结点
2、5结点的左结点B变为3结点的右结点
证明:
对5结点进行左旋
1、5结点比3结点要大,所以 {5结点变为3结点的右结点} 这个过程不会破坏顺序。
2、B结点比3大比5小,所以B结点成为5结点的左结点也不会破坏顺序。
对3结点进行右旋
证明过程同左旋
什么时候进行左旋或者右旋?
在破坏AVL树的要求时就要进行相应的调整。在左结点深度减右结点的深度的绝对值大于1时,就破坏了AVL树的平衡。
- 情况1_1:平衡因子>1时,且其左结点的平衡因子>0,对该结点进行右旋。
图片解释:当插入I结点时,I结点经过的A、B、C结点的平衡因子的值都发生了改变。其中A结点和B结点的平衡因子变为了2,所以对A结点或B结点进行右旋。
其中对A结点进行右旋在代码中是不推荐的,因为不是所有情况都存在两个平衡值为2的结点。而 2、1、0(B结点、C结点、I结点) 才是该情况的特点。 - 情况1_2:平衡因子>1时,且其左结点的平衡因子<0,对该结点的左结点进行左旋,然后对该结点进行右旋。
对该结点的左结点进行左旋的目的是,让左结点的平衡因子变为>0的值(就变成情况1_1了)。 情况2_1:平衡因子<-1时,且其右结点的平衡因子<0,对该结点进行左旋。
情况2_2:平衡因子<-1时,且其右结点的平衡因子>0,对该结点的右结点进行右旋,然后对该结点进行左旋。
对该结点的右结点进行左旋的目的是,让右结点的平衡因子变为<0的值(就变成情况2_1了)。
代码实现
//数据结构
struct Node{
int value;//序号
int balance;//平衡因子
Node *left;
Node *right;
}Node;
//所需函数
void LeftRotate(Node*);//左旋
void RightRotate(Node*);//右旋
class AvlTree{
public:
Node root;
AvlTree(){};
void insert(Node* pNode){
if(pNode -> value < root)
};
};