樹(Tree)解題總結
阿新 • • 發佈:2020-08-21
定義
樹是一種抽象資料型別(ADT)或是實現這種抽象資料型別的資料結構,用來模擬具有樹狀結構性質的資料集合。它是由 n(n>0) 個有限節點組成一個具有層次關係的集合。。
二叉搜尋樹(Binary Search Tree,簡稱 BST)是一種很常用的的二叉樹。它的定義是:一個二叉樹中,任意節點的值要大於等於左子樹所有節點的值,且要小於等於右邊子樹的所有節點的值。
如下就是一個符合定義的 BST:
演算法模板
void traverse(TreeNode root) { // root 需要做什麼?在這做。 // 其他的不用 root 操心,拋給框架 traverse(root.left); traverse(root.right); }
在二叉樹框架之上,擴展出一套 BST 遍歷框架:
void BST(TreeNode root, int target) {
if (root.val == target)
// 找到目標,做點什麼
if (root.val < target)
BST(root.right, target);
if (root.val > target)
BST(root.left, target);
}
前序遍歷(遞迴):
List<int> preorder(TreeNode root) { List<int> ans = new List<int>(); if (root == null) { return ans; } ans.Add(root.Val); ans.AddRange(preorder(root.Left)); ans.AddRange(preorder(root.Right)); return ans; }
中序遍歷(遞迴):
List<int> inorder(TreeNode root) {
List<int> ans = new List<int>();
if (root == null) {
return ans;
}
ans.AddRange(preorder(root.Left));
ans.Add(root.Val);
ans.AddRange(preorder(root.Right));
return ans;
}
後序遍歷(遞迴):
List<int> postorder(TreeNode root) { List<int> ans = new List<int>(); if (root == null) { return ans; } ans.AddRange(preorder(root.Left)); ans.AddRange(preorder(root.Right)); ans.Add(root.Val); return ans; }
前序遍歷(迭代/棧):
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res; //儲存結果
stack<TreeNode*> call; //呼叫棧
if(root!=nullptr) call.push(root); //首先介入root節點
while(!call.empty()){
TreeNode *t = call.top();
call.pop(); //訪問過的節點彈出
if(t!=nullptr){
if(t->right) call.push(t->right); //右節點先壓棧,最後處理
if(t->left) call.push(t->left);
call.push(t); //當前節點重新壓棧(留著以後處理),因為先序遍歷所以最後壓棧
call.push(nullptr); //在當前節點之前加入一個空節點表示已經訪問過了
}else{ //空節點表示之前已經訪問過了,現在需要處理除了遞迴之外的內容
res.push_back(call.top()->val); //call.top()是nullptr之前壓棧的一個節點,也就是上面call.push(t)中的那個t
call.pop(); //處理完了,第二次彈出節點(徹底從棧中移除)
}
}
return res;
}
};
中序遍歷(迭代/棧):
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> call;
if(root!=nullptr) call.push(root);
while(!call.empty()){
TreeNode *t = call.top();
call.pop();
if(t!=nullptr){
if(t->right) call.push(t->right);
call.push(t); //在左節點之前重新插入該節點,以便在左節點之後處理(訪問值)
call.push(nullptr); //nullptr跟隨t插入,標識已經訪問過,還沒有被處理
if(t->left) call.push(t->left);
}else{
res.push_back(call.top()->val);
call.pop();
}
}
return res;
}
};
後序遍歷(迭代/棧):
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> call;
if(root!=nullptr) call.push(root);
while(!call.empty()){
TreeNode *t = call.top();
call.pop();
if(t!=nullptr){
call.push(t); //在右節點之前重新插入該節點,以便在最後處理(訪問值)
call.push(nullptr); //nullptr跟隨t插入,標識已經訪問過,還沒有被處理
if(t->right) call.push(t->right);
if(t->left) call.push(t->left);
}else{
res.push_back(call.top()->val);
call.pop();
}
}
return res;
}
};
要點
- 二叉樹演算法設計的總路線:把當前節點要做的事做好,其他的交給遞迴框架,不用當前節點操心。
- 如果當前節點會對下面的子節點有整體影響,可以通過輔助函式增長引數列表,藉助引數傳遞資訊。
- 刪除二叉搜尋樹的節點有三種情況:
- A 恰好是末端節點,兩個子節點都為空,那麼它可以當場去世了
- A 只有一個非空子節點,那麼它要讓這個孩子接替自己的位置。
- A 有兩個子節點,麻煩了,為了不破壞 BST 的性質,A 必須找到左子樹中最大的那個節點,或者右子樹中最小的那個節點來接替自己。