这几天写OS lab写的焦虑爆棚,好在现在写完了lab4/7. 又忙活哥大入学的事情,最终结论是可以先快乐网课一学期。所以最近都没更新博客,my apologies…
1.二叉搜索树
1.1 定义
一个节点的左后代都比它小,右后代都比它大。
对BST做中序遍历,是单调非降的。
1.2 二叉搜索树的查找
对二叉搜索树做查找,相当于对一个有序向量做二分查找。
TreeNode* search(TreeNode* root, int val){
//找到树里值为val的节点,成功时返回该节点,失败时返回NULL
if(root == NULL) //ooops! missed
return NULL;
if(root->val == val)
return root;
if(root->val<val)
search(root->rightchild,val);
if(root->val>val)
search(root->leftchild,val);
}
[Alert] 在递归函数中,一定要先考虑节点为NULL的问题,否则访问一个NULL节点的val就会出问题的!!
1.3 二叉搜索树的插入
void insert(TreeNode* & root, int val){
if((root->leftchild == nullptr) && (val< root->val)){
//find a node to insert!
root->leftchild = new TreeNode(val);
return;
}
if((root->rightchild == nullptr) && (val> root->val)){
//find a node to insert!
root->rightchild = new TreeNode(val);
return;
}
if(root->val == val){
//oooops! already exist
return;
}
if(val>root->val){
//deep into right child...
insert(root->rightchild,val);
}
if(val<root->val){
//deep into left child...
insert(root->leftchild,val);
}
}
1.4 二叉搜索树的删除
分两种情况讨论:
(1)待删除节点的任一子树为空
只需用那个非空子树来替代待删除节点即可
(2)待删除节点的两个子树都非空
找到待删除节点在中序遍历下的后继节点(先向右迈一步,然后一直向左)。在这里,这个后继节点应该是比待输出节点大的第一个。将他们交换。
这样,问题就转化为了情况(1),因为现在待删除节点一定没有左孩子。
1.5 平衡
1.5.1 二叉搜索树的期望树高
我们用两个方法来看二叉搜索树的期望树高。
-
随机生成法来看
-
随机组成法来看
O(logn)和O(sqrt(n))还是差着很多的啊!那么,BST的期望树高到底是多少呢?
1.5.2 理想和适度平衡
- 理想平衡 – 满树:叶节点只能出现在最底部的两层,这个条件过于严苛
- 适度平衡 – 高度为O(logn)的二叉搜索树,叫做平衡二叉搜索树(BBST)
1.5.3 等价变换
观察如下的两个树,发现它们虽然连接关系不相同,但是中序遍历序列完全一致,说明它们是等价的BST。
利用这一性质,可以知道,BST通过若干次旋转(O(logn))可以变成BBST.
具体做法:旋转调整 – zigzag
2. AVL树
2.1 定义
对于任何一个节点,其左孩子和右孩子的高度之差需要<=1。
2.2 AVL树的树高
可以证明,AVL树虽然不是理想平衡,但一定是适度平衡的(树高O(logn))
证明:
将一棵高度为h的树包含的节点数目记作S(h). 由于这是一棵AVL树,所以左右孩子高度相差不超过1. 因此有:
反过来,由n个节点构成的AVL树,高度不超过O(logn).
2.3 AVL树的插入
插入是一个比较复杂的问题,因为从被插入节点的祖父开始,每个祖先都有可能失衡,且可能同时失衡!
但好消息是,我们可以利用旋转变换将其重平衡,因为树高为O(logn),所以只需要O(logn)的时间就可以将其变成平衡的AVL树。
- 单旋
- 双旋
2.4 AVL树的删除
-
单旋
-
双旋