二叉树


1、求根节点到叶子节点数字之和

129. 求根节点到叶节点数字之和 - 力扣(LeetCode)
image.png
image.png


我的解法:使用中序遍历,为什么使用中序遍历?
第一、直观印象,回溯算法我惯用中序遍历
第二、对于本题,我没有记录访问过的路径 vector<int> path,我是在叶子节点处对数值进行累加的,所以必须要用中序遍历的方式。

废话不多说,C++完整代码如下:

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. int sum = 0; // 结果
  15. void traversal(TreeNode* root, int curSum){
  16. // 中
  17. if(root->left == nullptr && root->right == nullptr){
  18. sum += curSum;
  19. return;
  20. }
  21. // 左
  22. if(root->left) traversal(root->left, (curSum * 10 + root->left->val)); // 回溯,藏在了参数里
  23. // 右
  24. if(root->right) traversal(root->right, curSum * 10 + root->right->val); // 回溯,藏在了参数里
  25. return;
  26. }
  27. int sumNumbers(TreeNode* root) {
  28. traversal(root, root->val);
  29. return sum;
  30. }
  31. };

上面的代码,需要对回溯有一定的理解,看不懂的话可以看下面的代码:

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. int result;
  15. vector<int> path;
  16. // 自定义函数,将vector转成int
  17. int vectorToInt(const vector<int>& path){
  18. int sum = 0;
  19. for(int i = 0; i < path.size(); i++){
  20. sum = sum * 10 + path[i];
  21. }
  22. return sum;
  23. }
  24. // 自定义函数,实现对二叉树的回溯
  25. void backtracking(TreeNode* root){
  26. // 中
  27. if(!root->left && !root->right){
  28. result += vectorToInt(path);
  29. return;
  30. }
  31. // 左
  32. if(root->left){
  33. path.push_back(root->left->val); // 处理节点
  34. backtracking(root->left); // 递归
  35. path.pop_back(); // 回溯,撤销上一步的操作
  36. }
  37. // 右
  38. if(root->right){
  39. path.push_back(root->right->val); // 处理节点
  40. backtracking(root->right); // 递归
  41. path.pop_back(); // 回溯,撤销上一步的操作
  42. }
  43. return;
  44. }
  45. int sumNumbers(TreeNode* root) {
  46. if(root == nullptr) return 0;
  47. path.clear();
  48. result = 0;
  49. path.push_back(root->val);
  50. backtracking(root);
  51. return result;
  52. }
  53. };

Java代码:

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. int result = 0;
  18. public void traversal(TreeNode root, int curSum){
  19. // 中,处理节点
  20. if(root.left == null && root.right == null){
  21. result += curSum;
  22. return;
  23. }
  24. // 左(空节点不递归)
  25. if(root.left != null) traversal(root.left, curSum * 10 + root.left.val);
  26. // 右(空姐点不递归)
  27. if(root.right != null) traversal(root.right, curSum * 10 + root.right.val);
  28. return;
  29. }
  30. public int sumNumbers(TreeNode root) {
  31. traversal(root, root.val);
  32. return result;
  33. }
  34. }

2、路径总和 II

113. 路径总和 II - 力扣(LeetCode)
image.png
image.png


  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. vector<vector<int>> result;
  15. vector<int> path;
  16. void backtracking(TreeNode* root, int targetSum){
  17. if(!root->left && !root->right){
  18. if(getPathSum(path) == targetSum){
  19. result.push_back(path);
  20. return;
  21. }
  22. }
  23. if(root->left){
  24. path.push_back(root->left->val);
  25. backtracking(root->left, targetSum);
  26. path.pop_back();
  27. }
  28. if(root->right){
  29. path.push_back(root->right->val);
  30. backtracking(root->right, targetSum);
  31. path.pop_back();
  32. }
  33. return;
  34. }
  35. int getPathSum(vector<int>& path){
  36. int sum = 0;
  37. for(int i = 0; i < path.size(); i++){
  38. sum += path[i];
  39. }
  40. return sum;
  41. }
  42. vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
  43. result.clear();
  44. path.clear();
  45. if(root == nullptr) return result;
  46. path.push_back(root->val);
  47. backtracking(root, targetSum);
  48. return result;
  49. }
  50. };

Java代码

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. List<List<Integer>> result = new ArrayList<>();
  18. List<Integer> path = new ArrayList<>();
  19. // 自定义函数,回溯二叉树
  20. public void backtracking(TreeNode root, int targetSum){
  21. if(root.left == null && root.right == null){
  22. if(getPathSum(path) == targetSum){
  23. result.add(new ArrayList<>(path));
  24. }
  25. return;
  26. }
  27. if(root.left != null){
  28. path.add(root.left.val); // 添加元素
  29. backtracking(root.left, targetSum); // 递归
  30. path.remove(path.size() - 1); // 回溯
  31. }
  32. if(root.right != null){
  33. path.add(root.right.val);
  34. backtracking(root.right, targetSum);
  35. path.remove(path.size() - 1);
  36. }
  37. }
  38. // 自定义函数,对path求和
  39. public int getPathSum(List<Integer> path){
  40. int sum = 0;
  41. for(int i = 0; i < path.size(); i++){
  42. sum += path.get(i);
  43. }
  44. return sum;
  45. }
  46. public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
  47. // result.removeAll(result);
  48. // path.removeAll(path);
  49. if(root == null) return result;
  50. path.add(root.val);
  51. backtracking(root, targetSum);
  52. return result;
  53. }
  54. }

3、路径总和

112. 路径总和 - 力扣(LeetCode)
image.png
image.png


  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. bool backtracking(TreeNode* root, int count){
  15. if(!root->left && !root->right && count == 0) return true; // 遇到叶子节点,并且计数为0
  16. if(!root->left && !root->right) return false; // 遇到叶子节点直接返回
  17. if(root->left){
  18. if(backtracking(root->left, count - root->left->val)){
  19. return true;
  20. }
  21. }
  22. if(root->right){
  23. if(backtracking(root->right, count - root->right->val)){
  24. return true;
  25. }
  26. }
  27. return false;
  28. }
  29. bool hasPathSum(TreeNode* root, int targetSum) {
  30. if(root == nullptr) return false;
  31. return backtracking(root, targetSum - root->val);
  32. }
  33. };

Java代码

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. boolean flag = false;
  18. public void backtracking(TreeNode root, int targetSum){
  19. if(root.left == null && root.right == null){
  20. if(targetSum == 0){
  21. flag = true;
  22. }
  23. }
  24. if(root.left != null){
  25. backtracking(root.left, targetSum - root.left.val); // 回溯
  26. }
  27. if(root.right != null){
  28. backtracking(root.right, targetSum - root.right.val);
  29. }
  30. }
  31. public boolean hasPathSum(TreeNode root, int targetSum) {
  32. if(root == null) return false;
  33. backtracking(root, targetSum - root.val);
  34. return flag;
  35. }
  36. }

4、验证二叉搜索树

98. 验证二叉搜索树 - 力扣(LeetCode)
image.png
image.png


递归法

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. vector<int> vec;
  15. void backtracking(TreeNode* root){
  16. if(root == nullptr){
  17. return;
  18. }
  19. backtracking(root->left);
  20. vec.push_back(root->val);
  21. backtracking(root->right);
  22. }
  23. bool isValidBST(TreeNode* root) {
  24. backtracking(root);
  25. for(int i = 0; i < vec.size() - 1; i++){
  26. if(vec[i] >= vec[i + 1]) return false;
  27. }
  28. return true;
  29. }
  30. };

迭代法
方法一:
这道题可以使用 long long maxVal = LONG_MIN; 来记录上一次出现的最大的值,如果当前节点的值小于这个 maxVal,说明这不是一颗二叉搜索树。
代码如下:

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. bool isValidBST(TreeNode* root) {
  15. stack<TreeNode*> st;
  16. st.push(root);
  17. long long maxVal = LONG_MIN;
  18. while(!st.empty()){
  19. TreeNode* cur = st.top();
  20. if(cur != nullptr){
  21. st.pop();
  22. if(cur->right) st.push(cur->right); // 右
  23. st.push(cur); // 中
  24. st.push(nullptr);
  25. if(cur->left) st.push(cur->left); // 左
  26. }
  27. else{
  28. st.pop(); // 弹出标记的节点 nullptr
  29. if(maxVal >= st.top()->val) return false;
  30. maxVal = maxVal > st.top()->val ? maxVal : st.top()->val; // 更新maxVal的值
  31. st.pop(); // 弹出当前节点
  32. }
  33. }
  34. return true;
  35. }
  36. };

但是这种解法仍然存在不足,如果二叉树节点的数值最大比 long long 类型还大呢?这时候就溢出啦。

方法二:
使用pre 指针,指向上一次访问的节点。

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. bool isValidBST(TreeNode* root) {
  15. stack<TreeNode*> st;
  16. st.push(root);
  17. TreeNode* pre = nullptr;
  18. while(!st.empty()){
  19. TreeNode* cur = st.top();
  20. if(cur != nullptr){
  21. st.pop();
  22. if(cur->right) st.push(cur->right); // 右
  23. st.push(cur); // 中
  24. st.push(nullptr); // 插入标志
  25. if(cur->left) st.push(cur->left); // 左
  26. }
  27. else{
  28. st.pop(); // 弹出标记的节点 nullptr
  29. if(pre != nullptr && pre->val >= st.top()->val){
  30. return false;
  31. }
  32. pre = st.top(); // 记录前一个节点值
  33. st.pop();
  34. }
  35. }
  36. return true;
  37. }
  38. };

Java代码:

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. List<Integer> list = new ArrayList<>();
  18. void traversal(TreeNode root){
  19. if(root == null){
  20. return;
  21. }
  22. traversal(root.left);
  23. list.add(root.val);
  24. traversal(root.right);
  25. }
  26. public boolean isValidBST(TreeNode root) {
  27. list.clear();
  28. traversal(root);
  29. for(int i = 0; i < list.size() - 1; i++){
  30. if(list.get(i) >= list.get(i + 1)) return false;
  31. }
  32. return true;
  33. }
  34. }
  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. public boolean isValidBST(TreeNode root) {
  18. Stack<TreeNode> st = new Stack<>();
  19. st.push(root);
  20. TreeNode pre = null;
  21. while(!st.isEmpty()){
  22. TreeNode cur = st.peek();
  23. if(cur != null){
  24. st.pop();
  25. if(cur.right != null) st.push(cur.right); // 右
  26. st.push(cur); // 中
  27. st.push(null); // 插入标志
  28. if(cur.left != null) st.push(cur.left); // 左
  29. } else{
  30. st.pop();
  31. if(pre != null && pre.val >= st.peek().val){
  32. return false;
  33. }
  34. pre = st.peek();
  35. st.pop();
  36. }
  37. }
  38. return true;
  39. }
  40. }

5、将有序数组转为二叉搜索树

108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
image.png
image.png


题目中说要转换为一棵高度平衡二叉搜索树。这和转换为一棵普通二叉搜索树有什么差别呢?
其实这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取,所以想构成不平衡的二叉树是自找麻烦
二叉树:构造二叉树登场!(opens new window)二叉树:构造一棵最大的二叉树(opens new window)中其实已经讲过了,如果根据数组构造一棵二叉树。
本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间

那么为问题来了,如果数组长度为偶数,中间节点有两个,取哪一个?
取哪一个都可以,只不过构成了不同的平衡二叉搜索树。

C++完整代码如下:

  1. /**
  2. * Definition for a binary tree node.
  3. * struct TreeNode {
  4. * int val;
  5. * TreeNode *left;
  6. * TreeNode *right;
  7. * TreeNode() : val(0), left(nullptr), right(nullptr) {}
  8. * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
  9. * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
  10. * };
  11. */
  12. class Solution {
  13. public:
  14. TreeNode* traversal(vector<int>& nums, int left, int right){
  15. if(left > right){ // 如果区间元素与足以构成节点,即区间为空
  16. return nullptr;
  17. }
  18. int middle = left + ((right - left) >> 1); // BST对应数组的中间节点就是根节点
  19. TreeNode* node = new TreeNode(nums[middle]); // 构建根节点
  20. node->left = traversal(nums, left, middle - 1); // 递归左子树
  21. node->right = traversal(nums, middle + 1, right); // 递归右子树
  22. return node;
  23. }
  24. TreeNode* sortedArrayToBST(vector<int>& nums) {
  25. return traversal(nums, 0, nums.size() - 1);
  26. }
  27. };

理解递归我觉得就可以了,迭代法的话,面试应该不会这么变态同时让写递归和迭代。

Java代码:

  1. /**
  2. * Definition for a binary tree node.
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode() {}
  8. * TreeNode(int val) { this.val = val; }
  9. * TreeNode(int val, TreeNode left, TreeNode right) {
  10. * this.val = val;
  11. * this.left = left;
  12. * this.right = right;
  13. * }
  14. * }
  15. */
  16. class Solution {
  17. public TreeNode buildBST(int[] nums, int left, int right){
  18. if(left > right) return null; // 如果left>right,说明当前区间为null
  19. int middle = left + ((right - left) >> 1);
  20. TreeNode node = new TreeNode(nums[middle]); // new一个节点
  21. node.left = buildBST(nums, left, middle - 1); // 递归左子树
  22. node.right = buildBST(nums, middle + 1, right); // 递归右子树
  23. return node;
  24. }
  25. public TreeNode sortedArrayToBST(int[] nums) {
  26. return buildBST(nums, 0, nums.length - 1);
  27. }
  28. }

6、从中序与后续遍历构建二叉树


106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
image.png
image.png


下面的代码,我不做解释,如果看不懂,请看这里 二刷代码随想录 - 二叉树 - 从中序与后序遍历构建二叉树

2、将二叉搜索树变平衡

1382. 将二叉搜索树变平衡 - 力扣(LeetCode)
image.png
image.png