zoukankan      html  css  js  c++  java
  • 我要好offer之 二叉树大总结

    一. 二叉树定义

    二叉树具有天然的递归特性,凡是二叉树相关题,首先应该联想到递归

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

    二. 二叉树遍历

    详见笔者博文:二叉树遍历大总结

    #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 << "	 PreOrder:" ; 
        PreOrder(root);
        std::cout << std::endl;
        std::cout << "	 PreOrderStk:"; 
        PreOrderStk(root); 
        std::cout << std::endl;
        std::cout << "	 PreOrderMorris:"; 
        PreOrderMorris(root); 
        std::cout << std::endl;
        
        std::cout << "2:InOrder:" << std::endl;
        std::cout << "	 InOrder:";
        InOrder(root); 
        std::cout << std::endl;
        std::cout << "	 InOrderStk:";
        InOrderStk(root); 
        std::cout << std::endl;
        std::cout << "	 InOrderMorris:";
        InOrderMorris(root);
        std::cout << std::endl;
        
        std::cout << "3:PostOrder:" << std::endl;
        std::cout << "	 PostOrder:";
        PostOrder(root);
        std::cout << std::endl;
        std::cout << "	 PostOrderStk:";
        PostOrderStk(root);
        std::cout << std::endl;
    
        std::cout << "4:LevelOrder:" << std::endl;
        std::cout << "	 LevelOrder:";
        LevelOrderQueue(root); 
        std::cout << std::endl;
        return 0;
    }

    三. 二叉树构建

    1. 先序序列和中序序列构建一颗二叉树  中序序列和后序序列构建一棵二叉树

    注:为什么先序和后序不能确定一棵二叉树?(例如:先序ABCD,后序CBDA)

    给定先序遍历序列和后序遍历序列,求有多少种不同形态的二叉树?

    考虑简单的两个节点A和B,前序遍历为AB,后序遍历为BA,则会存在两种二叉树: A->left=B 和 A->right=B

    存在不同二叉树的原因在于某节点缺少其中一个子树,在遍历序列中呈现出前序和后序遍历中节点的值相邻但顺序相反(例如先序AB,后序BA),那么总的二叉树数量番倍

    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

    四. 二叉树判断

     1. 判断二叉查找树BST

     2. 判断平衡二叉树

     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;
    }
  • 相关阅读:
    ubuntu操作技巧
    电机
    哈佛结构与冯若依曼结构
    linux command ------ ls
    Frames of Reference参考框架
    What Are Tango Poses?Tango姿态是什么?
    Google Tango Java SDK开发:Motion Tracking 运动追踪
    Google Tango Java SDK开发:Configure and Connect 配置和连接
    gradle
    Google Tango service outdated谷歌Tango的服务过时了
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3891037.html
Copyright © 2011-2022 走看看