我要好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 << "\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. 先序序列和中序序列构建一颗二叉树 中序序列和后序序列构建一棵二叉树
注:为什么先序和后序不能确定一棵二叉树?(例如:先序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; }
四. 二叉树判断
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)); }
思路:有点类似于 数组最大子段和问题,部分和大于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; }