ref : wiki
二元搜尋樹(英語:Binary Search Tree),也稱為有序二元樹(ordered binary tree)或排序二元樹(sorted binary tree),是指一棵空樹或者具有下列性質的二元樹:
如果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樹、紅黑樹等。
當BST(Binary Search Tree)使用Inorder Traversal的時候,可以得到遞增或是遞減的數列。
以下的程式碼是得到遞增序列。
void dfs(TreeNode *root, vector<int>& rtn) {
if(!root) return;
dfs(root->left);
rtn.push_back(root->val);
dfs(root->right);
}
以下的程式碼可以得到遞減序列。
void dfs(TreeNode *root, vector<int>& rtn) {
if(!root) return;
dfs(root->right);
rtn.push_back(root->val);
dfs(root->left);
}
另外也可以用stack來實現
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;
}
}
很多題目會根據上訴兩種來變形。
在BST中有兩個node被交換了,試復原成原本的BST。
- 使用inorder traversal。如果是正常的BST會得到從小到大的排列。
- 題目只有兩個node被交換了,例如
1, 2, 3, 4, 5, 6 –> 1, 5, 3, 4, 2, 6
5, 3 –> prev > curr, 取前者
4, 2 –> prev > cur, 取後者
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);
}
找出BST中,第k個最小的值。
- 在BST中使用Inorder Traversal來找出第k個數值。
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;
}
};
把BST,從小到大,依序串在node的右邊。
- 對BST做Inorder Traversal。
- 對每個node,把left child設成nullptr, 把right child設成下一個走訪的node。
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;
}
};
給你一個BST,試給出一個Greater Tree。
- 對BST做Inorder Traversal(從大到小)。
- 每遇到一個node就把數值相加,並更新到目前的node。
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;
}
};
給你一個key,刪除BST中和key值一樣的node,並且保持原本的樹還是BST。
例如:
Input: root = [5,3,6,2,4,null,7], key = 3
Output: [5,4,6,2,null,null,7]
解題思路:
case 1: 如果要刪除的node是leaf,那就直接刪除。
case 2: 如果要刪除的node不是leaf,但是只有一個child,則直接把child接上來。
case 3: 如果要刪除的node不是leaf,而且有兩個child!,為了保持刪除後依然是BST,所以勢必要調整樹的結構。如何做到最小調整? 使用右子樹最小值,或是左子樹最大值代替原本欲刪除的node,就可以達到最小的修正。
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;
}
找出左邊子樹的最大值一樣也可以。
// 找出左子樹最大值
TreeNode *cur = root->left;
while(cur->right) cur = cur->right;
root->val = cur->val;
// 刪除左子樹最大值的node
root->left = deleteNode(root->left, cur->val);
給你兩個在BST中的node, p和q。找出這兩個的共同祖先(ancestor)。
- 因為沒說明p, q是否有什麼關係,所以必須用max(), min()找出最大最小值。
- root->val > max(p->val, q->val) 表示p和q都在root的左側。
- root->val < min(p->val, q->val) 表示p和q都在root的右側。
- 不是上述兩種狀況表示root即為共同的祖先。
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;
}
在BST中,找出兩個node之間最小的差。
- 因為BST使用inorder traversal可以得到遞增數列,使用此特性比較前後兩個node,就可以找出最小的差值。
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;
}
};
leetcode
刷題