SCNU-Turing-Class CLRS Discussion Week4-5
C i a l l o ~ ( ∠ ・ ω < ) ⌒★! Ciallo~(∠・ω< )⌒★! C ia ll o ~ ( ∠ ・ ω < ) ⌒★ !
右子树就是柚子树
柚子厨蒸鹅心
C i a l l o ~ ( ∠ ・ ω < ) ⌒★! Ciallo~(∠・ω< )⌒★! C ia ll o ~ ( ∠ ・ ω < ) ⌒★ !
二叉树 Binary Tree
二叉树的定义
一种非线性数据结构,代表“祖先”与“后代”之间的派生关系,体现了“一分为二”的分治逻辑。
每个节点包含的属性有:
1. left指针,指向左子节点
2. right指针,指向右子节点
3. prev指针,指向父节点(书上这么写的,但实际构建可能不写这个。当然可以构建一个prev指针指向父节点,形成一个双向二叉树)
4. key关键字,节点的值
构建代码如下:
1 2 3 4 5 6 7 8 struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode *prev; TreeNode (int x) : val (x), left (nullptr ), right (nullptr ), prev (nullptr ) {} };
二叉树常用术语
「根节点 r o o t n o d e root\ node roo t n o d e 」:位于二叉树顶层的节点,没有父节点。
「叶节点 l e a f n o d e leaf\ node l e a f n o d e 」:没有子节点的节点,其两个指针均指向 None 。
节点的「度 d e g r e e degree d e g ree 」:节点的子节点的数量 。在二叉树中,度的取值范围是 0、1、2 。
二叉树的「高度 h e i g h t height h e i g h t 」:从根节点到最远叶节点 所经过的边的数量。
节点的「深度 d e p t h depth d e pt h 」:从根节点到该节点所经过的边的数量 。
节点的「高度 h e i g h t height h e i g h t 」:从距离该节点最远的叶节点 到该节点所经过的边的数量。
常见二叉树类型
完美二叉树 p e r f e c t b i n a r y t r e e perfect\ binary\ tree p er f ec t bina ry t ree
所有层的节点都被完全填满。
在完美二叉树中,叶节点的度为 0,其余所有节点的度都为2
若树的高度为 ℎ ,则节点总数为 2^ℎ+1^−1 ,呈现标准的指数级关系。
完全二叉树 c o m p l e t e b i n a r y t r e e complete\ binary\ tree co m pl e t e bina ry t ree
只有最底层的节点未被填满
最底层节点尽量靠左填充。
完满二叉树 f u l l b i n a r y t r e e full\ binary\ tree f u ll bina ry t ree
平衡二叉树 b a l a n c e d b i n a r y t r e e balanced\ binary\ tree ba l an ce d bina ry t ree
任意节点的左子树和右子树的高度之差的绝对值不超过 1 。
二叉树的退化
当二叉树的每层节点都被填满时,达到“完美二叉树”;而当所有节点都偏向一侧时,二叉树退化为“链表”。
完美二叉树是理想情况,可以充分发挥二叉树“分治”的优势。
链表则是另一个极端,各项操作都变为线性操作,时间复杂度退化至O ( n ) O(n) O ( n ) 。
完美二叉树
链表
第i i i 层的节点数量
2 i − 1 2^{i-1} 2 i − 1
1 1 1
高度为h h h 的树的叶节点数量
2 h − 1 2^{h-1} 2 h − 1
1 1 1
高度为h h h 的树的节点总数
2 h − 1 − 1 2^{h-1}-1 2 h − 1 − 1
h + 1 h+1 h + 1
节点总数为n n n 的树的高度
log 2 ( n + 1 ) − 1 \log_{2}{(n+1)}-1 log 2 ( n + 1 ) − 1
n − 1 n-1 n − 1
二叉搜索树 Binary Search Tree
一棵 x.left.key≤ \le ≤ x.right.key 的二叉树
二叉树的遍历
深度优先搜索 Depth-first traversal
代码实现
1 2 3 4 5 INORDER-TREE-WALK(x) if x≠NIL INORDER-TREE-WALK(x.left) print x.key INORDER-TREE-WALK(x.right)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void preOrder (TreeNode *root) { if (root == nullptr ) return ; vec.push_back (root->val); preOrder (root->left); preOrder (root->right); } void inOrder (TreeNode *root) { if (root == nullptr ) return ; inOrder (root->left); vec.push_back (root->val); inOrder (root->right); } void postOrder (TreeNode *root) { if (root == nullptr ) return ; postOrder (root->left); postOrder (root->right); vec.push_back (root->val); }
复杂度分析
对于渐进下界:
遍历需要访问 B S T BST BST 的全部节点。所以有:
T ( n ) = Ω ( n ) T(n)=\Omega(n)
T ( n ) = Ω ( n )
接下来证明渐进上界:
对于一棵空树,调用遍历函数需要一个极小的常数时间。所以设T ( 0 ) = c T(0)=c T ( 0 ) = c ,c c c 为常数
对于一棵节点数n > 0 n>0 n > 0 的树,设左子树节点数 k > 0 k>0 k > 0 ,右子树节点数为 n − k − 1 n-k-1 n − k − 1 ,额外开销为d d d ,则递归不等式为:
T ( n ) ≤ T ( k ) + T ( n − k − 1 ) + d , d 为常数 T(n)\le T(k)+T(n-k-1)+d,d为常数
T ( n ) ≤ T ( k ) + T ( n − k − 1 ) + d , d 为常数
我们不妨假设 ∃ c ∀ R \exists{\ c\ \forall\ R} ∃ c ∀ R ,当 c c c 很大时,有:T ( n ) ≤ c n T(n)\le cn T ( n ) ≤ c n
则上式可化为:
T ( n ) ≤ c k + c ( n − k − 1 ) + d = c n − ( c − d ) T(n) \le ck+c(n-k-1)+d=cn-(c-d)
T ( n ) ≤ c k + c ( n − k − 1 ) + d = c n − ( c − d )
当 c − d ≤ 0 c-d\le 0 c − d ≤ 0 时,有 T ( n ) = O ( n ) T(n)=O(n) T ( n ) = O ( n ) 。
综上所述,==T ( n ) = Θ ( n ) T(n)=\Theta (n) T ( n ) = Θ ( n ) ==
在最差情况下,即树退化为链表时,递归深度达到 n n n ,系统占用 O ( n ) O(n) O ( n ) 栈帧空间。
广度优先遍历 Breadth-first traversal
「层序遍历 level-order traversal」
从顶部到底部逐层遍历二叉树,并在每一层按照从左到右的顺序访问节点。
代码实现
广度优先遍历通常借助“队列 ”来实现。队列遵循“先进先出 ”的规则,而广度优先遍历则遵循“逐层推进”的规则,两者背后的思想是一致的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 vector<int > levelOrder (TreeNode *root) { queue<TreeNode *> queue; queue.push (root); vector<int > vec; while (!queue.empty ()) { TreeNode *node = queue.front (); queue.pop (); vec.push_back (node->val); if (node->left != nullptr ) queue.push (node->left); if (node->right != nullptr ) queue.push (node->right); } return vec; }
复杂度分析
时间复杂度:Θ ( n ) \Theta (n) Θ ( n )
所有节点被访问一次,使用 Θ ( n ) \Theta (n) Θ ( n ) 时间,其中 n n n 为节点数量。
在最差情况下,即满二叉树时,遍历到最底层之前,队列中最多同时存在 ( n + 1 ) / 2 (n+1)/2 ( n + 1 ) /2 个节点,占用 O ( n ) O(n) O ( n ) 空间。
二叉搜索树的查找
给定目标节点值 num ,可以根据二叉搜索树的性质来查找。如图 7-17 所示,我们声明一个节点 cur ,从二叉树的根节点 root 出发,循环比较节点值 cur.val 和 num 之间的大小关系。
若 cur.val < num ,说明目标节点在 cur 的右子树中,因此执行 cur = cur.right 。
若 cur.val > num ,说明目标节点在 cur 的左子树中,因此执行 cur = cur.left 。
若 cur.val = num ,说明找到目标节点,跳出循环并返回该节点。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 TreeNode *search (int num) { TreeNode *cur = root; while (cur != nullptr && cur->val != num) { if (cur->val < num) cur = cur->right; else cur = cur->left; } return cur; }
最大关键字元素和最小关键字元素
1 2 3 4 5 6 TreeNode *Minimun (TreeNode *x) { while (x->left != nullptr ) x = x->left; return x; }
1 2 3 4 5 6 7 TreeNode *Minimun (TreeNode *x) { if (x->left != nullptr ) return Minimun (x->left); else return x; }
1 2 3 4 5 6 TreeNode *Maximun (TreeNode *x) { while (x != nullptr ) x = x->right; return x; }
1 2 3 4 5 6 7 TreeNode *Maximun (TreeNode *x) { if (x->right != nullptr ) return Minimun (x->right); else return x; }
后继和前驱
1 2 3 4 5 6 7 8 TREE-SUCCESSOR(x) if x.right ≠ NIL return TREE-MINIMUM(x.right) y = x.p while y ≠ NIL and x == y.right x = y y = y.p return y
1 2 3 4 5 6 7 8 9 10 11 12 13 TreeNode *Successor (TreeNode *x) { if (x->right != nullptr ) return Minimun (x->right); TreeNode *y=x->prev; while (y != nullptr && x->val == y->right->val){ x = y; y = y->prev; } return y; }
1 2 3 4 5 6 7 8 9 10 11 12 13 TreeNode *Predecessor (TreeNode *x) { if (x->left != nullptr ) return Maximun (x->left); TreeNode *y=x->prev; while (y != nullptr && x->val == y->left->val){ x = y; y = y->prev; } return y; }
二叉搜索树的插入
给定一个待插入元素 num ,为了保持二叉搜索树“左子树 < 根节点 < 右子树 ”的性质,插入操作流程如下所示:
查找插入位置 :与查找操作相似,从根节点出发,根据当前节点值和 num 的大小关系循环向下搜索,直到越过叶节点(遍历至 None )时跳出循环。
在该位置插入节点 :初始化节点 num ,将该节点置于 None 的位置。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /* 伪代码 */ TREE-INSERT(T,z) y = NIL x = T.root while x ≠ NIL y = x if z.key < x.key x = x.left else x = x. right z.p = y if y == NIL T. root= z // tree T was empty elseif z.key < y.key y.left= z else y.right = z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void insert (int num) { if (root == nullptr ) { root = new TreeNode (num); return ; } TreeNode *cur = root, *pre = nullptr ; while (cur != nullptr ) { if (cur->val == num) return ; pre = cur; if (cur->val < num) cur = cur->right; else cur = cur->left; } TreeNode *node = new TreeNode (num); if (pre->val < num) pre->right = node; else pre->left = node; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void insert (TreeNode* &cur, int num) { if (cur == nullptr ) { cur = new TreeNode (num); return ; } if (cur->val == num) return ; if (cur->val < num) insert (cur->right, num); else insert (cur->left, num); }
复杂度分析
时间复杂度:O ( l o g n ) O(log\ n) O ( l o g n ) ,n为节点数
空间复杂度:O ( 1 ) O(1) O ( 1 )
二叉搜索树的删除
先在二叉树中查找到目标节点,再将其删除。与插入节点类似,我们需要保证在删除操作完成后,二叉搜索树的“左子树 < 根节点 < 右子树 ”的性质仍然满足。
因此,我们根据目标节点的子节点数量,分 0、1 和 2 三种情况,执行对应的删除节点操作。
若 z z z 有0 个子节点,则简单地将 z z z 删除,并修改 z z z 的父节点,用 N I L NIL N I L 作为孩子来替换 z z z 。
若 z z z 有1 个子节点,则将这个子节点提升到 z z z 的位置,并修改 z z z 的父节点,用 z z z 的孩子来替换 z z z 。(图a、b)
若 z z z 有2 个子节点,找 z z z 的后继 y y y ,然后 y y y 的右孩子(若有)取代 y y y 的位置,最后用 y y y 取代 z z z 。 (图c、d)
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 /* 将以u为根的子树用以v为根的子树代替 */ TRANSPLANT(T,u,v) /*若u为根节点*/ if u.p == NIL T.root=v /*若u是其父节点的左孩子*/ else if u == u.p.left u.p.left = v /*若u是其父节点的右孩子*/ else u.p.right = v if v ≠ NIL v.p=u.p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /* 删除结点z */ TREE-DELETE(T,z) /* 若z没有左孩子-图a */ if z.left == NIL TRANSPLANT(T,z,z.right) /* 以z的右子树代替z */ /* 若z没有右孩子-图b */ else if z.right == NIL TRANSPLANT(T,z,z.left) /* 以z的左子树代替z */ /* z有两个孩子 */ else y = TREE-MINIMUM(z.right) /* y为z的后继,y必然没有左节点 */ /* 若y不是z的孩子-图d */ if y.p ≠ z TRANSPLANT(T,y,y.right) /* 将y的位置替换为y的右子树 */ y.right = z.right /* 将y的右子树设置为z的右子树 */ y.right.p = y /* 将y的右子树的父节点设置为y */ /* 若y是z的右孩子-图c 以及图d的后续 */ TRANSPLANT(T,z,y) /* 用y替换z */ y.left = z.left y.left.p = y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 void remove (int num) { if (root == nullptr ) return ; TreeNode *cur = root, *pre = nullptr ; while (cur != nullptr ) { if (cur->val == num) break ; pre = cur; if (cur->val < num) cur = cur->right; else cur = cur->left; } if (cur == nullptr ) return ; if (cur->left == nullptr || cur->right == nullptr ) { TreeNode *child = cur->left != nullptr ? cur->left : cur->right; if (cur != root) { if (pre->left == cur) pre->left = child; else pre->right = child; } else { root = child; } delete cur; } else { TreeNode *tmp = cur->right; while (tmp->left != nullptr ) { tmp = tmp->left; } int tmpVal = tmp->val; remove (tmp->val); cur->val = tmpVal; } }
复杂度分析
时间复杂度:O ( l o g n ) O(log\ n) O ( l o g n ) ,n为节点数
空间复杂度:O ( 1 ) O(1) O ( 1 )
*随机构建二叉搜索树 Randomly built binary search trees
定义 n n n 个关键字的一棵随机构建二叉搜索树为按随机次序插入这些关键字到一棵初始的空树中而生成的树,这里输入关键字的 n ! n! n ! 个排列中的每个都是等可能地出现。那么对于这棵树的期望高度 ,则有如下定理:
定理 一棵有 n n n 个不同关键字的随机构建二叉搜索树的期望高度为 ==O ( l o g n ) O(log\ n) O ( l o g n ) == 。
证明如下:
我们先定义如下三个随机变量:
1. X n X_{n} X n 为一棵有 n n n 个不同关键字的随机构建二叉搜索树的高度 。
2. Y n = 2 X n Y_{n}=2^{X_{n}} Y n = 2 X n 为指数高度 ,定义 Y 0 = 0 Y_{0}=0 Y 0 = 0 和 Y 1 = 2 0 = 1 Y_{1}=2^{0}=1 Y 1 = 2 0 = 1 。
3. R n R_{n} R n 为一个随机变量,选择一个关键字作为树根,其为这个关键字在 n n n 个关键字集合中的秩(r a n k rank r ank ) ,表示这个关键字在排好序后应占据的位置 。R n R_{n} R n 的值对于集合 { 1 , 2 , . . . , n } \{1, 2,...,n\} { 1 , 2 , ... , n } 中的任何元素都是等可能的。
若 R n = i R_{n}=i R n = i ,那么根的左子树是一棵有 i − 1 i-1 i − 1 个关键字的随机构建二叉搜索树,并且右子树是一棵有 n − i n-i n − i 个关键字的随机构建搜二叉索树。
因为二叉树的高度为:
X n = m a x ( l e f t S u b t r e e . h e i g h t , r i g h t S u b t r e e . h e i g h t ) + 1 X_{n} =max(leftSubtree.height,rightSubtree.height)+1
X n = ma x ( l e f tS u b t ree . h e i g h t , r i g h tS u b t ree . h e i g h t ) + 1
所以有:
Y n = 2 ⋅ m a x ( Y i − 1 , Y n − i ) Y_{n}=2\cdot max(Y_{i-1},Y_{n-i})
Y n = 2 ⋅ ma x ( Y i − 1 , Y n − i )
接下来,我们定义指示器随机变量 (c h a p t e r 5.2 chapter\ 5.2 c ha pt er 5.2 )Z n , 1 Z_{n,1} Z n , 1 ,Z n , 2 Z_{n,2} Z n , 2 ,. . . ... ... ,Z n , n Z_{n,n} Z n , n ,其中:
Z n , i = I { R n = i } = { 1 n = i 0 n ≠ i ① Z_{n,i}=I\{R_{n}=i\}=\begin{cases}
\ 1 &n=i\\
\ 0 &n\ne i
\end{cases}
\qquad ①
Z n , i = I { R n = i } = { 1 0 n = i n = i ①
又因为 R n = i R_{n}=i R n = i 对于集合中每一个元素取值的概率是相同的,所以:
P { R n = i } = 1 n , ( i = 1 , 2 , . . . , n ) ② P\{R_{n}=i\}=\frac{1}{n}\ ,(i=1,2,...,n) \qquad ②
P { R n = i } = n 1 , ( i = 1 , 2 , ... , n ) ②
结合 ① ② ①\ ② ① ② ,得到:
E [ Z n , i ] = 1 ⋅ P { R n = i } = 1 n , ( i = 1 , 2 , . . . , n ) ③ E[Z_{n,i}]=1 \cdot P\{R_{n}=i\}=\frac{1}{n} \ ,(i=1,2,...,n) \quad ③
E [ Z n , i ] = 1 ⋅ P { R n = i } = n 1 , ( i = 1 , 2 , ... , n ) ③
由于 Z n , i Z_{n,i} Z n , i 恰有一个值为 1 1 1 ,其余所有值为 0 0 0 ,因此:
Y n = ∑ i = 1 n Z n , i ( 2 ⋅ m a x ( Y i − 1 , Y n − i ) ) Y_{n}=\sum_{i=1}^{n}{Z_{n,i}(2\cdot max(Y_{i-1},Y_{n-i}))}
Y n = i = 1 ∑ n Z n , i ( 2 ⋅ ma x ( Y i − 1 , Y n − i ))
由于 Z n , i Z_{n,i} Z n , i 独立于 Y i − 1 , Y n − i Y_{i-1},Y_{n-i} Y i − 1 , Y n − i 的值,上式两边取期望,得:
E [ Y n ] = E [ ∑ i = 1 n Z n , i ( 2 ⋅ max ( Y i − 1 , Y n − i ) ) ] = ∑ i = 1 n E [ Z n , i ( 2 ⋅ max ( Y i − 1 , Y n − i ) ) ] ( 期望的线性性质 ) = ∑ i = 1 n E [ Z n , i ] ⋅ E [ 2 ⋅ max ( Y i − 1 , Y n − i ) ] ( 独立性 ) = ∑ i = 1 n 1 n ⋅ E [ 2 ⋅ max ( Y i − 1 , Y n − i ) ] ( ③式 ) = 2 n ∑ i = 1 n E [ max ( Y i − 1 , Y n − i ) ] ( 期望的线性性质 ) ≤ 2 n ∑ i = 1 n ( E [ Y i − 1 ] + E [ Y n − i ] ) ( 补充证明 1 ) \begin{align*}
E[Y_{n}] &= E\left[\sum_{i=1}^{n}{Z_{n,i}(2 \cdot \max(Y_{i-1},Y_{n-i}))}\right] \\
&=\sum_{i=1}^{n}E[{Z_{n,i}}(2 \cdot \max(Y_{i-1},Y_{n-i}))] &(期望的线性性质)\\
&=\sum_{i=1}^{n}E[{Z_{n,i}}]\cdot E[2 \cdot \max(Y_{i-1},Y_{n-i})] &(独立性)\\
&=\sum_{i=1}^{n}\frac{1}{n}\cdot E[2 \cdot \max(Y_{i-1},Y_{n-i})] &(③式)\\
&=\frac{2}{n}\sum_{i=1}^{n}E[\max(Y_{i-1},Y_{n-i})] &(期望的线性性质)\\
&\le\frac{2}{n}\sum_{i=1}^{n}(E[Y_{i-1}]+E[Y_{n-i}]) &(补充证明1)\\
\end{align*}
E [ Y n ] = E [ i = 1 ∑ n Z n , i ( 2 ⋅ max ( Y i − 1 , Y n − i )) ] = i = 1 ∑ n E [ Z n , i ( 2 ⋅ max ( Y i − 1 , Y n − i ))] = i = 1 ∑ n E [ Z n , i ] ⋅ E [ 2 ⋅ max ( Y i − 1 , Y n − i )] = i = 1 ∑ n n 1 ⋅ E [ 2 ⋅ max ( Y i − 1 , Y n − i )] = n 2 i = 1 ∑ n E [ max ( Y i − 1 , Y n − i )] ≤ n 2 i = 1 ∑ n ( E [ Y i − 1 ] + E [ Y n − i ]) ( 期望的线性性质 ) ( 独立性 ) ( ③ 式 ) ( 期望的线性性质 ) ( 补充证明 1 )
因为 E [ Y 0 ] E[Y_{0}] E [ Y 0 ] ,E [ Y 1 ] E[Y_{1}] E [ Y 1 ] ,. . . ... ... ,E [ Y n − 1 ] E[Y_{n-1}] E [ Y n − 1 ] 每一项各出现两次,一次作为 E [ Y i − 1 ] E[Y_{i-1}] E [ Y i − 1 ] ,另一次为 E [ Y n − 1 ] E[Y_{n-1}] E [ Y n − 1 ] 。
所以上述公式可化为:
E [ Y n ] ≤ 4 n ∑ i = 0 n − 1 E [ Y i ] E[Y_{n}] \le\frac{4}{n}\sum_{i=0}^{n-1}E[Y_{i}]
E [ Y n ] ≤ n 4 i = 0 ∑ n − 1 E [ Y i ]
使用替换法,证明 E [ Y ( n ) ] = O ( n 3 ) E[Y(n)]=O(n^3) E [ Y ( n )] = O ( n 3 ) :
4 n ∑ i = 0 n − 1 E [ Y i ] ≤ 4 n ∑ i = 0 n − 1 c n 3 ≤ 4 n ⋅ c n 4 4 = c n 3 \begin{align*}
\frac{4}{n}\sum_{i=0}^{n-1}E[Y_{i}] &\le \frac{4}{n}\sum_{i=0}^{n-1}cn^3\\
&\le \frac{4}{n} \cdot \frac{cn^4}{4}=cn^3
\end{align*}
n 4 i = 0 ∑ n − 1 E [ Y i ] ≤ n 4 i = 0 ∑ n − 1 c n 3 ≤ n 4 ⋅ 4 c n 4 = c n 3
所以,E [ Y ( n ) ] = O ( n 3 ) E[Y(n)]=O(n^3) E [ Y ( n )] = O ( n 3 ) ,即E [ 2 X ( n ) ] = O ( n 3 ) E[2^{X(n)}]=O(n^3) E [ 2 X ( n ) ] = O ( n 3 ) 。
由 J e n s e n Jensen J e n se n 不等式(补充证明2):
2 E [ X ( n ) ] ≤ E [ 2 X ( n ) ] = O ( n 3 ) 2^{E[X(n)]}\le E[2^{X(n)}]=O(n^3)
2 E [ X ( n )] ≤ E [ 2 X ( n ) ] = O ( n 3 )
两边取对数:
E [ X ( n ) ] = O ( l g n ) E[X(n)]=O(lgn)
E [ X ( n )] = O ( l g n )
综上所述,一棵有 n n n 个不同关键字的随机构建二叉搜索树的期望高度为 ==O ( l o g n ) O(log\ n) O ( l o g n ) == 。
补充证明1:
要证明 E [ max ( X , Y ) ] ≤ E [ X ] + E [ Y ] E[\max(X,Y)] \leq E[X] + E[Y] E [ max ( X , Y )] ≤ E [ X ] + E [ Y ] ,我们可以考虑分两种情况:
当 X ≤ Y X \leq Y X ≤ Y 时,有 max ( X , Y ) = Y \max(X,Y) = Y max ( X , Y ) = Y 。
当 X > Y X > Y X > Y 时,有 max ( X , Y ) = X \max(X,Y) = X max ( X , Y ) = X 。
因此,我们可以写出以下不等式:
E [ max ( X , Y ) ] = E [ max ( X , Y ) ∣ X ≤ Y ] ⋅ P ( X ≤ Y ) + E [ max ( X , Y ) ∣ X > Y ] ⋅ P ( X > Y ) ≤ E [ Y ] ⋅ P ( X ≤ Y ) + E [ X ] ⋅ P ( X > Y ) = E [ Y ⋅ 1 X ≤ Y ] + E [ X ⋅ 1 X > Y ] \begin{align*}
E[\max(X,Y)] &= E[\max(X,Y) \mid X \leq Y] \cdot P(X \leq Y) + E[\max(X,Y) \mid X > Y] \cdot P(X > Y) \\
&\leq E[Y] \cdot P(X \leq Y) + E[X] \cdot P(X > Y) \\
&= E[Y \cdot \mathbb{1}_{X \leq Y}] + E[X \cdot \mathbb{1}_{X > Y}]
\end{align*}
E [ max ( X , Y )] = E [ max ( X , Y ) ∣ X ≤ Y ] ⋅ P ( X ≤ Y ) + E [ max ( X , Y ) ∣ X > Y ] ⋅ P ( X > Y ) ≤ E [ Y ] ⋅ P ( X ≤ Y ) + E [ X ] ⋅ P ( X > Y ) = E [ Y ⋅ 1 X ≤ Y ] + E [ X ⋅ 1 X > Y ]
其中,1 X ≤ Y \mathbb{1}_{X \leq Y} 1 X ≤ Y 和 1 X > Y \mathbb{1}_{X > Y} 1 X > Y 是指示函数。现在,因为指示函数的期望是概率本身,所以我们有:
E [ Y ⋅ 1 X ≤ Y ] = P ( X ≤ Y ) ⋅ E [ Y ] ≤ P ( X ≤ Y ) ⋅ E [ X ] + P ( X ≤ Y ) ⋅ E [ Y ] E [ X ⋅ 1 X > Y ] = P ( X > Y ) ⋅ E [ X ] ≤ P ( X > Y ) ⋅ E [ X ] + P ( X > Y ) ⋅ E [ Y ] E[Y \cdot \mathbb{1}_{X \leq Y}] = P(X \leq Y) \cdot E[Y] \leq P(X \leq Y) \cdot E[X] + P(X \leq Y) \cdot E[Y] \\
E[X \cdot \mathbb{1}_{X > Y}] = P(X > Y) \cdot E[X] \leq P(X > Y) \cdot E[X] + P(X > Y) \cdot E[Y]
E [ Y ⋅ 1 X ≤ Y ] = P ( X ≤ Y ) ⋅ E [ Y ] ≤ P ( X ≤ Y ) ⋅ E [ X ] + P ( X ≤ Y ) ⋅ E [ Y ] E [ X ⋅ 1 X > Y ] = P ( X > Y ) ⋅ E [ X ] ≤ P ( X > Y ) ⋅ E [ X ] + P ( X > Y ) ⋅ E [ Y ]
将上述两个不等式代入前面的不等式中,我们得到:
E [ max ( X , Y ) ] ≤ E [ Y ] ⋅ P ( X ≤ Y ) + E [ X ] ⋅ P ( X > Y ) ≤ E [ X ] + E [ Y ] E[\max(X,Y)] \leq E[Y] \cdot P(X \leq Y) + E[X] \cdot P(X > Y) \leq E[X] + E[Y]
E [ max ( X , Y )] ≤ E [ Y ] ⋅ P ( X ≤ Y ) + E [ X ] ⋅ P ( X > Y ) ≤ E [ X ] + E [ Y ]
因此,E [ max ( X , Y ) ] ≤ E [ X ] + E [ Y ] E[\max(X,Y)] \leq E[X] + E[Y] E [ max ( X , Y )] ≤ E [ X ] + E [ Y ] ,证毕。
补充证明2:
假设 f f f 是一个 c o n v e x convex co n v e x 函数(国外是凸函数,国内是凹函数)。我们想要证明对于任意随机变量 X X X 和权重 w i w_i w i ,满足 ∑ i w i = 1 \sum_{i} w_i = 1 ∑ i w i = 1 ,有:
E [ f ( X ) ] ≥ f ( E [ X ] ) E[f(X)] \geq f(E[X])
E [ f ( X )] ≥ f ( E [ X ])
为了证明这一点,我们首先需要定义 c o n v e x convex co n v e x 函数的性质。一个函数 f f f 被称为 c o n v e x convex co n v e x 函数,如果对于任意 x 1 , x 2 x_1, x_2 x 1 , x 2 和 0 ≤ λ ≤ 1 0 \leq \lambda \leq 1 0 ≤ λ ≤ 1 ,都有:
f ( λ x 1 + ( 1 − λ ) x 2 ) ≤ λ f ( x 1 ) + ( 1 − λ ) f ( x 2 ) f(\lambda x_1 + (1 - \lambda) x_2) \leq \lambda f(x_1) + (1 - \lambda) f(x_2)
f ( λ x 1 + ( 1 − λ ) x 2 ) ≤ λ f ( x 1 ) + ( 1 − λ ) f ( x 2 )
现在,我们来证明 Jensen 不等式:
根据 c o n v e x convex co n v e x 函数的定义,我们有:
f ( λ E [ X ] + ( 1 − λ ) E [ X ] ) ≤ λ f ( E [ X ] ) + ( 1 − λ ) f ( E [ X ] ) f(\lambda E[X] + (1 - \lambda) E[X]) \leq \lambda f(E[X]) + (1 - \lambda) f(E[X])
f ( λ E [ X ] + ( 1 − λ ) E [ X ]) ≤ λ f ( E [ X ]) + ( 1 − λ ) f ( E [ X ])
⇒ f ( E [ X ] ) ≤ λ f ( E [ X ] ) + ( 1 − λ ) f ( E [ X ] ) \Rightarrow f(E[X]) \leq \lambda f(E[X]) + (1 - \lambda) f(E[X])
⇒ f ( E [ X ]) ≤ λ f ( E [ X ]) + ( 1 − λ ) f ( E [ X ])
⇒ f ( E [ X ] ) ≤ f ( E [ X ] ) \Rightarrow f(E[X]) \leq f(E[X])
⇒ f ( E [ X ]) ≤ f ( E [ X ])
这是显然成立的。因此,我们得出:
E [ f ( X ) ] ≥ f ( E [ X ] ) E[f(X)] \geq f(E[X])
E [ f ( X )] ≥ f ( E [ X ])
这就证明了 Jensen 不等式。
:sweat_smile: 红黑树 Red-Black Tree :fearful:
🥵Q宝速速给我抄抄😋
红黑树的性质
每个节点要么红,要么黑
根节点永远为黑
叶节点 ( N I L ) (NIL) ( N I L ) 为黑
不会有两个相连的红节点(一个节点是红的,那么俩子节点是黑的)
对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点(即黑高 b h ( x ) bh(x) bh ( x ) 相同)
(*)每一棵红黑树都对应着一棵 2 − 3 − 4 2-3-4 2 − 3 − 4 树(或 2 − 3 2-3 2 − 3 树)
其最明显的优势:一棵有 n n n 个节点的红黑树的高度至多为 ==2 l o g ( n + 1 ) 2log( n+1) 2 l o g ( n + 1 ) == 。
证明如下:
设 x x x 为一棵红黑树的根节点,b h ( x ) bh(x) bh ( x ) 为一棵红黑树某个节点的黑高(即一条简单路径上黑色节点的个数),h h h 为一棵红黑树的高度。
我们先证明以 x x x 为根的子树中至少包含 2 b h ( x ) − 1 2^{bh(x)}-1 2 bh ( x ) − 1 个内部节点
要证明此点,我们以数学归纳法进行证明:
1. 当 x . d e p t h = 0 x.depth=0 x . d e pt h = 0 ,即 x x x 为根节点,此时子树中的节点满足 2 b h ( x ) − 1 = 2 0 − 1 = 0 2^{bh(x)}-1=2^{0}-1=0 2 bh ( x ) − 1 = 2 0 − 1 = 0 。
2. 当 x . d e p t h = k x.depth=k x . d e pt h = k ,假设该命题成立,即以 x x x 为根的子树中至少包含 2 b h ( x k ) − 1 2^{bh(x_{k})}-1 2 bh ( x k ) − 1 个内部节点,此时的黑高为 b h ( x k ) bh(x_{k}) bh ( x k ) 或 b h ( x k ) − 1 bh(x_{k})-1 bh ( x k ) − 1 ,这取决于 x . c o l o r x.color x . co l or 是黑还是红。
**3.**则当 x . d e p t h = k + 1 x.depth=k+1 x . d e pt h = k + 1 ,即 x x x 下一节点时,因为同一节点左右子树的黑高一致,所以有:
b h ( x k + 1 ) = b h ( x k ) − 1 bh(x_{k+1})=bh(x_{k})-1
bh ( x k + 1 ) = bh ( x k ) − 1
此时 x . d e p t h = k x.depth=k x . d e pt h = k 的左右子树节点数至少为:2 b h ( x k + 1 ) − 1 = 2 b h ( x k ) − 1 − 1 2^{bh(x_{k+1})}-1=2^{bh(x_{k})-1}-1 2 bh ( x k + 1 ) − 1 = 2 bh ( x k ) − 1 − 1 。
所以内部总节点数至少为:( 2 b h ( x k ) − 1 − 1 ) + ( 2 b h ( x k ) − 1 − 1 ) + 1 = 2 b h ( x k ) − 1 (2^{bh(x_{k})-1}-1)+(2^{bh(x_{k})-1}-1)+1=2^{bh(x_{k})}-1 ( 2 bh ( x k ) − 1 − 1 ) + ( 2 bh ( x k ) − 1 − 1 ) + 1 = 2 bh ( x k ) − 1 。
(这里只加一次 1 1 1 是因为考虑到了左右子树可能存在 N I L NIL N I L )
综上所述,以 x x x 为根的子树中至少包含 ==2 b h ( x ) − 1 2^{bh(x)}-1 2 bh ( x ) − 1 == 个内部节点。
又因为根节点到叶节点至少有一半是黑节点,所以黑高至少为 ⌈ h / 2 ⌉ \left\lceil h/2\right\rceil ⌈ h /2 ⌉ ,于是有:
n ≥ 2 ⌈ h / 2 ⌉ − 1 n \ge 2^{\left\lceil h/2\right\rceil}-1
n ≥ 2 ⌈ h /2 ⌉ − 1
两边取对:
l o g ( n + 1 ) ≥ ⌈ h / 2 ⌉ log(n+1) \ge \left\lceil h/2\right\rceil
l o g ( n + 1 ) ≥ ⌈ h /2 ⌉
即:
h ≤ 2 l o g ( n + 1 ) h\le 2log(n+1)
h ≤ 2 l o g ( n + 1 )
红黑树的旋转
在 C S 61 B CS61B CS 61 B 上,旋转的正式定义为:
rotateLeft(G) : Let x be the right child of G. Make G the new left child of x.
左旋:设 x x x 为 G G G 的右子结点,让 G G G 成为 x x x 的新左子结点。
rotateRight(G): Let x be the left child of G. Make G the new right child of x.
右旋:设 x x x 为 G G G 的左子结点,让 G G G 成为 x x x 的新右子结点。
上面发生的事情的书面描述是这样的:
G G G 的右子项 P P P 与 G G G 合并,并带来了它的孩子。
然后 P P P 将其左子项传递给 G G G ,G G G 向左下降成为 P P P 的左子项。
可以看到树的结构及其高度发生了变化。我们也可以在非根节点上轮换。我们只是暂时断开节点与父节点的连接,旋转节点上的子树,然后重新连接新的根。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 /* 伪代码-左旋 */ LEIT-ROTATE(T,x) y = x.right x.right = y.left if y.left ≠ T.nil y.left.p = x y.p = x.p if x.p == T.nil /* 根节点 */ T.root=y else if x == x.p.left x.p.left = y else x.p.right = y y.left= x X.p = y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private Node rotateLeft (Node h) { Node x = h.right; h.right = x.left; x.left = h; return x; } private Node rotateRight (Node h) { Node x = h.left; h.left = x.right; x.right = h; return x; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 TreeNode *rotateLeft (TreeNode *h) { TreeNode *child = h->right; h->right = child->left; child->left = h; return x; } TreeNode *rotateRight (TreeNode *h) { TreeNode *child = h->left; h->left = child->right; child->right = h; return x; }
复杂度分析
时间复杂度:O ( 1 ) O(1) O ( 1 )
空间复杂度:O ( 1 ) O(1) O ( 1 )
红黑树的插入
插入操作与 B S T BST BST 基本一致。只是将插入节点染成红色,接着调用 R B T − I n s e r t − F i x u p RBT-Insert-Fixup RBT − I n ser t − F i xu p 函数,修复红黑树的性质。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 RB-INSERT(T,z) y = T.nil x = T.root while x != T.nil y = x if z. key < x. key x = x.left else x = x.right z.p = y if y == T.nil T.root= z elseif z.key < y.key y.left = z else y.right = z /* 以上是BST-Insert操作 */ z.left = T.nil z.right = T.nil z.color = RED RB-INSERT-FIXUP(T,z)
f i x u p fixup f i xu p 原理分析
接下来我们来分析一下 F i x u p Fixup F i xu p 的原理:
执行 I n s e r t Insert I n ser t 操作后,针对 R B T RBT RBT 的五个性质,我们不难发现:
性质 1 1 1 和性质 3 3 3 以及性质 5 5 5 依然成立
可能被破坏的是
性质 2 2 2 (根节点为黑色)
性质 4 4 4 (不会有两个相连的红节点)
并且这两个性质至多只有一条 被破坏。
因此我们从修复性质 4 4 4 入手,确定循环终止条件,即 z z z 的父节点是黑色时终止。
接下来我们以 z . p z.p z . p 是左孩子为例,右孩子的代码直接所有方向取反。
此时,有如下 3 3 3 种情况:
c a s e 1 case1 c a se 1 :z z z 的叔节点 y y y 是红色的
该情况在 z z z 和 z . p z.p z . p 都是红色时发生。因为 z . p . p z.p.p z . p . p 是黑色的,所以我们把 z . p z.p z . p 和 y y y 染成黑色,把 z . p . p z.p.p z . p . p 染成红色以保持性质 5 5 5 。然后我们以 z . p . p z.p.p z . p . p 作为新的 z z z 节点来重复循环(即 z z z 上移两个节点)。
c a s e 2 case2 c a se 2 :z z z 的叔结点 y y y 是黑色的且 z z z 是一个右孩子( z z z 和其父节点方向相反)
c a s e 3 case3 c a se 3 :z z z 的叔结点 y y y 是黑色的且 z z z 是一个左孩子( z z z 和其父节点方向一致)
我们将情况 2 2 2 和 3 3 3 合并在一起来看。
对于情况 2 2 2 ,我们立即对 z . p z.p z . p 执行一次左旋,将其转变为情况 3 3 3 。
对于情况 3 3 3 ,我们能肯定的是,z . p . p z.p.p z . p . p 必定为黑色。为了修复性质,我们交换 z . p z.p z . p 和z . p . p z.p.p z . p . p 的颜色,并对 z . p . p z.p.p z . p . p 执行一次右旋。此时,z . p z.p z . p 的颜色是黑色,循环终止,修复成功。
f i x u p fixup f i xu p 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 RB-INSERT-FIXUP(T,z) while z.p.color == RED if z.p == z.p.p.left y = z.p.p.right /* y是z的叔节点 */ /* case1 */ if y.color == RED z.p.color = BLACK y.color = BLACK z.p.p.color = RED z = z.p.p else /* case2 */ if z == z. p. right z = z.p LEFT-ROTATE(T,z) /* case3 */ z.p.color = BLACK z.p.p.color = RED RIGHT-ROTATE(T,z.p.p) else(same as then clause with "right" and "left" exchanged) ...... T.root.color= BLACK
复杂度分析
时间复杂度:O ( l o g n ) O(log\ n) O ( l o g n )
树高为 O ( l o g n ) O(log\ n) O ( l o g n ) ,则插入操作需要 O ( l o g n ) O(log\ n) O ( l o g n ) 的时间
调用 F i x u p Fixup F i xu p 时,仅当情况 1 1 1 发生时,z z z 才会沿树上升两层,此时需要O ( l o g n ) O(log\ n) O ( l o g n ) 的时间,其他情况都是 O ( 1 ) O(1) O ( 1 ) 。
因此是 O ( l o g n ) O(log\ n) O ( l o g n )
空间复杂度:O ( 1 ) O(1) O ( 1 )
红黑树的删除
删除操作跟 B S T BST BST 的删除操作类似,只是多了一个记录节点 x x x 以及在最后添加了一个 R B T − D e l e t e − F i x u p RBT-Delete-Fixup RBT − De l e t e − F i xu p 的操作
代码实现
1 2 3 4 5 6 7 8 9 10 /* 参考BST的删除 */ /* 用以v为根的子树替换以u为根的子树 */ RB-TRANSPLANT(T, u, v) if u.p == T.nil /* 若u为根节点 */ T.root=v else if u == u.p.left /* 若u是其父节点的左孩子 */ u.p.left = v else /* 若u是其父节点的右孩子 */ u.p.right = v v.p = u.p
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 /* 参考BST的删除 */ /* attention:第9、13、24行传递的第三个参数与x相同,x只是引用作用,不参与实际删除操作,只是给后续的fixup操作提供参数 */ RB-DELETE(T, z) y = z y-original-color = y.color /* 若z的左树为空 */ if z.left == T.nil x = z.right RB-TRANSPLANT(T, z, z.right) /* 用z的右孩子代替z */ /* 若z的右树为空 */ else if z.right == T.nil x = z.left RB-TRANSPLANT(T, z, z.left) /* 用z的左孩子代替z */ /* 若z有两个孩子 */ else y = TREE-MINIMUM(z.right) /* y是z的后继 */ y-original-color = y.color x = y.right /* 若y是z的孩子 */ if y.p == z x.p = y /* 若y不是z的孩子 */ else RB-TRANSPLANT(T, y, y.right) /* 用y的右孩子代替y */ y.right = z.right /* 令z的右孩子 */ y.right.p = y /* 成为y的右孩子 */ RB-TRANSPLANT(T, z, y) /* 用后继y代替z */ y.left = z.left /* 将z的左孩子给 */ y.left.p = y /* (肯定)没有左孩子的y */ y.color = z.color if y-original-color == BLACK /* y是红的不用管 不会影响红黑树的性质 */ RB-DELETE-FIXUP(T, x)
f i x u p fixup f i xu p 原理分析
下面重点讲 F i x u p Fixup F i xu p 的原理:
记录节点 x x x ,其为我们替换 y y y 所需的节点。
因为其引用的节点替换 y y y 后可能会违反红黑树的性质,所以我们要对 x x x 及其子树向上进行 F i x u p Fixup F i xu p 操作。
如果 y y y 是黑色 的话,会产生三个问题:
1. 若 y y y 是原先的根节点,而其一个红色的孩子代替了 y y y ,则会违反==性质2==,即根节点不能为红色。
2. 若 x x x 和 x . p x.p x . p 是红色的话,则违反了==性质4==,即两个红色节点不能相连。
**3.**若移动 y y y 后导致之前包含 y y y 的简单路径的 b h ( x ) bh(x) bh ( x ) 少了 1 1 1 ,则 y y y 的任何祖先都不满足性质5,即 b h ( x ) bh(x) bh ( x ) 相等。
解决这一问题的方法就是将现在占有 y y y 原先位置的 x x x 视作还有额外一层黑色,即黑黑色 或红黑色 。
(但 x . c o l o r x.color x . co l or 不变 ,只是视作有两种颜色,这里则变为违反==性质1==,但是后面会进行修复)
所以问题转变为 F i x u p Fixup F i xu p 操作需要修复性质 1 、 2 、 4 1、2、4 1 、 2 、 4 。
w h i l e while w hi l e 循环的目标就是要把额外的黑色沿树上移,直到:
1. x x x 指向红黑节点,此时将其染成单 个黑色
2. x x x 指向根节点,此时可以简单地"移除"额外的黑色
3. 执行适当的旋转及重新染色,退出循环
在循环里, x x x 是黑黑色的~~(如果是红黑色,那就直接染成黑色了,瞎进什么循环)~~,下面设 x x x 是其父节点左孩子, w w w 是 x x x 的兄弟节点。
则会有如下几种情况:
c a s e 1 case1 c a se 1 :x x x 的兄弟节点 w w w 是红色的 兄弟兄弟,你怎么是红色的(疾旋鼬.jpg)
因为 w w w 是红色,其必有俩黑色子节点,所以可以改变 w w w 以及 w . p w.p w . p 的颜色,并做一次旋转。
这样就将 c a s e 1 case1 c a se 1 转换为接下来的三种情况处理。
c a s e 2 case2 c a se 2 :x x x 的兄弟节点 w w w 是黑色的且 w w w 的两个孩子都是黑色
因为 w w w 是黑色的,所以从 x x x 和 w w w 上各去掉一层黑色,即 w w w 变为红色,x x x 上移。为补偿删掉的一层黑色,将新的 x x x 加一层黑色,继续循环,直到满足条件。
若从 c a s e 1 case1 c a se 1 过来的,此时新的 x x x 是红黑色的,满足条件,直接将新的 x x x 染成黑色,结束循环,修复完成。
c a s e 3 case3 c a se 3 :x x x 的兄弟节点 w w w 是黑色的且 w w w 的右节点是黑色,左节点是红色
此情况需要转换为 c a s e 4 case4 c a se 4 来解决,所以我们先交换 w w w 和 w . l e f t w.left w . l e f t 的颜色,然后对 w w w 进行右旋。
c a s e 4 case4 c a se 4 :x x x 的兄弟节点 w w w 是黑色的且 w w w 的右节点是红色
此情况下,将 w . r i g h t w.right w . r i g h t 变为黑色,x . p x.p x . p 与 w w w 交换颜色,并对 x . p x.p x . p 进行一次左旋,消除 x x x 的额外黑色,最后将 x x x 设为根节点,跳出循环。( x x x 的那一层黑色给到了原来 w w w 的右节点)
f i x u p fixup f i xu p 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 RB-DELETE-FIXUP(T, x) while x != T. root and x.color == BLACK if x == x.p.left w = x.p.right /* case1 */ if w.color == RED w.color = BLACK x.p.color = RED LEFT-ROTATE(T, x.p) w = x.p.right /* case2 */ if w.left.color == BLACK and w.right.color == BLACK w.color = RED x = x.p else /* case3 */ if w.right.color == BLACK w.left.color = BLACK w.color = RED RIGHT-ROTATE(T,w) w = x.p.right /* case4- */ w.color = x.p.color x.p.color = BLACK w.right.color = BLACK LEFT-ROTATE(T, x.p) x = T.root else (same as then clause with "right" and "left" exchanged) ...... x.color = BLACK
复杂度分析
B树 B-tree
未完工喵~:yum:
s h m m shmm s hmm 速速🤺