对应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]
们知道后续遍历的最后一个数字一定是根节点,所以数组中最后一个数字9就是根节点,我们从前往后找到第一个比9大的数字10,那么10后面的[10,12](除了9)都是9的右子节点,10前面的[3,5,4]都是9的左子节点,后面的需要判断一下,如果有小于9的,说明不是二叉搜索树,直接返回false。然后再以递归的方式判断左右子树。
再来看一个,他的后续遍历是[3,5,13,10,12,9]
我们来根据数组拆分,第一个比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的右子树,这时确定最近公共祖先需要遍历子树进行递归求解
对应代码:
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为公共祖先 } };
刚开始的时候我是这么想的:
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的最近公共节点来画个图看一下
我们看到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_map
parent;//记录遍历到的每个节点的父亲 queue Q; 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_set anscetor; 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; } };
我们也可以利用它是一颗搜索二叉树:
对应代码:
class Solution { public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if(root->val
val&&root->val val){//都在左边 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->val
val&&root->val val){ root=root->right; } else{ break; } } return root; } };
最后:
结语:对于个人来讲,在leetcode上进行探索以及单人闯关是一件有趣的时间,一个程序员,如果不喜欢编程,那么可能就失去了这份职业的乐趣。刷到我的文章的人,我希望你们可以驻足一小会,忙里偷闲的阅读一下我的文章,可能文章的内容对你来说很简单,(^▽^)不过文章中的每一个字都是我认真专注的见证!希望您看完之后,若是能帮到您,劳烦请您简单动动手指鼓励我,我必回报更大的付出~