Leetcode数据结构入门第十四天(二叉搜索树(2))
98. 验证二叉搜索树
题目描述
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
样例
输入:root = [2,1,3]
输出:true
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
思路
二叉搜索树的性质:根节点的左子树的所有节点值都小于根节点,右子树的所有节点值都大于根节点(节点值相等的,也不属于二叉搜索树)
因为中序遍历的顺序为左-根-右,所以二叉搜索树的中序遍历结果为升序,所以我们只需要每次判断当前遍历的值是否比前一个节点值大,如果不是,说明不是二叉搜索树。(这里只需要保存前一个节点的值,用的是中序遍历的递归方法)
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
//二叉搜索树的中序遍历结果为升序,所以只要跟前一个节点比较即可
long pre=LONG_MIN;//记录中序遍历的前一个节点值,
bool isValidBST(TreeNode* root) {
//中序遍历
//根节点为空,返回true
if(root==NULL) return true;
//访问左子树
bool left=isValidBST(root->left);
//如果为flase,直接返回:可以节省
if(!left) return false;
//访问根节点
if(root->val<=pre) return false;
//更新前一个节点值
pre=root->val;
//访问右子树
bool right=isValidBST(root->right);
return left&&right;
}
};
653. 两数之和 IV - 输入 BST
题目描述
给定一个二叉搜索树 root 和一个目标结果 k,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
样例
输入: root = [5,3,6,2,4,null,7], k = 9
输出: true
输入: root = [5,3,6,2,4,null,7], k = 28
输出: false
思路一
利用二叉搜索树的性质:中序遍历后结果为升序,所以这里我们先利用中序遍历,把遍历后的升序结果存在nums数组中,再通过双指针对数组中元素的不同组合进行遍历
双指针指的是左端点(初始值:下标0)和右端点(初始值:下标len-1),每次遍历,判断左端点和右端点值之和是否等于目标值,相等返回true,不等,移动左指针或者右指针(大了,就移动右指针,变小一点,反之,移动左指针)
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void inorder(TreeNode* root,vector<int> &res)
{
if(root==NULL) return;
//左子树
if(root->left) inorder(root->left,res);
//根节点
res.push_back(root->val);
if(root->right) inorder(root->right,res);
}
bool findTarget(TreeNode* root, int k) {
vector<int> nums;//存储遍历结果
//中序遍历:获得升序的结果
inorder(root,nums);
//双指针:
int len=nums.size();
int left=0;//左端点
int right=len-1;//右端点
int res;//结果
while(left<right)
{
//当前数组左端点和右端点值之和存在res中
res=nums[left]+nums[right];
//如果res等于目标值,结束,
if(res==k) return true;
//不等于,移动指针:比较小,左端点增,向右边值较大的方向移动
//比较大,右端点减,向左边值较小的方向移动
else if(res<k) left++;
else right--;
}
//遍历后,还没返回说明没找到
return false;
}
};
思路二
使用哈希表,对访问过的节点值进行存储,避免重复访问。具体实现:在树的每个节点上通过哈希表查找是否存在哈希表中的一个值与当前节点值之和等于目标值,存在返回true。然后遍历它的两棵子树(左子树和右子树),不断递归即可。
hashtable.count(num)//计算哈希表中元素为num的个数(其实就是0、1)
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
unordered_set<int> hashtable;//哈希表
bool findTarget(TreeNode* root, int k) {
//递归终点:
//1.如果根节点为空,返回false
if(root==NULL) return false;
//2.如果哈希表中能够找到一个值,使得两数之和=目标值,返回true
if(hashtable.count(k-root->val))
{
return true;
}
//插入当前根节点值到哈希表中
hashtable.insert(root->val);
//处理单层递归逻辑:当前节点的左右子树是否能够找到两个值之和满足target
bool left=findTarget(root->left,k);
bool right=findTarget(root->right,k);
return left||right;
}
};
235. 二叉搜索树的最近公共祖先
题目描述
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
样例
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
输出: 2
解释: 节点 2 和节点 4 的最近公共祖先是 2, 因为根据定义最近公共祖先节点可以为节点本身。
思路一
非递归,利用二叉搜索树的性质:对二叉搜索树进行一次遍历,
如果当前根节点的值大于两个节点的值,那么根节点向左子树继续搜索;
如果当前根节点的值小于两个节点的值,那么根节点向右子树继续搜索;
如果一个节点值大于等于当前根节点,一个节点值小于等于根节点,那么当前根节点就是他们的最近公共祖先
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//二叉搜索树,两节点的祖先ances范围:p<=ances<=q(假设p<q)
TreeNode* ances=root;//从根节点开始
while(1)
{
//如果当前节点比p,q两节点值都大,继续向左子树寻找
if(p->val<ances->val&&q->val<ances->val)
{
ances=ances->left;
}
//如果当前节点比p,q节点都要小,继续向右子树寻找
else if(p->val>ances->val&&q->val>ances->val)
{
ances=ances->right;
}
//直到找到一个值处于两节点之间
else break;
}
return ances;
}
};
思路二
递归法:因为二叉搜索树本质也是二叉树,所以用求解二叉树的方式也可以求解此题,详细思路见下一题思路分析。
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//递归终点:
//根节点等于两个节点其中一个或者为空,这已经是最大深度,再下去就有一个节点不存在了
if(root==NULL||root==p||root==q)
{
return root;
}
//如果根节点不是p,q节点中的一个,查找左子树和右子树
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
//如果left为空,说明p,q都在右子树上,返回右子树的结果即可
if(left==NULL) return right;
//同理右子树为空,返回左子树结果
if(right==NULL) return left;
//都非空,说明p,q位于当前根节点的左右两侧,所以当前节点为最近公共祖先
return root;
}
};
236. 二叉树的最近公共祖先
题目描述
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
样例
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
思路
递归法:
递归终点:根节点为空或者根节点为两个节点其中一个,直接返回当前根节点即可
单层递归逻辑:如果根节点不是p或q中的一个,那么就要在左子树和右子树中查找p和q,如果左子树查找为空,说明p,q两节点都在右子树上,返回右子树结果即可,同理对右子树也是如此。直到左右子树的查找结果都非空时,说明p,q节点一个在根节点的左侧,一个在根节点右侧,他们的公共祖先就是当前根节点。
参考代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//递归终点:根节点为空或者根节点为p或q中的一个,直接返回
if(root==NULL||root==p||root==q)
{
return root;
}
//单层逻辑:根节点不是p或q中的一个,那么就要在左子树和右子树中查找p和q
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
//如果left为空,说明这两个节点在根结点的右子树上,只需要返回右子树查找的结果即可
if(left==NULL) return right;
//如果right为空,也是一样
if(right==NULL) return left;
//如果左右子树都非空,说明p,q节点在当前根节点的左右两侧,那么当前节点为公共祖先
return root;
}
};