抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结常见的二叉树问题~
参考链接:leetcode 剑指offer cyc的github Annie Kim的博客
一些概念
- 完全二叉树:堆就是完全二叉树
- 二叉搜索树:右孩子<=根<左孩子
- 平衡二叉树: (左子树高度-右子树高度)<=1
- dfs=先/中/后序遍历
bfs=层次遍历
快速排序相当于前序遍历
归并排序相当于后序遍历
一些经验
- 一般都有递归、迭代的方法
- 迭代的方法中:dfs(比如求路径和)用栈,bfs(比如求树高)用队列
各种遍历
层次遍历
- 非递归
void traverse(TreeNode* root) {
if (!root) return;
queue myQueue;
myQueue.push(root);
while (!myQueue.empty()) {
TreeNode* cur = myQueue.front();
visit(cur);
myQueue.pop();
if (cur->left) myQueue.push(cur->left);
if (cur->right) myQueue.push(cur->right);
}
}
- 递归
void traverse(TreeNode* root) {
if (!root) return;
int depth = getDepth(root);
for (int i = 1; i <= depth; ++i) visitAtDepth(root, i);
}
int getDepth(TreeNode* root) {
if (!root) return 0;
return max(getDepth(root->left), getDepth(root->right)) + 1;
}
void visitAtDepth(TreeNode* root, int depth) {
if (!root || depth < 1) return;
if (depth == 1) {
visit(root);
return;
}
visitAtDepth(root->left, depth - 1);
visitAtDepth(root->right, depth - 1);
}
中序遍历
- 非递归
// 写法一
void traverse(TreeNode* root) {
if (root == nullptr) return;
stack myStack;
while (!myStack.empty() || root != nullptr) {
if (root) {
myStack.push(root);
root = root->left;
} else {
root = myStack.top();
myStack.pop();
visit(root);
root = root->right;
}
}
}
// 写法二
void traverse(TreeNode* root) {
if (root == nullptr) return;
stack myStack;
while (!myStack.empty() || root != nullptr) {
while (root != nullptr) {
myStack.push(root);
root = root->left;
}
root = myStack.top();
visit(root);
myStack.pop();
root = root->right;
}
}
- 递归
void traverse(TreeNode* root) {
if (!root) return;
traverse(root->left);
visit(root);
traverse(root->right);
}
- morris遍历
void inorderMorrisTraversal(TreeNode *root) {
TreeNode *cur = root, *prev = NULL;
while (cur != NULL) {
if (cur->left == NULL) {
printf("%d ", cur->val);
cur = cur->right;
}
else {
// find predecessor
prev = cur->left;
while (prev->right != NULL && prev->right != cur)
prev = prev->right;
if (prev->right == NULL) {
prev->right = cur;
cur = cur->left;
} else {
prev->right = NULL;
printf("%d ", cur->val);
cur = cur->right;
}
}
}
}
前序遍历
- 非递归
// 写法一
void traverse(TreeNode* root) {
if (root == nullptr) return;
stack myStack;
while (!myStack.empty || root != nullptr) {
while (root != nullptr) { // 不断把左孩子推入栈
visit(root);
myStack.push(root);
root = root->left;
}
root = myStack.top();
myStack.pop();
root = root->right; // 前序遍历右子树
}
}
// 写法二
void traverse(TreeNode* root) {
if (root == nullptr) return;
stack myStack;
while (!myStack.empty() || root != nullptr) {
if (root) {
myStack.push(root);
visit(root);
root = root->left;
} else {
root = myStack.top();
myStack.pop();
root = root->right;
}
}
}
- 递归
void traverse(TreeNode* root) {
if (!root) return;
visit(root);
traverse(root->left);
traverse(root->right);
}
- morris遍历
void preorderMorrisTraversal(TreeNode *root) {
TreeNode *cur = root, *prev = NULL;
while (cur != NULL) {
if (cur->left == NULL) {
printf("%d ", cur->val);
cur = cur->right;
} else {
prev = cur->left;
while (prev->right != NULL && prev->right != cur)
prev = prev->right;
if (prev->right == NULL) {
printf("%d ", cur->val); // the only difference with inorder-traversal
prev->right = cur;
cur = cur->left;
} else {
prev->right = NULL;
cur = cur->right;
}
}
}
}
后序遍历
- 非递归
void traverse(TreeNode* root) {
stack myStack;
TreeNode* lastvisit = root;
while (!myStack.empty || root != nullptr) {
if (root != nullptr) {
myStack.push(root);
root = root->left;
} else {
root = myStack.top();
if (root->right == nullptr || root->right == lastvisit) {
visit(root);
myStack.pop();
lastvisit = root;
root = nullptr;
} else {
root = root->right;
}
}
}
}
- 递归
void traverse(TreeNode* root) {
if (!root) return;
traverse(root->left);
traverse(root->right);
visit(root);
}
- morris遍历
void reverse(TreeNode *from, TreeNode *to) { // reverse the tree nodes 'from' -> 'to'.
if (from == to)
return;
TreeNode *x = from, *y = from->right, *z;
while (true) {
z = y->right;
y->right = x;
x = y;
y = z;
if (x == to)
break;
}
}
void printReverse(TreeNode* from, TreeNode *to) { // print the reversed tree nodes 'from' -> 'to'.
reverse(from, to);
TreeNode *p = to;
while (true) {
printf("%d ", p->val);
if (p == from)
break;
p = p->right;
}
reverse(to, from);
}
void postorderMorrisTraversal(TreeNode *root) {
TreeNode dump(0);
dump.left = root;
TreeNode *cur = &dump, *prev = NULL;
while (cur) {
if (cur->left == NULL) {
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 {
printReverse(cur->left, prev); // call print
prev->right = NULL;
cur = cur->right;
}
}
}
}
一些题目
求二叉树的直径(lc543)
- 思路
- 包含根的最大直径=左子树的高+右子树的高,不包含根的最大直径=max(左子树的直径,右子树的直径)
- 所以,直径=max(左子树的直径,右子树的直径,左子树的高+右子树的高)
前序+中序->二叉树(lc105)
- 思路
从前序中找当前树的根节点,中序中根节点的左(右)边就是左(右)子树的节点
class Solution {
public:
TreeNode *buildTree(vector &preorder, vector &inorder) {
return buildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
TreeNode *buildTree(vector &preorder, int pLeft, int pRight, vector &inorder, int iLeft, int iRight) {
if (pLeft > pRight || iLeft > iRight) return nullptr;
int i = 0;
for (i = iLeft; i <= iRight; ++i) {
if (preorder[pLeft] == inorder[i]) break;
}
int leftTreeSize = i - iLeft;
TreeNode *cur = new TreeNode(preorder[pLeft]);
cur->left = buildTree(preorder, pLeft + 1, pLeft + leftTreeSize, inorder, iLeft, i - 1);
cur->right = buildTree(preorder, pLeft + leftTreeSize + 1, pRight, inorder, i + 1, iRight);
return cur;
}
};
前序+中序->后序
class Solution {
public:
void buildTree(vector &preorder, vector &inorder, vector& postorder) {
buildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, postorder);
}
void buildTree(vector &preorder, int pLeft, int pRight, vector &inorder, int iLeft, int iRight, vector& postorder) {
if (pLeft > pRight || iLeft > iRight) return NULL;
int i = 0;
for (i = iLeft; i <= iRight; ++i) {
if (preorder[pLeft] == inorder[i]) break;
}
int leftTreeSize = i - iLeft;
buildTree(preorder, pLeft + 1, pLeft + leftTreeSize, inorder, iLeft, i - 1);
buildTree(preorder, pLeft + leftTreeSize + 1, pRight, inorder, i + 1, iRight);
postorder.push_back(inorder[i]);
}
};
判断二叉树是否对称(lc101)
- 法一:递归
class Solution {
public:
bool isSameRoot(TreeNode* root1, TreeNode* root2) {
if (!root1 && !root2) return true;
if (!root1 || !root2) return false;
if (root1->val != root2->val) return false;
return isSameRoot(root1->left, root2->right) && isSameRoot(root1->right, root2->left);
}
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return isSameRoot(root->left, root->right);
}
};
- 法二:迭代,队列1装左子树层序遍历的结果,队列2装右子树反向层次遍历的结果
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
if (!root->left && !root->right) return true;
if (!root->left || !root->right) return false;
queue q1, q2;
q1.push(root->left);
q2.push(root->right);
while (!q1.empty()) {
TreeNode* cleft = q1.front(); q1.pop();
TreeNode* cright = q2.front(); q2.pop();
if (cleft->val != cright->val) return false;
if (!cleft->left && cright->right || cleft->left && !cright->right) return false;
if (!cleft->right && cright->left || cleft->right && !cright->left) return false;
if (cleft->left) {
q1.push(cleft->left);
q2.push(cright->right);
}
if (cleft->right) {
q1.push(cleft->right);
q2.push(cright->left);
}
}
return true;
}
};
判断二叉树是否有从根到节点的路径,和为target(lc112)
- 法一:递归
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
if (!root->left && !root->right) return root->val == sum;
int flag = false;
if (root->left) {
flag = flag || hasPathSum(root->left, sum - root->val);
}
if (root->right) {
flag = flag || hasPathSum(root->right, sum - root->val);
}
return flag;
}
- 法二:迭代,把根节点的值累加到孩子节点上
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
stack s;
s.push(root);
while (!s.empty()) {
TreeNode* cur = s.top();
s.pop();
if (!cur->left && !cur->right && cur->val == sum) return true;
if (cur->left) {
cur->left->val += cur->val;
s.push(cur->left);
}
if (cur->right) {
cur->right->val += cur->val;
s.push(cur->right);
}
}
return false;
}
二叉树中和为target的路径(jz24)
class Solution {
public:
void helper(TreeNode* root, int sum, vector& cur, vector>& result) {
if (!root) return;
cur.push_back(root->val);
if (root->left) helper(root->left, sum - root->val, cur, result);
if (root->right) helper(root->right, sum - root->val, cur, result);
if (!root->left && !root->right && sum == root->val) result.push_back(cur);
cur.pop_back();
}
vector> pathSum(TreeNode* root, int sum) {
vector> result;
vector cur;
helper(root, sum, cur, result);
return result;
}
};
二叉搜索树的最近公共祖先(lc235)
- 思路:如果根节点的值大于p和q之间的较大值,说明p和q都在左子树中,那么此时我们就进入根节点的左子节点继续递归,如果根节点小于p和q之间的较小值,说明p和q都在右子树中,那么此时我们就进入根节点的右子节点继续递归,如果都不是,则说明当前根节点就是最小共同父节点
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root) return nullptr;
while (root) {
if (root->val > max(p->val, q->val)) root = root->left;
else if (root->val < min(p->val, q->val)) root = root->right;
else return root;
}
return nullptr;
}
二叉树的最近公共祖先(lc236)
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root || p == root || q == root) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
return left ? left : right;
}
判断是否为二叉搜索树(lc98)
- 注意:在这道题中定义是左子树值<根<右子树
- 法一:中序遍历该数到数组中,判断左树是否都小于根、右树是否都大于根、左右树是否都是二叉搜索树
- 法二:判断左子树的最右节点是否大于根、右子树的最左节点是否小于根、左右树是否都是二叉搜索树
bool isValidBST(TreeNode* root) {
if (!root) return true;
TreeNode* rleft = root->left;
while (rleft && rleft->right) {
rleft = rleft->right;
}
if (rleft && rleft->val >= root->val) return false;
TreeNode* rright = root->right;
while (rright && rright->left) {
rright = rright->left;
}
if (rright && rright->val <= root->val) return false;
return isValidBST(root->left) && isValidBST(root->right);
}
判断一棵树是否另一棵树的子树(jz17)
class Solution {
public:
bool SubtreeWithRoot(TreeNode* pRoot1, TreeNode* pRoot2) {
if (!pRoot2) return true;
if (!pRoot1) return false;
if (pRoot1->val != pRoot2->val) return false;
return SubtreeWithRoot(pRoot1->left, pRoot2->left) && SubtreeWithRoot(pRoot1->right, pRoot2->right);
}
bool HasSubtree(TreeNode* pRoot1, TreeNode* pRoot2) {
if (!pRoot2) return false;
if (!pRoot1) return false;
return HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2) || SubtreeWithRoot(pRoot1, pRoot2);
}
};
把二叉树变为其镜像(jz18)
- 法一:迭代
void Mirror(TreeNode *pRoot) {
if (!pRoot) return;
queue q;
q.push(pRoot);
while (!q.empty()) {
TreeNode* cur = q.front();
TreeNode* tmp = cur->left;
cur->left = cur->right;
cur->right = tmp;
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
q.pop();
}
}
- 法二:递归
void Mirror(TreeNode *pRoot) {
if (!pRoot) return;
Mirror(pRoot->left);
Mirror(pRoot->right);
TreeNode* tmp = pRoot->left;
pRoot->left = pRoot->right;
pRoot->right = tmp;
}
判断数组是否为二叉搜索树后序遍历结果(jz23)
bool VerifySquenceOfBSTHelp(vector& sequence, int left, int right) {
if (left >= right) return true;
int root = sequence[right];
int index = left;
while (index < right && sequence[index] <= root) ++index;
for (int i = index; i < right; ++i) {
if (sequence[i] < root) return false;
}
return VerifySquenceOfBSTHelp(sequence, left, index - 1) && VerifySquenceOfBSTHelp(sequence, index, right - 1);
}
bool VerifySquenceOfBST(vector sequence) {
if (!sequence.size()) return false;
int n = sequence.size();
return VerifySquenceOfBSTHelp(sequence, 0, n - 1);
}
二叉树的深度(jz38)
- 法一:递归,max(左子树深度,右子树深度)+1
- 法二:迭代,层次遍历算有多少层
int TreeDepth(TreeNode* pRoot) {
if (!pRoot) return 0;
queue myQueue;
myQueue.push(pRoot);
int cnt = 0;
while (!myQueue.empty()) {
int width = myQueue.size();
++cnt;
while (width--) {
TreeNode* cur = myQueue.front();
myQueue.pop();
if (cur->left) myQueue.push(cur->left);
if (cur->right) myQueue.push(cur->right);
}
}
return cnt;
}
二叉树的最小深度(lc111)
- 法一:递归(注意:考虑到[1,2]的情况,不能直接 return 1 + min(minDepth(root->left), minDepth(root->right)))
int minDepth(TreeNode* root) {
if (!root) return 0;
if (!root->left) return 1 + minDepth(root->right);
if (!root->right) return 1 + minDepth(root->left);
return 1 + min(minDepth(root->left), minDepth(root->right));
}
- 法二:迭代
int minDepth(TreeNode* root) {
if (!root) return 0;
queue q;
q.push(root);
int res = 0;
while (!q.empty()) {
int width = q.size();
++res;
while (width--) {
TreeNode* cur = q.front();
q.pop();
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
if (!cur->left && !cur->right) return res;
}
}
return res;
}
判断是否为平衡二叉树(jz39)
- 思路:递归,abs(左子树深度-右子树深度) <= 1 && 左右子树均是平衡二叉树
二叉树最大路径和(lc124)
- 思路:对于每个结点来说,要知道经过其左子结点的 path 之和大还是经过右子节点的 path 之和大。递归函数返回值就可以定义为以当前结点为根结点,到叶节点的最大路径之和,然后全局路径最大值放在参数中,用结果 res 来表示
class Solution {
public:
int maxPathSum(TreeNode* root) {
int res = INT_MIN;
helper(root, res);
return res;
}
int helper(TreeNode* node, int& res) { // 返回以node为根节点到叶节点的最大路径和
if (!node) return 0;
int left = max(helper(node->left, res), 0);
int right = max(helper(node->right, res), 0);
res = max(res, left + right + node->val);
return max(left, right) + node->val;
}
};
二叉树展开为链表(lc114)
- 法一:递归
void flatten(TreeNode* root) {
if (!root) return;
if (root->left) flatten(root->left);
if (root->right) flatten(root->right);
// 把根节点的左孩子插到根和右孩子之间
TreeNode* tmp = root->right;
root->right = root->left;
root->left = nullptr; // 不要忘了这一句!
while (root->right) root = root->right;
root->right = tmp;
}
- 法二:迭代
void flatten(TreeNode *root) {
TreeNode *cur = root;
while (cur) {
if (cur->left) {
TreeNode *p = cur->left;
while (p->right) p = p->right;
p->right = cur->right;
cur->right = cur->left;
cur->left = NULL;
}
cur = cur->right;
}
}
有序数组转平衡二叉树(lc108)
- 思路:递归,每次取中点作为root,再分别构建左子树和右子树
class Solution {
public:
TreeNode* sortedArrayToBST(vector& nums, int left, int right) {
if (right < 0 || right >= nums.size() || left > right) return nullptr;
int mid = left + (right - left) / 2;
ListNode* root = new ListNode(nums[mid]);
ListNode* leftRoot = sortedArrayToBST(nums, left, mid - 1);
ListNode* rightRoot = sortedArrayToBST(nums, mid + 1, right);
root->left = leftRoot;
root->right = rightRoot;
return root;
}
TreeNode* sortedArrayToBST(vector& nums) {
if (!nums.size()) return nullptr;
TreeNode* root = sortedArrayToBST(nums, 0, nums.size() - 1);
return root;
}
};
返回所有和为target的路径(lc113)
void findPath(TreeNode* root, int sum, vector>& result, vector& cur) {
if (!root) return;
cur.push_back(root->val);
if (!root->left && !root->right) {
if (root->val == sum) result.push_back(cur);
}
if (root->left) findPath(root->left, sum - root->val, result, cur);
if (root->right) findPath(root->right, sum - root->val, result, cur);
cur.pop_back();
}
vector> pathSum(TreeNode* root, int sum) {
vector> result;
vector cur;
findPath(root, sum, result, cur);
return result;
}