二叉树基础(二)

关于二叉树的特性很多,这里只是做了一个很简单的总结。二叉树的基本操作主要可以分为以下几类:

1、前序遍历、中序遍历、后序遍历

关于二叉树的前序遍历、中序遍历、后序遍历参看上一篇二叉树基础。

具体的求解方式主要通过递归的方式求解该问题。递归的思想在求解很多问题上都是非常实用的。这里给出一个中序遍历的例子:
在这里插入图片描述
对于上述这个二叉树,我们可以通过下面的代码进行递归。

    void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        vec.push_back(cur->val);    // 中
        traversal(cur->right, vec); // 右
    }
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root, result);
        return result;
    }

算法的入口inorderTraversal输入一棵树。实际的遍历在traversal中实现。算法首先向左子树重新执行traversal函数:

traversal(cur->left, vec);

此时左子树不存在。所以算法运行:

vec.push_back(cur->val);

将值1放入容器中。然后在递归查找右子树:

traversal(cur->right, vec);

在右子树中重新执行traversal递归函数。此时同样先遍历右子树下的左子树,得到值3

然后插入右子树自身的值2。

最后得到二叉树的中序遍历值:132

2、相同的树

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

对于如何判断两棵树是否相同同样也是可以使用递归的思想。两棵树以相同的方式遍历每一个节点。并判断值是否相同。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (p == NULL && q == NULL) {
            return true;
        } else if ((p != NULL && q == NULL) || (p == NULL && q != NULL)) {
            return false;
        } else if (p->val != q->val) {
            return false;
        } else {
            return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
        }
    }
};

注意这里还存在一种可能在于可能一棵树中的节点存在而另一颗树中的节点不存在。所以需要有所判断。另外判断是否到最后位置也就是叶节点的返回。如果两棵树都检查到了位置往下不存在新的节点的话,此时应该返回true。

但是这里跟前面的顺序遍历有所不同的是在于最后一行调用递归的时候:

return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);

也就是说两棵树既要左子树相同同时还要右子树相同才是相同的数

3、对称二叉树

给定一个二叉树,检查它是否是镜像对称的。也即是说这棵树关于最上层根节点中心对称。

例如:这是对称的:

    1
   / \
  2   2
 / \ / \
3  4 4  3

这是不对称的:

    1
   / \
  2   2
   \   \
   3    3

这是我写的错误代码

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root->left==NULL && root->right==NULL)
            return true;
        else if((root->left != NULL && root->right==NULL) || (root->left == NULL && root->right !=NULL))
            return false;
        else 
            return (isSymmetric(root->left) && isSymmetric(root->right));
    }
};

这里只考虑了左右是否存在但是缺少了同样位置的值的判断。
正确代码:

bool check(TreeNode *p, TreeNode *q) {
        if (!p && !q) return true;
        if (!p || !q) return false;
        return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
    }
    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }

这题其实用的还是相同的树的思想,只是把左右对换了而已。思路是:先复制一棵树,这样我就得到了两棵相同的树,然后我们将两棵树对称遍历:遍历第一棵树的左子树将其与第二棵树的右子树相比较是否相同,然后遍历第一棵树的右子树将其与第二棵树的左子树相比较是否相同。

4、二叉树的最大深度

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

示例:
给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == NULL)  
            return 0;
        return max(maxDepth(root->left),maxDepth(root->right))+1;
    }
};

这题只有三行代码,确实精辟。

与二叉树的最大深度对应的是求二叉树的最小深度:

这是我写的错误代码:

class Solution {
public:
    int minDepth(TreeNode* root) {
        if(root==NULL)
            return 0;
        return min(minDepth(root->left),minDepth(root->right))+1;
    }
};

但是输入为:

[2,null,3,null,4,null,5,null,6]

时输出为1,正确结果为5。

下面是正确解答代码:

class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) {
            return 0;
        }
        if (root->left == nullptr && root->right == nullptr) {
            return 1;
        }
        int min_depth = INT_MAX;
        if (root->left != nullptr) {
            min_depth = min(minDepth(root->left), min_depth);
        }
        if (root->right != nullptr) {
            min_depth = min(minDepth(root->right), min_depth);
        }
        return min_depth + 1;
    }

};

5、二叉搜索树

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与删除操作的特点,又有数组快速查找的优势;所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。

题目:将有序数组转换为二叉搜索树

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return helper(nums, 0, nums.size() - 1);
    }
    TreeNode* helper(vector<int>& nums, int left, int right) {
        if (left > right) {
            return nullptr;
        }
        // 总是选择中间位置左边的数字作为根节点
        int mid = (left + right) / 2;
        TreeNode* root = new TreeNode(nums[mid]);
        root->left = helper(nums, left, mid - 1);
        root->right = helper(nums, mid + 1, right);
        return root;
    }
};

6、平衡二叉树

一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

class Solution {
public:
    public:
    int height(TreeNode* root) {
        if (root == NULL) 
            return 0; 
        return max(height(root->left), height(root->right)) + 1; 
    }
    bool isBalanced(TreeNode* root) {
        if (root == NULL) 
            return true;
        return abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
    }
};

7、路径总和

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

核心思想是对树进行一次遍历,在遍历时记录从根节点到当前节点的路径和,以防止重复计算。

class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if(root==NULL)
            return false;
        if(root->left==NULL && root->right==NULL)
            return targetSum == root->val;
        return hasPathSum(root->left,targetSum-root->val) || hasPathSum(root->right,targetSum-root->val);
    }
};

8、翻转二叉树

给你一棵二叉树,输出它翻转后的树
示例:

输入:

     4
   /   \
  2     7
 / \   / \
1   3 6   9

输出:

     4
   /   \
  7     2
 / \   / \
9   6 3   1

这题用递归求解的话就比较简单了,把左右树对换即可:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==NULL)
            return root;
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};

8、二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。这道题目其实挺简单的,虽然一开始确实没做出来。注意到这里提到的二叉搜索树,二叉搜索树的特点就是 左子树的所有节点都小于当前节点,右子树的所有节点都大于当前节点,并且每棵子树都具有上述特点。因此我们知道作为其公共祖先的值应该介于两个数之间。那么我们可以拿当前节点的值与两个值比较,如果当前值比两个节点都小,说明公共祖先在右子树,反之在左子树。递归直到找到一个值在它们之间便是正确解。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode* trr=root;
        while(true)
        {
            if(trr->val<p->val &&trr->val<q->val)
            {
                trr=trr->right;
            }
            else if(trr->val>p->val && trr->val>q->val)
            {
                trr=trr->left;
            }
            else
                break;
        }
        return trr;
    }
};

9、二叉树的所有路径

给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
在这里插入图片描述
最直观的方法是使用深度优先搜索。在深度优先搜索遍历二叉树时,我们需要考虑当前的节点以及它的孩子节点。

如果当前节点不是叶子节点,则在当前的路径末尾添加该节点,并继续递归遍历该节点的每一个孩子节点。
如果当前节点是叶子节点,则在当前路径末尾添加该节点后我们就得到了一条从根节点到叶子节点的路径,将该路径加入到答案即可。

class Solution {
public:
    //void trrenode(TreeNode* root, string temp, vector<string>& vec)
    void trrenode(TreeNode* root, string temp, vector<string>& vec)
    {
        if(root!=nullptr)
        {
            temp+=to_string(root->val);
            //temp += to_string(root->val);
            if(root->left == nullptr && root->right == nullptr)
            {
                vec.push_back(temp);
            }
            else
            {
                temp.append("->");
                //temp += "->";
                trrenode(root->left,temp,vec);
                trrenode(root->right,temp,vec);
            }
        }
        
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> vec;
        //string temp=root->val;
        trrenode(root,"",vec);
        return vec;
    }
};

参考:

https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/

https://leetcode-cn.com/problems/symmetric-tree/

https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/

https://leetcode-cn.com/problems/path-sum/

https://leetcode-cn.com/problems/binary-tree-paths/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶执念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值