Contents
开始之前学习一个单词热热身:
freebie 英[ˈfriːbi]
n. (常指公司提供的) 免费品;
二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。
二叉查找树中序遍历有序。DL是玄学,递归也是玄学!!!
1 修剪二叉搜索树(669)
题目:
给定一个二叉搜索树,同时给定最小边界L 和最大边界 R。通过修剪二叉搜索树,使得所有节点的值在[L, R]中 (R>=L) 。你可能需要改变树的根节点,所以结果应当返回修剪好的二叉搜索树的新的根节点。
示例:
思路:
从树的根节点开始判断,若根节点的值小于L,则返回的根节点在左子树中;若根节点的值大于R,则返回的根节点在右子树中;
如果根节点的值在[L, R]范围内,则对根节点的左右子节点递归即可。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root) return nullptr;
if(root->val > high) return trimBST(root->right, low, high);
if(root->val < low) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
2 二叉搜索树中第K小的元素(230)
题目:
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。
示例:
思路:
对二叉搜索树中序遍历得到的结果即是有序的,进而返回有序列表中第k-1个元素即可。
// 递归实现
class Solution {
public:
int num =0, k, res;
void func(TreeNode* node, int K){
if(!node || k>K) return;
func(node->left, K);
k++;
if(k==K){
res = node->val;
k++;
return;
}
func(node->right, K);
}
int kthSmallest(TreeNode* root, int k) {
func(root, k);
return res;
}
};
// 迭代实现
class Solution {
public:
int kthSmallest(TreeNode* root, int k) {
stack<TreeNode*> stk;
vector<int> res;
TreeNode* temp = root;
int num = 0;
while(temp != nullptr || !stk.empty() ){
if(temp!=nullptr){
stk.push(temp);
temp = temp->left;
}
else{
temp = stk.top();
stk.pop();
res.push_back(temp->val);
temp = temp->right;
}
}
return res[k-1];
}
};
3 把二叉搜索树转换为累加树(538)
题目:
给定一个二叉搜索树(Binary Search Tree),把它转换成为累加树(Greater Tree),使得每个节点的值是原来的节点值加上所有大于它的节点值之和。
示例:
思路:
将中序遍历二叉树的左中右顺序变为右中左顺序,不断累加右子节点的数值即可。
class Solution {
private:
int pre; // 记录前一个节点的数值
void traversal(TreeNode* cur) { // 右中左遍历
if (cur == NULL) return;
traversal(cur->right);
cur->val += pre;
pre = cur->val;
traversal(cur->left);
}
public:
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
};
4 二叉搜索树的最近公共祖先(235)
题目:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
示例:
思路:
在二叉搜索树里,如何判断一个节点的左子树里有p,右子树里有q呢?
在从上到下遍历树的时候,满足条件(p->val <= cur->val && cur->val <= q->val)
则说明该节点cur就是最近公共祖先了。
当cur->val均小于p与q的值,去递归cur的右子节点;当cur->val均大于p与q的值,去递归cur的左子节点;否则当前节点即为p与q的公共祖先。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root->val > p->val && root->val > q->val) { return lowestCommonAncestor(root->left, p, q);}
else if(root->val < p->val && root->val < q->val) {return lowestCommonAncestor(root->right, p, q);}
else return root;
}
};
5 二叉树的最近公共祖先(236)
题目:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
示例:
思路:
(1) 如果当前结点 root等于 NULL,则直接返回 NULL
(2) 如果 root等于 p或者 q,那这棵树一定返回 p 或者 q
(3) 然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left 和 right 表示
(4) 此时若left为空,那最终结果只要看 right;若 right 为空,那最终结果只要看 left
(5) 如果 left和 right都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root 是他们的最近公共祖先
(6) 如果 left 和 right 都为空,则返回空(其实已经包含在第4步中了)
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root==NULL) return NULL;
if(root==p|| root==q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left == NULL)
return right;
if(right == NULL)
return left;
if(left && right) // p和q在两侧
return root;
return NULL; // 必须有返回值
}
};
6 将有序数组转换为二叉搜索树(108)
题目:
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
示例:
思路:
有序数组的中点为树的根节点,中点前半部分的数构成左子树,中点后半部分的数构成右子树,通过对根节点的左右子树递归得到整棵树。
class Solution {
public:
TreeNode* DFS(vector<int>& nums, int start, int end){
if(start == end) return NULL;
int mid = (end + start)>>1;
TreeNode* root = new TreeNode(nums[mid]);
root->left = DFS(nums, start, mid);
root->right = DFS(nums, mid+1, end);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return DFS(nums, 0, nums.size());
}
};
7 有序链表转换二叉搜索树(109)
题目:
给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
示例:
思路:
①.原链表是有序的,要使转换成的二叉树高度平衡,只需要寻找中间节点作为跟节点即可,然后作用两侧分别转换成左子树和右子树。
②.查找中间节点可以使用快慢指针进行查找。
③.通过递归算法即可完成二叉搜索树的转换。
class Solution {
public:
ListNode* preMID(ListNode* head){
ListNode* fast = head;
ListNode* slow = head;
ListNode* pre = NULL;
while(fast!=nullptr && fast->next!=nullptr){
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
return pre;
}
TreeNode* sortedListToBST(ListNode* head) {
if(head==nullptr) return nullptr;
if(head->next == nullptr) return new TreeNode(head->val);
ListNode* premid = preMID(head);
ListNode* mid = premid->next;
premid->next = nullptr;
TreeNode* root = new TreeNode(mid->val);
root->left = sortedListToBST(head);
root->right = sortedListToBST(mid->next);
return root;
}
};
8 两数之和 IV - 输入 BST(653)
题目:
给定一个二叉搜索树和一个目标结果,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回 true。
思路:
使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。
应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。
class Solution {
public:
vector<int> sort;
void vct(TreeNode* root){
if(!root) return;
vct(root->left);
sort.push_back(root->val);
vct(root->right);
}
bool findTarget(TreeNode* root, int k) {
vct(root);
int i = 0;
int j = sort.size()-1;
int sum = 0;
while(i<j){
sum = sort[i] + sort[j];
if(sum==k) return true;
if(sum<k) i++;
else j--;
}
return false;
}
};
9 二叉搜索树的最小绝对差(530)
题目:
给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。
思路:
利用中序遍历二叉搜索树为有序数组的性质,取数组相邻元素之差最小值即可。
class Solution {
public:
vector<int> vct;
void handdle(TreeNode* root){
if(!root) return;
handdle(root->left);
vct.push_back(root->val);
handdle(root->right);
}
int getMinimumDifference(TreeNode* root) {
handdle(root);
int min = vct[1]-vct[0];
for(int i=2; i<vct.size();i++){
int temp = vct[i]-vct[i-1];
if(temp<min) min=temp;
}
return min;
}
};
10 二叉搜索树中的众数(501)
题目:
给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。
思路:
首先 BST 如果中序遍历,就是一个排序的数组,可以在遍历时记录这个数字出现的次数。
当新的数字与前一个数字不同,则根据这个数字出现的次数判断。
如果 小于 最大次数,不管;
如果 等于 最大次数,加入到答案列表中;
如果 大于 最大次数,清空答案列表,然后加入这个数字,并更新最大次数。
可以先遍历树放入数组中查找,也可以直接在中序遍历中做上述的处理。
class Solution {
public:
int count;
int maxCount;
int val = INT_MIN;
vector<int> res;
void inorder(TreeNode* root){
if(!root) return;
inorder(root->left);
count = (root->val==val)? count+1:1;
if(count==maxCount) res.push_back(root->val);
else if(count>maxCount){
maxCount = count;
res.clear();
res.push_back(root->val);
}
val = root->val;
inorder(root->right);
}
vector<int> findMode(TreeNode* root) {
inorder(root);
return res;
}
};
欢迎关注【OAOA】