我要好offer之 二叉樹大總結

一. 二叉樹定義

二叉樹具備自然的遞歸特性,凡是二叉樹相關題,首先應該聯想到遞歸html

struct BinTreeNode {
    BinTreeNode* left;
    BinTreeNode* right;
    int          val;
    BinTreeNode(int value) : left(nullptr), right(nullptr), val(value) { }
};

 

二. 二叉樹遍歷

詳見筆者博文:二叉樹遍歷大總結node

#include <iostream>
#include <vector>
#include <stack>
using namespace std;

// 二叉樹遞歸定義
struct BinTreeNode {
    BinTreeNode* left;
    BinTreeNode* right;
    int          val;
    BinTreeNode(int value) : left(NULL), right(NULL), val(value) { }
};

// 根據結點值構建二叉樹
BinTreeNode* BuildBinTree(BinTreeNode* root, int value) {
    if (root == NULL) {
        root = new BinTreeNode(value);  // 結點爲空,new一個節點
    } else if (value <= root->val) {
        root->left = BuildBinTree(root->left, value);
    } else {
        root->right = BuildBinTree(root->right, value);
    }
    return root;
}

// 先序遍歷,遞歸
void PreOrder(BinTreeNode* root) {
    if (root == NULL) return;
    std::cout << root->val << "-->";  // 訪問節點
    PreOrder(root->left);
    PreOrder(root->right);
}

// 先序遍歷,非遞歸,棧版本
void PreOrderStk(BinTreeNode* root) {
    if (root == NULL) return;
    std::stack<BinTreeNode*> stk;
    while (root != NULL || !stk.empty()) {
        while (root != NULL) {
            std::cout << root->val << "-->";  // 訪問節點
            stk.push(root);
            root = root->left;  // 深度優先搜索到最左節點,而後回溯
        }
        BinTreeNode* cur = stk.top();
        stk.pop();
        root = cur->right;     // 轉向右子樹
    }
}

// 先序遍歷,非遞歸,非棧,Morris
void PreOrderMorris(BinTreeNode* root) {
    if (root == NULL) return;
    BinTreeNode* prev = NULL;  
    BinTreeNode* cur = root;   // cur爲正在訪問的結點
    while (cur != NULL) {
        if (cur->left == NULL) {
            std::cout << cur->val << "-->";  // 訪問節點
            cur = cur->right;
        } else {
            prev = cur->left;            // 前驅結點爲 左子樹最右下節點
            while (prev->right != NULL && prev->right != cur) {
                prev = prev->right;
            }
            if (prev->right == NULL) {           // 還沒線索化,則創建線索
                std::cout << cur->val << "-->";  // 僅這一行與中序不一樣
                prev->right = cur;               // 創建線索
                cur = cur->left;                 // 轉向處理左子樹
            } else {
                cur = cur->right;                // 轉向處理右子樹
                prev->right = NULL;              // 已經線索化,則刪除線索
            }
        }
    }
}

// 中序遍歷,遞歸
void InOrder(BinTreeNode* root) {
    if (root == NULL) return;
    InOrder(root->left);
    std::cout << root->val << "-->";  // 訪問節點
    InOrder(root->right);
}

// 中序遍歷,非遞歸,棧版本
void InOrderStk(BinTreeNode* root) {
    if (root == NULL) return;
    std::stack<BinTreeNode*> stk;
    while (root != NULL || !stk.empty()) {
        while (root != NULL) {
            stk.push(root);
            root = root->left;  // 深度優先搜索到最左節點,而後回溯
        }
        BinTreeNode* cur = stk.top();
        stk.pop();
        std::cout << cur->val << "-->";  // 訪問節點
        root = cur->right;     // 轉向右子樹
    }
}

// 中序遍歷,非遞歸,非棧,Morris
void InOrderMorris(BinTreeNode* root) {
    if (root == NULL) return;
    BinTreeNode* prev = NULL;  
    BinTreeNode* cur = root;   // cur爲正在訪問的結點
    while (cur != NULL) {
        if (cur->left == NULL) {
            std::cout << cur->val << "-->";  // 訪問節點
            cur = cur->right;
        } else {
            prev = cur->left;               // 前驅結點爲 左子樹最右下節點
            while (prev->right != NULL && prev->right != cur) {
                prev = prev->right;
            }
            if (prev->right == NULL) {          // 還沒線索化,則創建線索
                prev->right = cur;              // 創建線索
                cur = cur->left;                // 轉向處理左子樹
            } else {
                std::cout << cur->val << "-->"; // 已經線索化,則訪問節點,並刪除線索
                cur = cur->right;               // 轉向處理右子樹
                prev->right = NULL;             // 刪除線索
            }
        }
    }
}

// 後序遍歷,遞歸
void PostOrder(BinTreeNode* root) {
    if (root == NULL) return;
    PostOrder(root->left);
    PostOrder(root->right);
    std::cout << root->val << "-->";
}

// 後序遍歷,非遞歸,棧版本
void PostOrderStk(BinTreeNode* root) {
    if (root == NULL) return;
    std::stack<BinTreeNode*> stk;
    BinTreeNode* cur = root;
    BinTreeNode* prev = NULL;
    do {
        while (cur != NULL) {
            stk.push(cur);
            cur = cur->left;
        }
        prev = NULL;
        while (!stk.empty()) {
            cur = stk.top();
            stk.pop();
            if (cur->right == prev) {   // 從右子樹返回,表示其右子樹已經訪問完畢
                std::cout << cur->val << "-->";
                prev = cur;
            } else {
                stk.push(cur);
                cur = cur->right;      // 其右子樹還未訪問,轉向其右子樹訪問
                break;
            }
        }
    } while (!stk.empty());
}

// 層次遍歷,遞歸版
void LevelOrder(BinTreeNode* root, int level, std::vector<std::vector<int>>& res) {
    if (root == NULL) return;
    if (level > res.size()) {
        res.push_back(std::vector<int>());
    }
    res.at(level - 1).push_back(root->val);
    LevelOrder(root->left, level + 1, res);
    LevelOrder(root->right, level + 1, res);
}
std::vector<std::vector<int>> LevelOrderHelp(BinTreeNode* root) {
    std::vector<std::vector<int>> res;
    LevelOrder(root, 1, res);
    return res;
}

// 層次遍歷,非遞歸,隊列版
void LevelOrderQueue(BinTreeNode* root) {
    if (root == NULL) return;
    std::deque<BinTreeNode*> dequeTreeNode;
    dequeTreeNode.push_back(root);
    while (!dequeTreeNode.empty()) {
        BinTreeNode* cur = dequeTreeNode.front();
        dequeTreeNode.pop_front();
        std::cout << cur->val << "-->"; // 訪問節點
        if (cur->left  != NULL) dequeTreeNode.push_back(cur->left);
        if (cur->right != NULL) dequeTreeNode.push_back(cur->right);
    }
}

int main() {
    // your code goes here
    std::vector<int> num = {6, 2, 7, 1, 4, 9, 3, 5, 8};
    BinTreeNode* root = nullptr;
    for (auto val : num) {
        root = BuildBinTree(root, val);
    }
    std::cout << "1:PreOrder:" << std::endl;
    std::cout << "\t PreOrder:" ; 
    PreOrder(root);
    std::cout << std::endl;
    std::cout << "\t PreOrderStk:"; 
    PreOrderStk(root); 
    std::cout << std::endl;
    std::cout << "\t PreOrderMorris:"; 
    PreOrderMorris(root); 
    std::cout << std::endl;
    
    std::cout << "2:InOrder:" << std::endl;
    std::cout << "\t InOrder:";
    InOrder(root); 
    std::cout << std::endl;
    std::cout << "\t InOrderStk:";
    InOrderStk(root); 
    std::cout << std::endl;
    std::cout << "\t InOrderMorris:";
    InOrderMorris(root);
    std::cout << std::endl;
    
    std::cout << "3:PostOrder:" << std::endl;
    std::cout << "\t PostOrder:";
    PostOrder(root);
    std::cout << std::endl;
    std::cout << "\t PostOrderStk:";
    PostOrderStk(root);
    std::cout << std::endl;

    std::cout << "4:LevelOrder:" << std::endl;
    std::cout << "\t LevelOrder:";
    LevelOrderQueue(root); 
    std::cout << std::endl;
    return 0;
}

 

三. 二叉樹構建

1. 先序序列和中序序列構建一顆二叉樹  中序序列和後序序列構建一棵二叉樹ios

注:爲何先序和後序不能肯定一棵二叉樹?(例如:先序ABCD,後序CBDA)數組

給定先序遍歷序列和後序遍歷序列,求有多少種不一樣形態的二叉樹?post

考慮簡單的兩個節點A和B,前序遍歷爲AB,後序遍歷爲BA,則會存在兩種二叉樹: A->left=B 和 A->right=Bui

存在不一樣二叉樹的緣由在於某節點缺乏其中一個子樹,在遍歷序列中呈現出前序和後序遍歷中節點的值相鄰但順序相反(例如先序AB,後序BA),那麼總的二叉樹數量番倍spa

int BinTreeNum(const std::vector<int>& preOrder, const std::vector<int>& postOrder) {
    assert(preOrder.size() == postOrder.size());
    int num = 1;
    for (int i = 0; i < preOrder.size(); ++i) {
        auto iter = std::find(postOrder.begin(), postOrder.end(), preOrder.at(i));
        int index = std::distance(postOrder.begin(), iter);
        if (i < preOrder.size() - 1 && index > 0 && preOrder.at(i+1) == postOrder.at(index-1)) {
            num *= 2;
        }
    }
    return num;
}

 

2. 有序數組/鏈表 構建一顆二叉查找樹BST.net

 

四. 二叉樹判斷

 1. 判斷二叉查找樹BSTcode

 2. 判斷平衡二叉樹htm

 3. 判斷對稱二叉樹

 4. 判斷相同二叉樹

 5. 判斷二叉樹的子樹

 

五. 二叉樹路徑相關問題

 1. 二叉樹路徑和問題

 

 2. 二叉樹節點之間的最大距離(任意兩個節點之間的最大步數)

二叉樹中相距最遠的兩個節點之間的距離。
遞歸解法:
(1)若是二叉樹爲空,返回0,同時記錄左子樹和右子樹的深度,都爲0
(2)若是二叉樹不爲空,最大距離要麼是左子樹中的最大距離,要麼是右子樹中的最大距離,要麼是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,同時記錄左子樹和右子樹節點中到根節點的最大距離

int GetMaxDistance(BinTreeNode* root, int& maxLeft, int& maxRight) {
    // maxLeft,  左子樹中的節點距離根節點的最遠距離  
    // maxRight, 右子樹中的節點距離根節點的最遠距離
    if (root == NULL) {
        maxLeft = 0;
        maxRight = 0;
        return 0;
    }
    int maxLL, maxLR, maxRL, maxRR;
    int maxDistLeft = 0;
    int maxDistRight = 0;
    
    if (root->left != NULL) {
        maxDistLeft = GetMaxDistance(root->left, maxLL, maxLR);
        maxLeft = std::max(maxLL, maxLR) + 1;
    } else {
        maxDistLeft = 0;
        maxLeft = 0;
    }
    
    if (root->right != NULL) {
        maxDistRight = GetMaxDistance(root->right, maxRL, maxRR);
        maxRight = std::max(maxRL, maxRR) + 1;
    } else {
        maxDistRight = 0;
        maxRight = 0;
    }
    
    return std::max(maxLeft + maxRight, std::max(maxDistLeft, maxDistRight));
}

 

 3. 二叉樹最大路徑和問題(任意兩個節點之間)

思路:有點相似於 數組最大子段和問題,部分和大於0,表示貢獻值爲0,能夠相加起來

咱們對二叉樹進行 dfs,先算出 左右子樹的和值 leftSum 和 rightSum, 若是 leftSum 或者 rightSum 大於0,則表示此結果對 後續結果是有利的

int maxSum = INT_MIN;
int dfs(BinTreeNode* root) {
    if (root == NULL) return 0;
    int leftSum = dfs(root->left);
    int rightSum = dfs(root->right);
    int sum = root->val;
    if (leftSum > 0)  sum += leftSum;
    if (rightSum > 0) sum += rightSum;
    maxSum = std::max(maxSum, sum);
    if (std::max(leftSum, rightSum) > 0) {
        return std::max(leftSum, rightSum) + root->val;
    } else {
        return root->val;
    }
}

int maxPathSum(BinTreeNode* root) {
    if (root == NULL) return 0;
    dfs(root);
    return maxSum;
}

 

 4. 二叉樹最低公共祖先

思路:先求取 根節點到兩個結點的路徑序列,而後在這兩個路徑中查找最後一個公共結點便可

擴展:如何求 兩個結點之間的距離呢? 能夠先分別求得 根節點到這兩個結點的路徑,而後 最低公共祖先結點到 結點A的距離 + 最低公共祖先結點到 結點B的距離

struct BinTreeNode {
    BinTreeNode* left;
    BinTreeNode* right;
    int          val;
    BinTreeNode(int value) : left(NULL), right(NULL), val(value) { }
};


BinTreeNode* BuildBinTree(BinTreeNode* root, int value) {
    if (root == NULL) {
        root = new BinTreeNode(value);
    } else if (value <= root->val) {
        root->left = BuildBinTree(root->left, value);
    } else {
        root->right = BuildBinTree(root->right, value);
    }
    return root;
}

// 二叉樹中序 遞歸
void Inorder(BinTreeNode* root) {
    if (root == NULL) return;
    Inorder(root->left);
    std::cout << root->val << "-->";
    Inorder(root->right);
}

// 二叉樹中序 非遞歸 棧
void InorderStk(BinTreeNode* root) {
    if (root == NULL) return;
    std::stack<BinTreeNode*> stk;
    while (root != NULL || !stk.empty()) {
        while (root != NULL) {
            stk.push(root);
            root = root->left;
        }
        BinTreeNode* cur = stk.top();
        stk.pop();
        std::cout << cur->val << "-->";
        root = cur->right;
    }
}

// 二叉樹中序 非遞歸 非棧
void InorderMorris(BinTreeNode* root) {
    if (root == NULL) return;
    BinTreeNode* cur = root;
    BinTreeNode* prev = NULL;
    while (cur != NULL) {
        if (cur->left == NULL) {
            std::cout << cur->val << "-->";
            prev = cur;
            cur = cur->right;
        } else {
            BinTreeNode* node = cur->left;
            while (node->right != NULL && node->right != cur) {
                node = node->right;
            }
            if (node->right == NULL) {  
                node->right = cur;
                cur = cur->left;
            } else {
                std::cout << cur->val << "-->";
                prev = cur;
                cur = cur->right;
                node->right = NULL;
            }
        }
    }
}

// 按值查找結點
BinTreeNode* GetNode(BinTreeNode* root, int value) {
    if (root == NULL) return NULL;
    if (root->val == value) {
        return root;
    } else if (value < root->val) {
        return GetNode(root->left, value);
    } else {
        return GetNode(root->right, value);
    }
}

// 獲取從根節點到某節點的路徑
bool GetNodePath(BinTreeNode* root, BinTreeNode* pNode, std::vector<BinTreeNode*>& path) {
    if (root == NULL || pNode == NULL) return false;
    bool found = false;
    if (root == pNode) {
        path.push_back(root);
        found = true;
        return found;
    }
    path.push_back(root);
    found = GetNodePath(root->left, pNode, path);
    if (!found) {  // 左子樹未找到,繼續右子樹找
        found = GetNodePath(root->right, pNode, path);
    }
    if (!found) { // 左右子樹均未找到,此時才pop
 path.pop_back();
    }
    return found;
}

// 兩個結點的最低公共祖先
BinTreeNode* GetLastCommonParent(BinTreeNode* root, BinTreeNode* pNode1, BinTreeNode* pNode2) {
    if (root == NULL || pNode1 == NULL || pNode2 == NULL) {
        return NULL;
    }
    std::vector<BinTreeNode*> path1;  
    std::vector<BinTreeNode*> path2;
    bool bFound1 = GetNodePath(root, pNode1, path1);
    bool bFound2 = GetNodePath(root, pNode2, path2);
    if (bFound1 == false || bFound2 == false) {
        return NULL;
    }
    std::vector<BinTreeNode*>::iterator iter1 = path1.begin();
    std::vector<BinTreeNode*>::iterator iter2 = path2.begin();
    for (; iter1 != path2.end() && iter2 != path2.end(); ++iter1, ++iter2) {
        if (*iter1 != *iter2) {
            break;
        }
    }
    --iter1;
    return *iter1;
}
相關文章
相關標籤/搜索