700. 二叉搜索树中的搜索
递归法
1.确定递归函数的参数和返回值
参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点
2.确定终止条件
如果root为空,或者找到这个数值了,就返回root节点
3.确定单层递归的逻辑
root->val > val,搜索左子树,如果root->val < val,就搜索右子树,最后如果都没有搜索到,就返回NULL。
/**
* 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:
TreeNode* searchBST(TreeNode* root, int val) {
//终止条件
if(root==NULL || root->val==val) return root;
//单层循环
if(root->valright,val);//往右子树搜索
if(root->val>val) return searchBST(root->left,val);//往左子树搜索
return NULL;//最后都没有搜索到,直接返回NULL
}
};
迭代法
二叉搜索树不一样,因为二叉搜索树的特殊性,也就是节点的有序性,可以不使用辅助栈或者队列就可以写出迭代法。
对于二叉搜索树,不需要回溯的过程,因为节点的有序性就帮我们确定了搜索的方向。
/**
* 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:
TreeNode* searchBST(TreeNode* root, int val) {
while(root!=NULL){
if(root->val > val) root=root->left;//往左子树走
else if(root->val < val) root=root->right;//往右子树走
else return root;
}
return NULL;
}
};
98. 验证二叉搜索树
递归法:
中序遍历下,输出的二叉搜索树节点的数值是有序序列
验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
把二叉树转变为数组来判断
因此先递归求出二叉搜索树的中序遍历数组,接着遍历这个中序遍历数组,看看是否是升序的
不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。
所以以下代码的逻辑是错误的
if (root->val > root->left->val && root->val < root->right->val) {
return true;
} else {
return false;
}
整体代码
/**
* 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:
vector res;//放在外面,如果放在里面,递归的过程中会不断更新新的res
//递归求出二叉搜索树的中序遍历数组
void travelsal(TreeNode* root){
//终止条件
if(root==NULL) return;//遇到空节点,立即return
//中序遍历
travelsal(root->left);//左
res.push_back(root->val);//根
travelsal(root->right);//右
}
bool isValidBST(TreeNode* root) {
travelsal(root);
//遍历二叉搜索树的中序遍历数组,看看是否是升序的
for(int i=0;i=res[i+1]) return false;//一旦不是升序,返回false
}
return true;
}
};
迭代法:
中序遍历的迭代法大框架,利用一个pre节点保存前一个访问的结点。
注意,pre不为空时做判断 if(pre!=NULL && cur->val <= pre->val) return false;
/**
* 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:
bool isValidBST(TreeNode* root) {
TreeNode* cur=root;
TreeNode* pre=NULL;
stack st;
while(cur!=NULL || !st.empty()){
if(cur!=NULL){//还未到最左边的节点
st.push(cur);
cur=cur->left;//左
}
else{
cur=st.top();//中
st.pop();
if(pre!=NULL && cur->val <= pre->val) return false;
pre=cur;
cur=cur->right;//右
}
}
return true;
}
};
530. 二叉搜索树的最小绝对差
递归法:
二叉搜索树是有序的,遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值,这样就简单多了。
/**
* 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:
vector res;
void travelsal(TreeNode* root){
if(root==NULL) return;
travelsal(root->left);//左
res.push_back(root->val);//根
travelsal(root->right);//右
}
int getMinimumDifference(TreeNode* root) {
travelsal(root);
int result=INT_MAX;
for(int i=0;i
以上是将二叉搜索树转化为有序数组了,其实在二叉搜素树中序遍历的过程中,我们就可以直接计算了。需要用一个pre节点记录一下cur节点的前一个节点。
/**
* 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:
int result=INT_MAX;
TreeNode* pre;
void travelsal(TreeNode* root){
if(root==NULL) return;
travelsal(root->left);//左
if(pre!=NULL){
result=min(result,root->val - pre->val);
}
pre=root;
travelsal(root->right);//右
}
int getMinimumDifference(TreeNode* root) {
travelsal(root);
return result;
}
};
迭代法:
二叉搜索树迭代法的大框架,用一个pre节点保存cur节点的前一个节点,
/**
* 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:
int getMinimumDifference(TreeNode* root) {
stack st;
int result=INT_MAX;
TreeNode* pre=NULL;
TreeNode* cur=root;
while(cur!=NULL || !st.empty()){
if(cur!=NULL){//还未到最左的叶子节点
st.push(cur);
cur=cur->left;
}
else{
cur=st.top();
st.pop();
if(pre!=NULL){
result=min(result,cur->val - pre->val);
}
pre=cur;
cur=cur->right;
}
}
return result;
}
};
501. 二叉搜索树中的众数
递归法:
if (pre == NULL) { // 第一个节点
count = 1; // 频率为1
} else if (pre->val == cur->val) { // 与前一个节点数值相同
count++;
} else { // 与前一个节点数值不同
count = 1;
}
pre = cur; // 更新上一个节点
此时又有问题了,因为要求最大频率的元素集合(注意是集合,不是一个元素,可以有多个众数),如果是数组上大家一般怎么办?应该是先遍历一遍数组,找出最大频率(maxCount),然后再重新遍历一遍数组把出现频率为maxCount的元素放进集合。(因为众数有多个)这种方式遍历了两遍数组。
但这里其实只需要遍历一次就可以找到所有的众数。
那么如何只遍历一遍呢?
如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组),代码如下
if (count == maxCount) { // 如果和最大值相同,放进result中
result.push_back(cur->val);
}
频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了。
if (count > maxCount) { // 如果计数大于最大值
maxCount = count; // 更新最大频率
result.clear(); // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
result.push_back(cur->val);
}
总体代码:
/**
* 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:
vector res;
int count=0,maxCount=0;
TreeNode* pre=NULL;
void travelsal(TreeNode* cur){
//终止条件
if(cur==NULL) return;
travelsal(cur->left);//左
//中
if(pre==NULL){//如果是第一个节点
count=1;
}else if(pre->val==cur->val){//和前一个节点值相同
count++;//统计值++
}else{//和前一个节点值不同
count=1;//统计值赋为1
}
pre=cur;//用pre保存上一个节点,更新上一个节点
if(count==maxCount){//次数是最大次数
res.push_back(cur->val);//放进res中
}
if(count>maxCount){//次数大于最大次数
maxCount=count;//更新最大次数
res.clear();//清除之前的数据
res.push_back(cur->val);//将新的众数放进res
}
travelsal(cur->right);//右
return;
}
vector findMode(TreeNode* root) {
travelsal(root);
return res;
}
};
迭代法:
迭代的主要逻辑和递归的是一样的。
/**
* 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:
//迭代法,中序遍历
vector findMode(TreeNode* root) {
vector res;
int count=0,maxCount=0;
TreeNode* pre=NULL;
TreeNode* cur=root;
stack st;
while(cur!=NULL || !st.empty()){
if(cur!=NULL){
st.push(cur);
cur=cur->left;//左
}
else{
cur=st.top();
st.pop();
if(pre==NULL){//要处理的第一个节点
count=1;
}else if(pre->val==cur->val){//节点值和上一个的相同
count++;
}else{//节点值和上一个的不同
count=1;
}
pre=cur;//更新上一个节点
if(count==maxCount){
res.push_back(cur->val);
}
if(count>maxCount){
maxCount=count;
res.clear();
res.push_back(cur->val);
}
cur=cur->right;//右
}
}
return res;
}
};
235. 二叉搜索树的最近公共祖先
递归法
在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?
其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。
普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。
判断节点p,q在不在同一侧,通过二叉搜索树的有序性来判断。如果p,q的值都小于root的值,递归往左子树搜索,如果p,q的值都大于root的值,递归往右子树搜索,否则,表明在异侧,直接返回root。
/**
* 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((p->valval && q->val>root->val) || (p->val>root->val && q->valval)) return root;
if(p->valval && q->valval) return lowestCommonAncestor(root->left,p,q);
else if(p->val>root->val && q->val>root->val) return lowestCommonAncestor(root->right,p,q);
else return root;
}
};
迭代法:
逻辑和递归一样
在root非空发情况下,去判断节点p,q在不在同一侧,通过二叉搜索树的有序性来判断。如果p,q的值都小于root的值,递归往左子树搜索,如果p,q的值都大于root的值,递归往右子树搜索,否则,表明在异侧,直接返回root。
/**
* 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) {
while(root){
if(p->valval && q->valval) root=root->left;
else if(p->val>root->val && q->val>root->val) root=root->right;
else return root;
}
return root;
}
};
701. 二叉搜索树中的插入操作
递归法
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
其实可以不考虑题目中提示所说的改变树的结构的插入方式。只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。
当root为空的时候,就是找到待插入的位置,建立值为val的节点
if(root->val > val) root->left=insertIntoBST(root->left,val);//如果是val小于root的值,那么新建立返回的node就插入root的左子树
/**
* 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:
TreeNode* insertIntoBST(TreeNode* root, int val) {
//终止条件
if(root==NULL){//为空,找到待插入的位置,建立值为val的节点
TreeNode* node=new TreeNode(val);
return node;
}
//单层循环
if(root->val > val) root->left=insertIntoBST(root->left,val);//如果是val小于root的值,那么新建立返回的node就插入root的左子树
if(root->val < val) root->right=insertIntoBST(root->right,val);
return root;//最后返回root
}
};
迭代法:
在迭代法遍历的过程中,需要记录一下当前遍历的节点的父节点,这样才能做插入节点的操作。
具体见注释
/**
* 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:
TreeNode* insertIntoBST(TreeNode* root, int val) {
//一开始排除root本身为空的情况
if(root==NULL){
TreeNode* node=new TreeNode(val);
return node;
}
TreeNode* cur=root;
TreeNode* parent=root;//用来记录当前遍历的节点的父节点,这样才能做插入节点的操作
//找到待插入位置
while(cur!=NULL){
parent=cur;
if(cur->val > val) cur=cur->left;
else cur=cur->right;
}
TreeNode* node=new TreeNode(val);
//插入节点
if(parent->val > val) parent->left=node;
else parent->right=node;
return root;
}
};
450. 删除二叉搜索树中的节点
删除节点有以下五种情况:
//左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
else{//待删除节点的左右节点都不为空。
//找右子树最左边的节点
TreeNode* cur=root->right;
while(cur->left){
cur=cur->left;
}
cur->left=root->left;
TreeNode* temp=root;//用一个临时节点保存root,便于等下删除
root=root->right;
delete temp;
return root;
}
/**
* 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:
TreeNode* deleteNode(TreeNode* root, int key) {
if(root==NULL) return root;//没找到待删除的节点,返回空
if(root->val==key){//找到待删除节点
if(root->left==NULL && root->right==NULL){//待删除节点是叶子节点,直接删除,返回空节点即可
delete root;
return NULL;
}
else if(root->left!=NULL && root->right==NULL){//待删除节点的左节点不为空,右节点为空。将节点删除,左节点作为根节点
auto reNode=root->left;//将左节点先保存起来
delete root;//删除根节点
return reNode;
}
else if(root->left==NULL && root->right!=NULL){//待删除节点的右节点不为空,左节点为空。将节点删除,右节点作为根节点
auto reNode=root->right;//将右节点先保存起来
delete root;//删除根节点
return reNode;
}
//左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
else{//待删除节点的左右节点都不为空。
//找右子树最左边的节点
TreeNode* cur=root->right;
while(cur->left){
cur=cur->left;
}
cur->left=root->left;
TreeNode* temp=root;//用一个临时节点保存root,便于等下删除
root=root->right;
delete temp;
return root;
}
}
if(root->val > key) root->left=deleteNode(root->left,key);
if(root->val < key) root->right=deleteNode(root->right,key);
return root;
}
};
669. 修剪二叉搜索树
终止条件:
修剪的操作并不是在终止条件上进行的,所以就是遇到空节点返回就可以了。
单层递归循环:
如果root(当前节点)的元素小于low的数值,那么应该递归右子树,并返回右子树符合条件的头结点。(在右子树找符合【low,high】的节点)
如果root(当前节点)的元素大于high的数值,那么应该递归左子树,并返回左子树符合条件的头结点。(在左子树找符合【low,high】的节点)
接下来要将下一层处理完左子树的结果赋给root->left,处理完右子树的结果赋给root->right。最后返回root节点。
/**
* 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:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root==NULL) return NULL;
//以下两个判断条件都是指不在区间范围内的,目的就是为了去找区间范围内的节点
if(root->val > high){//递归左子树,在左子树找符合[low,high]的节点
TreeNode *left=trimBST(root->left,low,high);
return left;
}
if(root->val < low){//递归右子树,在右子树找符合[low,high]的节点
TreeNode* right=trimBST(root->right,low,high);
return right;
}
root->left=trimBST(root->left,low,high);//trimBST递归的处理结果用root->left接住
root->right=trimBST(root->right,low,high);
return root;
}
};
108. 将有序数组转换为二叉搜索树
其实这里不用强调平衡二叉搜索树,数组构造二叉树,构成平衡树是自然而然的事情,因为大家默认都是从数组中间位置取值作为节点元素,一般不会随机取,所以想构成不平衡的二叉树是自找麻烦
本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间.
取数组中间元素的位置,不难写出int mid = (left + right) / 2;
,这么写其实有一个问题,就是数值越界,例如left和right都是最大int,这么操作就越界了,在二分法中尤其需要注意!可以这么写:int mid = left + ((right - left) / 2);
取了中间位置,就开始以中间位置的元素构造节点,代码:TreeNode* root = new TreeNode(nums[mid]);
。
接着划分区间,root的左孩子接住下一层左区间的构造节点,右孩子接住下一层右区间构造的节点。
最后返回root节点
/**
* 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:
TreeNode* travelsal(vector& nums,int left,int right){
if(left>right) return NULL;//终止条件
int mid=left+(right-left)/2;
TreeNode* root=new TreeNode(nums[mid]);
root->left=travelsal(nums,left,mid-1);
root->right=travelsal(nums,mid+1,right);
return root;
}
TreeNode* sortedArrayToBST(vector& nums) {
return travelsal(nums,0,nums.size()-1);
}
};
538. 把二叉搜索树转换为累加树
本题依然需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。 全局变量pre,用来保存cur节点的前一个节点的数值,定义为int型.
右中左来遍历二叉树
/**
* 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:
//反中序遍历
int pre;//用于记录cur的前一个结点
void travelsal(TreeNode* cur){
if(cur==NULL) return;
travelsal(cur->right);//右
cur->val+=pre;
pre=cur->val;
travelsal(cur->left);//左
}
TreeNode* convertBST(TreeNode* root) {
pre=0;
travelsal(root);
return root;
}
};