【算法导论】学习笔记——第12章 二叉搜索树

搜索树数据结构支持多种动态集合操作,包括SEARCH、MINIMUM、MAXIMUM、PREDECESSOR、SUCCESSOR、INSRT和DELETE操作等。基本的搜索树就是一棵二叉搜索树。
12.1 什么是二叉搜索树
1. 二叉搜索树的性质:
设x是二叉搜索树中的一个结点。如果y是x左子树中的一个结点,那么y.key<=x.key。如果y是x右子树中的一个结点,那么y.key>=x.key。
三种遍历时间复杂度是O(n),这是显然的。

12.1-3

 1 void Inorder_Tree_Walk_Nonrecursive_1(Node_t *x) {
 2     stack<Node_t *> S;
 3     bool mid = false;
 4
 5     printf("Using stack to implement Inorder_Tree_Walk:\n");
 6     S.push(x);
 7     while (!S.empty()) {
 8         if (x->l == NULL || mid) {
 9             printf("%d ", x->key);
10             S.pop();
11             if (x->r == NULL) {
12                 if (S.empty())
13                     break;
14                 x = S.top();
15                 mid = true;
16             } else {
17                 x = x->r;
18                 S.push(x);
19                 mid = false;
20             }
21         } else {
22             x = x->l;
23             S.push(x);
24         }
25     }
26     printf("\n");
27 }
28 void Inorder_Tree_Walk_Nonrecursive_2(Node_t *x) {
29     printf("Using nonstack to implement Inorder_Tree_Walk:\n");
30     Node_t *p = x->p, *y = NULL;
31     bool ret = false, retr = false;
32
33     while (!retr || y!=p) {
34         if (x->l == NULL || ret) {
35             if (!retr)
36                 printf("%d ", x->key);
37             if (x->r == NULL || retr) {
38                 retr = (x == y->r);
39                 x = y;
40                 y = y->p;
41                 ret = true;
42             } else {
43                 y = x;
44                 x = x->r;
45                 ret = false;
46             }
47         } else {
48             y = x;
49             x = x->l;
50         }
51     }
52     printf("\n");
53 }

12.1-5

12.2 查询二叉搜索树
除了基本的SEARCH外,还包括MINIMUM、MAXIMUM、SUCCESSOR、PREDESSOR等查询操作。

12.2-5

12.2-6

12.2-7

12.2-8

12.2-9

12.3 插入和删除
插入操作比较简,基本二叉树的删除操作需要分类讨论。书中使用了一个子函数TRANSPLANT简化了一些基本操作,使得DELETE过程变得更加清晰。
插入和删除的时间复杂度都是O(h).

12.3-2

12.3-3

12.3-4
不可交换,反例如下图:

12.3-5
中文的《算法导论(第3版)》这道题完全翻译错了,去看了一下英文的搞懂了题目做的。
如提示所示,就是利用后继的属性重新实现插入、删除、查找函数。
求结点p的父节点的思路是先求得p所在子树的最大值,该值所在结点的后继就是p的父节点。 其余
操作均需要利用后继的性质。以插入为例。

 1 void Tree_Insert(Tree_t *t, Node_t *z) {
 2     Node_t *y = NULL;
 3     Node_t *x = t->root;
 4     while (x != NULL) {
 5         y = x;
 6         if (z->key < x->key)
 7             x = x->l;
 8         else
 9             x = x->r;
10     }
11     if (y == NULL) {
12         t->root = z;    // tree t was empty
13         z->succ = NULL;
14     } else if (z->key < y->key) {
15         Node_t *p = ParentOf(y);
16         z.succ = y;
17         p.succ = z;
18     } else {
19         z->succ = y->succ
20         y->succ = z;
21     }
22 }

 12.4 随机构建二叉搜索树
算法导论很多章节都会讲随机算法在数据结构上的随机化。而且都是大段大段的证明,挺犀利。随机算法我个人打算单独花时间学习,因此涉及到的题目都在第二遍读算导的时候再做。
主要证明一棵有n个不同关键字的随机构建的二叉搜索树的期望高度为O(lgn)。主要随机搜索树的很多基本操作的时间复杂度都是O(lgn)。随机化的效率还是很高的,其实可以写个随机数据发生器,测试一下。

时间: 2024-10-26 20:32:40

【算法导论】学习笔记——第12章 二叉搜索树的相关文章

算法导论学习笔记——第12章 二叉查找树

二叉查找树性质 设x是二叉查找树中的一个结点,如果y是x的左子树中的一个结点,则k[y]<=key[x]:如果y是右子树中的一个结点,则k[y]>=k[x] 1 //中序遍历算法,输出二叉查找树T中的全部元素 2 INORDER-TREE-WALK(x) 3 if x!=nil 4 then INORDER-TREE-WALK(left[x]) 5 print key[x] 6 INORDER-TREE-WALK(right[x]) 查找 1 //递归版本 2 TREE-SEARCH(x,k)

【算法导论】动态规划之“最优二叉搜索树”

之前两篇分别讲了动态规划的"钢管切割"和"矩阵链乘法",感觉到了这一篇,也可以算是收官之作了.其实根据前两篇,到这里,也可以进行一些总结的,我们可以找到一些规律性的东西. 所谓动态规划,其实就是解决递归调用中,可能出现重复计算子问题,从而导致耗费大量时间,去做重复劳动的问题.解决思路就是,将重复做过的子问题的结果,先存起来,等之后再需要用到的时候,直接拿过来用,而不需要再去计算. 但是这里还需要注意一些地方: ①要解决的问题,比如"钢管切割"中的

算法导论 第三部分——基本数据结构——二叉搜索树

一.什么是二叉搜索树 二叉查找树是按照二叉树结构来组织的,因此可以用二叉链表结构表示.二叉查找树中的关键字的存储方式满足的特征是:设x为二叉查找树中的一个结点.如果y是x的左子树中的一个结点,则key[y]≤key[x].如果y是x的右子树中的一个结点,则key[x]≤key[y].根据二叉查找树的特征可知,采用中根遍历一棵二叉查找树,可以得到树中关键字有小到大的序列. 二叉树的查找.最大/小.前驱和后继的伪代码: 复杂度都是 h //search 递归版 TREE_SEARCH(x,k) if

算法导论学习笔记——第1章

所谓算法,就是定义良好的计算过程,它取一个或一组值作为输入,并产生出一个或一组值作为输出.亦即,算法是一系列的计算过程,将输入值转换成输出值. 一些常见的算法运行时间量级比较:对数级<多项式级<指数级<阶乘级 1 lgn < n 1/2 < n < nlgn < n 2 < n 3 < 2 n < n!

算法导论学习笔记——第10章 基本数据结构

栈 1 Stack-EMPTY(S) 2 if top[S]=0 3 then return TRUE 4 else return FALSE 5 6 PUSH(S,x) 7 top[S]←top[S]+1 8 S[top[S]]←x 9 10 POP(S) 11 if STACK-EMPTY(S) 12 then error "underflow" 13 else top[S]←top[S]-1 14 return S[top[S]+1] 队列 1 ENQUEUE(Q,x) 2 Q[

算法导论学习笔记——第8章 线性时间排序

任意一种比较排序算法,在最坏情况下的运行时间下限是Ω(nlgn) 计数排序 假设n个输入元素中的每一个都是介于0到k之间的整数,k为某个整数,当k=O(n)时,计数排序的运行时间为Θ(n) 1 //输入数组A[1..n],存放排序结果数组B[1..n],临时存储区C[0..k] 2 COUNTING-SORT(A,B,k) 3 for i←0 to k 4 do C[i]←0 5 for j←1 to length[A] 6 do C[A[j]]←C[A[j]]+1 7 for i←1 to k

算法导论学习笔记——第13章 红黑树

红黑树 红黑树是一种二叉查找树,但在每个结点上增加一个存储位存储结点的颜色,可以是red或black.通过对任意一条从根到叶的路径上结点颜色的限制,红黑树确保没有任何一条路径比其他路径长出两倍,因而是接近平衡的. 每个结点包含5个域,color,key,left,right,p 满足以下红黑性质: 1.每个结点是红色或黑色 2.根结点是黑色 3.每个叶结点(nil)是黑色 4.如果一个结点是红色,那么它的两个子结点都是黑色 5.对每个结点,从该结点到它每个子孙结点的路径上,黑结点数目相同 左旋转

算法导论学习笔记——第7章 快速排序

快速排序 1 QUICKSORT(A,p,r) 2 if p<r 3 then q←PARTITION(A,p,r) 4 QUICKSORT(A,p,q-1) 5 QUICKSORT(A,q+1,r) 6 7 PARTITION(A,p,r) 8 x←A[r] 9 i←p-1 10 for j←p to r-1 11 do if A[j]<=x 12 then i←i+1 13 exchange A[i]↔A[j] 14 exchange A[i+1]↔A[r] 15 return i+1 随

算法导论学习笔记 第7章 快速排序

对于包含n个数的输入数组来说,快速排序是一种时间复杂度为O(n^2)的排序算法.虽然最环情况的复杂度高,但是快速排序通常是实际应用排序中最好的选择,因为快排的平均性能非常好:它的期望复杂度是O(nlgn),而且O(nlgn)中的常数因子非常小.另外,快速排序还可以实现原址排序,甚至在虚拟环境中也能很好的工作. 1 快速排序的描述 与归并排序一样,快速排序也使用了分治法的思想,下面是对一个典型的子数组A[p.. r]进行快速排序的分治过长: 分解:数组A[p.. r]被划分为两个(可能为空)子数组