题目链接:二叉搜索树中的搜索
通过此题目,我们了解了二叉搜索树的特点,其本身就是中序遍历的顺序,因此左边一定小于根节点,右边一定大于根节点。照此特信息即可搜索出来目标值。
TreeNode* searchBST(TreeNode* root, int val) {
if(root == NULL || root ->val == val) return root;
TreeNode* res = NULL;
if(root ->val > val) res = searchBST(root ->left, val);
else if(root ->val < val) res = searchBST(root ->right, val);
return res;
}
题目链接 :验证二叉搜索树
该思路中,在向左遍历的时候,pre处于root的左下,然后遍历右边的话,pre更新位置,位于root左上,然后根据搜索树中序遍历的特点,实现比较。(本题注意两个节点值相同也不满足搜索树的特点)。
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool left = isValidBST(root ->left);
if(pre != NULL && pre ->val >= root ->val) return false;
pre = root;
bool right = isValidBST(root ->right);
return left && right;
}
本题也可以用数组法,因为如果,中序遍历之后存储的话,数组一定有序。然后比较就可以。
vector vec;
void backtracking(TreeNode* root)
{
if(root == NULL) return;
backtracking(root ->left);
vec.push_back(root ->val);
backtracking(root ->right);
}
bool isValidBST(TreeNode* root) {
if(root == NULL) return root;
backtracking(root);
for(int i = 0; i= vec[i+1]){
return false;
}
}
return true;
}
题目链接:二叉搜索树最小绝对差
int res = 1e6 + 10;
TreeNode* pre = NULL;
void backtracking(TreeNode* root)
{
if(root == NULL) return;
backtracking(root ->left);
if(pre != NULL) res = min(abs(root ->val - pre ->val), res);
pre = root;
backtracking(root ->right);
}
int getMinimumDifference(TreeNode* root) {
if(root == NULL) return 0;
backtracking(root);
return res;
}
题目链接: 二叉搜索树中的众数
本题的话,还是使用双指针,使用count记录当前的出现个数,maxcount记录历史最大个数,由于搜索树的特点,前一个数一定小于等于上一个数,因此可以这样记录。
int count = 0;int max_count = 0;
vector result;
TreeNode* pre = NULL;
void backtracking(TreeNode* cur)
{
if(cur == NULL) return;
backtracking(cur ->left);
if(pre == NULL) count = 1;
else if(pre ->val == cur ->val){
count ++;
}
else {
count = 1;
}
pre = cur;
if(count == max_count){
result.push_back(cur ->val);
}
if(count > max_count){
max_count = count;
result.clear();
result.push_back(cur ->val);
}
backtracking(cur ->right);
}
vector findMode(TreeNode* root) {
backtracking(root);
return result;
}
题目链接:二叉搜索树变为累加树
本题相当于将原先的前后指针用数值代替了,本质思想还没变。同时遍历顺序遍历为右中左,因为累加树顺序是这样的。
int pre = 0;
void backtracking(TreeNode* cur)
{
if(cur == NULL) return;
backtracking(cur ->right);
cur ->val += pre;
pre = cur ->val;
backtracking(cur ->left);
}
TreeNode* convertBST(TreeNode* root) {
if(root == NULL) return root;
backtracking(root);
return root;
}
该类型的题目,大部分需要使用前后指针配合上中序遍历的递归解法,这得益于二叉搜索树本身具有的中序的特点。同时我们遇到搜索树,要从其本身特点想起,方便解题。
题目链接:二叉树最近公共祖先
求解该问题,需要确定思路为分为左右两部分求解,如果左右都存在一个目标节点,那么返回当前节点就是最大公共祖先,如果只存在一个,那就返回存在的。如果都不存在,返回NULL,同时用NULL作为判断条件。
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL) return root;
if(root == q || root == p) return root;
TreeNode* left = lowestCommonAncestor(root ->left, p, q);
TreeNode* right = lowestCommonAncestor(root ->right, p, q);
if(left != NULL && right != NULL) return root;
else if(left == NULL && right != NULL) return right;
else if(left != NULL && right == NULL) return left;
else return NULL;
}
题目链接:二叉搜索树最近公共祖先
本题与上面基本相同。
TreeNode* pre = NULL;
TreeNode* backtracking(TreeNode* cur, TreeNode* p, TreeNode* q)
{
if(cur == NULL) return NULL;
if(cur ->val > p ->val && cur ->val > q ->val){
TreeNode* left = backtracking(cur ->left, p, q);
if(left != NULL) return left;
}
if(cur ->val < p ->val && cur ->val < q ->val){
TreeNode* right = backtracking(cur ->right, p, q);
if(right != NULL) return right;
}
return cur;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return backtracking(root, p, q);
}
我们可以看出,公共祖先问题的话,主要利用的返回值的方法求解,如果这部分不满足条件,则返回另一部分,从而求出最优解。
题目链接:二叉搜索树插入操作
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == NULL){
TreeNode* res = new TreeNode(val);
return res;
}
TreeNode* cur = root;
TreeNode* pre = root;
while(cur != NULL)
{
pre = cur;
if(cur ->val > val) cur = cur ->left;
else cur = cur ->right;
}
TreeNode* res = new TreeNode(val);
if(pre ->val > val) pre ->left = res;
else pre ->right = res;
return root;
}
此处和迭代法相同,均利用了二叉搜索树的特点。
TreeNode* parent = NULL;
void backtracking(TreeNode* root, int val)
{
if(root == NULL){
TreeNode* tem = new TreeNode(val);
if(parent ->val > val) parent ->left = tem;
else parent ->right = tem;
return;
}
parent = root;
if(root ->val < val) backtracking(root ->right, val);
if(root ->val > val) backtracking(root ->left, val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(root == NULL){
root = new TreeNode(val);
}
backtracking(root, val);
return root;
}
题目链接:删除二叉搜索树的节点
题目五种情况,一、未找到删除节点。
二、 删除节点左右都为空。
三、删除节点左为空,右不为空。
四、删除节点左不为空,右为空。
五、删除节点左右都不为空。
前四种情况简单,而最后一种情况的话,需要将符合key值的节点的左子树移动到右子树的最左边。从而实现删除节点。
TreeNode* deleteNode(TreeNode* root, int key) {
if(root == NULL) return NULL;
if(root ->val == key){
if(root ->left == NULL && root ->right == NULL){
delete root;
return NULL;
}
else if(root ->left == NULL && root ->right != NULL){
TreeNode* tem = root ->right;
delete root;
return tem;
}
else if(root ->left != NULL && root ->right == NULL){
TreeNode* tem = root ->left;
delete root;
return tem;
}
else {
TreeNode* tem = root ->right;
while(tem ->left != NULL){
tem = tem ->left;
}
tem ->left = root ->left;
TreeNode* cur = root;
root = root ->right;
delete cur;
return root;
}
}
if(root ->val > key) root ->left = deleteNode(root ->left, key);
if(root ->val < key) root ->right = deleteNode(root ->right, key);
return root;
}
题目链接:修剪二叉搜索树
本题使用直接用结点用递归的方式接收返回值,然后代码非常简单,同时会发现,搜索树的修改与构造的题很多都是接收返回值的解法。
注意:该题目无论前序还是后序,结果都是对的。
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return NULL;
root ->left = trimBST(root ->left, low, high);
root ->right = trimBST(root ->right, low, high);
if(root ->val > high) return root ->left;
else if(root ->val < low) return root ->right;
return root;
}
题目链接:有序数组变为二叉搜索树
我们本题选取中间值为根节点,然后分别分割数组,其中分割时我们根据前几天总结的整数二分的原则,区间的开闭要选好,我们这里选用左闭右闭的区间,然后进行分割,当left>right时,直接返回NULL。
TreeNode* backtracking(vector& nums, int left, int right)
{
if(left > right) return NULL;
int mid = (left + right) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root ->left = backtracking(nums,left, mid - 1);
root ->right = backtracking(nums, mid + 1, right);
return root;
}
TreeNode* sortedArrayToBST(vector& nums) {
TreeNode* root = backtracking(nums, 0, nums.size() - 1);
return root;
}
这部分,我们发现经常用到带返回值的递归,这样方便我们解题,而搜索树属性的那部分,经常使用前后指针。
二叉搜索树的3种常用解题思路:
①、利用搜索树本身的特点结合数组,例如其本身就是中序有序的,像求解验证搜索树是否有序的时候,数组收集完之后,只需要遍历数组是否有序就可以验证。
②、善于使用双指针,前后指针,因为搜索树有序的特点,每次遍历的时候比较相邻两个节点时非常有优势,像搜索树属性那一部分的题目就经常使用这个。
③、利用递归的返回值,这种方法,尤其经常用于涉及搜索树的结构会发生大幅度改变还有需要对搜索树进行构造,总之就是涉及结构的方面用的特别多。
只要把以上三种方法练熟练,只要碰到搜索树的题目,直接想这三种方法即可。