二叉树oj练习打卡

搜索二叉树的后序遍历

对应letecode链接:

https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/

题目描述:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:

     5
    / \
   2   6
  / \
 1   3
示例 1:

输入: [1,6,3,2,5]
输出: false
示例 2:

输入: [1,3,2,6,5]
输出: true

解题思路:

利用搜索二叉树的性质:

节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

 比如下面这棵二叉树,他的后续遍历是:[3,5,4,10,12,9]

二叉树oj练习打卡_第1张图片

们知道后续遍历的最后一个数字一定是根节点,所以数组中最后一个数字9就是根节点,我们从前往后找到第一个比9大的数字10,那么10后面的[10,12](除了9)都是9的右子节点,10前面的[3,5,4]都是9的左子节点,后面的需要判断一下,如果有小于9的,说明不是二叉搜索树,直接返回false。然后再以递归的方式判断左右子树。

再来看一个,他的后续遍历是[3,5,13,10,12,9]

二叉树oj练习打卡_第2张图片

我们来根据数组拆分,第一个比9大的后面都是9的右子节点[13,10,12]。然后再拆分这个数组,12是根节点,第一个比12大的后面都是12的右子节点[13,10],但我们看到10是比12小的,他不可能是12的右子节点,所以我们能确定这棵树不是二叉搜索树

 对应代码:

class Solution {
public:
    bool verifyPostorder(vector& postorder) {
                 return CheckBST(postorder,0,postorder.size()-1);
    }
    bool CheckBST(vector&postorder,int left,int right){
        if(left>=right)return true;
        //如果left==right,就一个节点不需要判断了,如果left>right说明没有节点,
        //也不用再看了,否则就要继续往下判断
          int mid=left;
          int key=postorder[right];
           //因为数组中最后一个值postorder[right]是根节点,这里从左往右找出第一个比
    //根节点大的值,他后面的都是根节点的右子节点(包含当前值,不包含最后一个值,
    //因为最后一个是根节点),他前面的都是根节点的左子节点
          while(mid<=right&&postorder[mid]

最近公共祖先

对应letecode链接:

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

题目描述:

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

提示:

树中节点数目在范围 [2, 105] 内。
-109 <= Node.val <= 109
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。

解题思路:

    情况一:root为p,q中的一个,这时公共祖先为root
   情况二:p,q分别在root的左右子树上(p在左子树,q在右子树;还是p在右子树,q在左子树的情况都统一放在一起考虑)这时满足p,q的最近公共祖先的结点也只有root本身
    情况三:p和q同时在root的左子树;这时确定最近公共祖先需要遍历子树来进行递归求解。

情况四:p,q同时在root的右子树,这时确定最近公共祖先需要遍历子树进行递归求解

二叉树oj练习打卡_第3张图片  

对应代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                        if(!root||root==q||root==p)return root;//p,q之中有一个是root则root是公共祖先
              TreeNode* pqInLeft=lowestCommonAncestor(root->left,p,q);
                    //去左子树找
                TreeNode*pqInRight=lowestCommonAncestor(root->right,p,q);
                    //去右子树找
                if(pqInLeft==NULL)return pqInRight;
                    //如果左子树没找到q,p则一定在右子树
               if(pqInRight==NULL)return pqInLeft;
                  //如果右子树没找到p,q则一定在左子树上
                        return root;
                        //如果左子树和右子树各找到一个说明root为公共祖先
    }
};

 刚开始的时候我是这么想的:

二叉树oj练习打卡_第4张图片

1.定义一个函数Find先去左子树找一个p,去右子树上找一下q 

2.看p和q是否同时在左边是否同时在右边。如果同时在左边则递归去左子树去找如果在右边则递归去右子树找。如果既不同时在左子树又不同时在右子树那么root就是最近公共祖先。

对应代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                   if(root==p||root==q)return root;

                   bool pInLeft=Find(root->left,p);
                   bool pInRight=!pInLeft;
                   //如果左边找到了p那么右边肯定找不到这是相反的关系
                   bool qInLeft=Find(root->left,q);
                   bool qInRigt=!qInLeft;
                     //如果左边找到了p那么右边肯定找不到这是相反的关系
                   if(pInLeft&&qInLeft)return lowestCommonAncestor(root->left,p,q);
                   //同时在左边则去左边找
                   if(pInRight&&qInRigt)return lowestCommonAncestor(root->right,p,q);
                   //同时在右边则去右边找
                   return root;
                //一个在左边一个在右边root为最近公共祖先
    }
    bool Find(TreeNode*root,TreeNode*node){
               if(!root)return false;//空树表示找不到
               if(root==node)return true;

               bool leftRet=Find(root->left,node);//先去左树找

               if(leftRet)return leftRet;//左树找到了返回不用找了

               bool rightRet=Find(root->right,node);//左树没找到右树找
               if(rightRet)return rightRet;//右树找到了

               return false;//左树右树都没找到

    }
};

非递归:

要想找到两个节点的最近公共祖先节点,我们可以从两个节点往上找,每个节点都往上走,一直走到根节点,那么根节点到这两个节点的连线肯定有相交的地方,如果是从上往下走,那么最后一次相交的节点就是他们的最近公共祖先节点。我们就以找6和7的最近公共节点来画个图看一下

二叉树oj练习打卡_第5张图片

我们看到6和7公共祖先有5和3,但最近的是5。我们只要往上找,找到他们第一个相同的公共祖先节点即可,但怎么找到每个节点的父节点呢,我们只需要把每个节点都遍历一遍,然后顺便记录他们的父节点存储在Map中。我们先找到其中的一条路径,比如6→5→3,然后在另一个节点往上找,由于7不在那条路径上,我们找7的父节点是2,2也不在那条路径上,我们接着往上找,2的父节点是5,5在那条路径上,所以5就是他们的最近公共子节点。

其实这里我们可以优化一下,我们没必要遍历所有的结点,我们一层一层的遍历(也就是BFS),只需要这两个节点都遍历到就可以了,比如上面2和8的公共结点,我们只需要遍历到第3层,把2和8都遍历到就行了,没必要再遍历第4层了。

对应代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                 unordered_mapparent;//记录遍历到的每个节点的父亲
                 queueQ;
                 Q.push(root);
                 //根节点没有父节点,所以为空
                 parent.insert(make_pair(root,nullptr));
                 while(!parent.count(p)||!parent.count(q)){//找到p,q就可以了
                         auto node=Q.front();//队列中取数据
                         Q.pop();
                         if(node->left){//和层序遍历一样左不为空加入队列
                             parent.insert(make_pair(node->left,node));
                             Q.push(node->left);
                         }

                         if(node->right){//右不为空加入队列
                             parent.insert(make_pair(node->right,node));
                             Q.push(node->right);
                         }
                 }

                 unordered_setanscetor;
                 while(p){
                    anscetor.insert(p);//将p及他的祖先放入set中
                    p=parent[p];
                 }

                 while(!anscetor.count(q)){//检查是否相交相交就停止循环
                     q=parent[q];
                 }
             return q;
    }
};

 

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

对应letecode链接:

https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

题目描述:

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。
示例 2:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。

 

同样的我们可以使用上题的方法在这题同样可以过:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                    if(!root||root==q||root==p){
                        return root;
                    }
                TreeNode*pqInLeft=lowestCommonAncestor(root->left,p,q);
                TreeNode*pqInRight=lowestCommonAncestor(root->right,p,q);
                if(!pqInLeft){
                    return pqInRight;
                }
                if(!pqInRight){
                    return pqInLeft;
                }
                return root;
    }
};

我们也可以利用它是一颗搜索二叉树:

二叉树oj练习打卡_第6张图片

 对应代码:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
                  if(root->valval&&root->valval){//都在左边
                      return lowestCommonAncestor(root->right,p,q);
                  }
                  if(root->val>p->val&&root->val>q->val){//都在右边
                      return lowestCommonAncestor(root->left,p,q);
                  }
                  return root;//一个在左边一个在右边
    }
};

迭代方式:

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

 最后:
结语:对于个人来讲,在leetcode上进行探索以及单人闯关是一件有趣的时间,一个程序员,如果不喜欢编程,那么可能就失去了这份职业的乐趣。刷到我的文章的人,我希望你们可以驻足一小会,忙里偷闲的阅读一下我的文章,可能文章的内容对你来说很简单,(^▽^)不过文章中的每一个字都是我认真专注的见证!希望您看完之后,若是能帮到您,劳烦请您简单动动手指鼓励我,我必回报更大的付出~ 

 

你可能感兴趣的:(算法,leetcode,c++)