# Leetcode刷題學習筆記 -- Binary Search Tree(BST) ## 簡介 ref : [wiki](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9) 二元搜尋樹(英語:Binary Search Tree),也稱為有序二元樹(ordered binary tree)或排序二元樹(sorted binary tree),是指一棵空樹或者具有下列性質的二元樹: 1. 若任意節點的左子樹不空,則左子樹上所有節點的值均小於它的根節點的值; 2. 若任意節點的右子樹不空,則右子樹上所有節點的值均大於它的根節點的值; 3. 任意節點的左、右子樹也分別為二元搜尋樹; ![BST](https://courses.engr.illinois.edu/cs225/sp2019/assets/notes/bst/bsttreetraversal.png) 如果BST,呈現不平衡的狀態,例如都串在同一個直線。那最差情況都是O(n)。 | 演算法 | 平均 | 最差 | | -------- | -------- | -------- | | 空間 | O(n) | O(n) | | 搜尋 | O(logn) | O(n) | | 插入 | O(logn) | O(n) | | 刪除 | O(logn) | O(n) | 二元搜尋樹的最壞效率是O(n),但它支援動態查詢,且有很多改進版的二元搜尋樹可以使樹高為O(log n),從而將最壞效率降至O(log n),如==AVL樹==、==紅黑樹==等。 ## Inorder Traversal 當BST(Binary Search Tree)使用Inorder Traversal的時候,可以得到遞增或是遞減的數列。 以下的程式碼是得到遞增序列。 ```cpp void dfs(TreeNode *root, vector<int>& rtn) { if(!root) return; dfs(root->left); rtn.push_back(root->val); dfs(root->right); } ``` 以下的程式碼可以得到遞減序列。 ```cpp void dfs(TreeNode *root, vector<int>& rtn) { if(!root) return; dfs(root->right); rtn.push_back(root->val); dfs(root->left); } ``` 另外也可以用stack來實現 ```cpp void inorder(TreeNode *root) { stack<TreeNode *> st; TreeNode *p = root; while(p || !st.empty()) { // 先把全部的left存起來 while(p) { st.push(p); p = p->left; } // 取出最後一個left node p = st.top(); st.pop(); cout << p->val << endl; // 檢查right child node p = p->right; } } ``` 很多題目會根據上訴兩種來變形。 1. 遞增訪問 + [230. Kth Smallest Element in a BST](/neNmzsZsRrmYQ-RtPWbYxA?both#230-Kth-Smallest-Element-in-a-BST) + [897. Increasing Order Search Tree](/neNmzsZsRrmYQ-RtPWbYxA?both#897-Increasing-Order-Search-Tree) + [99. Recover Binary Search Tree]() 2. 遞減訪問 + [538. Convert BST to Greater Tree](/neNmzsZsRrmYQ-RtPWbYxA?both#538-Convert-BST-to-Greater-Tree) 3. 找兩個node之間最小的差 + [783. Minimum Distance Between BST Nodes](/neNmzsZsRrmYQ-RtPWbYxA?both#783-Minimum-Distance-Between-BST-Nodes) ### [99. Recover Binary Search Tree](https://leetcode.com/problems/recover-binary-search-tree/) 在BST中有兩個node被交換了,試復原成原本的BST。 > 1. 使用inorder traversal。如果是正常的BST會得到從小到大的排列。 > 2. 題目只有兩個node被交換了,例如 > 1, 2, 3, 4, 5, 6 --> 1, **5**, 3, 4, **2**, 6 > 5, 3 --> prev > curr, 取前者 > 4, 2 --> prev > cur, 取後者 ```cpp void recoverTree(TreeNode* root) { TreeNode *prev = nullptr, *first = nullptr, *second = nullptr, *p = root; stack<TreeNode *> st; while(p || !st.empty()) { while(p) { st.push(p); p = p->left; } p = st.top(); st.pop(); if(prev && prev->val > p->val) { if(!first) first = prev; // 取前者 second = p; // 取後者 } prev = p; p = p->right; } swap(first->val, second->val); } ``` ### [230. Kth Smallest Element in a BST](https://leetcode.com/problems/kth-smallest-element-in-a-bst/) 找出BST中,第k個最小的值。 > 1. 在BST中使用Inorder Traversal來找出第k個數值。 ```cpp class Solution { int k, rtn; void helper(TreeNode *root) { if(!root) return; helper(root->left); k--; if(k == 0) rtn = root->val; else if(k < 0) return; helper(root->right); } public: int kthSmallest(TreeNode* root, int k) { this->k = k; helper(root); return rtn; } }; ``` ### [897. Increasing Order Search Tree](https://leetcode.com/problems/increasing-order-search-tree/) 把BST,從小到大,依序串在node的右邊。 ![897Example](https://assets.leetcode.com/uploads/2020/11/17/ex1.jpg) > 1. 對BST做Inorder Traversal。 > 2. 對每個node,把left child設成nullptr, 把right child設成下一個走訪的node。 ```cpp class Solution { void helper(TreeNode *root) { if(!root) return; helper(root->left); root->left = nullptr; cur->right = root; cur = cur->right; helper(root->right); } TreeNode *cur; public: TreeNode* increasingBST(TreeNode* root) { TreeNode *newroot = new TreeNode(); cur = newroot; helper(root); return newroot->right; } }; ``` ### [538. Convert BST to Greater Tree](https://leetcode.com/problems/convert-bst-to-greater-tree/) 給你一個BST,試給出一個Greater Tree。 ![538_Example](https://assets.leetcode.com/uploads/2019/05/02/tree.png) > 1. 對BST做Inorder Traversal(從大到小)。 > 2. 每遇到一個node就把數值相加,並更新到目前的node。 ```cpp class Solution { int sum = 0; void helper(TreeNode *root) { if(!root) return; helper(root->right); sum += root->val; root->val = sum; helper(root->left); } public: TreeNode* convertBST(TreeNode* root) { helper(root); return root; } }; ``` ### [450. Delete Node in a BST(Medium)](https://leetcode.com/problems/delete-node-in-a-bst/) 給你一個key,刪除BST中和key值一樣的node,並且保持原本的樹還是BST。 例如: Input: root = [5,3,6,2,4,null,7], key = 3 Output: [5,4,6,2,null,null,7] ![450 Example](https://assets.leetcode.com/uploads/2020/09/04/del_node_1.jpg) 解題思路: > case 1: 如果要刪除的node是leaf,那就直接刪除。 > case 2: 如果要刪除的node不是leaf,但是只有一個child,則直接把child接上來。 > case 3: ==如果要刪除的node不是leaf,而且有兩個child!==,為了保持刪除後依然是BST,所以勢必要調整樹的結構。如何做到最小調整? ==使用右子樹最小值,或是左子樹最大值代替原本欲刪除的node,就可以達到最小的修正。== ![](https://i.imgur.com/7Nw3KsK.png) ```cpp= TreeNode* deleteNode(TreeNode* root, int key) { if(!root) return nullptr; if(key > root->val) root->right = deleteNode(root->right, key); else if(key < root->val) root->left = deleteNode(root->left, key); else { // 只有一個child node,或是node為leaf if(!root->left || !root->right) root = root->left ? root->left : root->right; else { // node有兩個child // 找出右子樹最小值 TreeNode *cur = root->right; while(cur->left) cur = cur->left; root->val = cur->val; // 刪除右子樹最小值的node root->right = deleteNode(root->right, cur->val); } } return root; } ``` 找出左邊子樹的最大值一樣也可以。 ```cpp= // 找出左子樹最大值 TreeNode *cur = root->left; while(cur->right) cur = cur->right; root->val = cur->val; // 刪除左子樹最大值的node root->left = deleteNode(root->left, cur->val); ``` ### [235. Lowest Common Ancestor of a Binary Search Tree(Easy)](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) 給你兩個在BST中的node, p和q。找出這兩個的共同祖先(ancestor)。 > 1. 因為沒說明p, q是否有什麼關係,所以必須用max(), min()找出最大最小值。 > 2. root->val > max(p->val, q->val) 表示p和q都在root的左側。 > 3. root->val < min(p->val, q->val) 表示p和q都在root的右側。 > 4. 不是上述兩種狀況表示root即為共同的祖先。 ```cpp= TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(!root) return nullptr; else if(root->val > max(p->val, q->val)) return lowestCommonAncestor(root->left, p, q); else if(root->val < min(p->val, q->val)) return lowestCommonAncestor(root->right, p, q); else // root->val > min(p->val, q->val) && root->val < max(p->val, q->val) return root; } ``` ### [783. Minimum Distance Between BST Nodes](https://leetcode.com/problems/minimum-distance-between-bst-nodes/description/) 在BST中,找出兩個node之間最小的差。 > 1. 因為BST使用inorder traversal可以得到遞增數列,使用此特性比較前後兩個node,就可以找出最小的差值。 ```cpp= class Solution { // 在BST中使用inorder可以得到從小到大的排序。 int ans = 1e5 + 1, prev = -1; void helper(TreeNode *root) { if(!root) return; helper(root->left); if(prev != -1) ans = min(ans, root->val - prev); prev = root->val; helper(root->right); } public: int minDiffInBST(TreeNode* root) { helper(root); return ans; } }; ``` ###### tags: `leetcode` `刷題`