刷完了二叉树,我们接着在二叉搜索树的路上努力~
二叉搜索树属性
二叉搜索树中的搜索
迭代法
TreeNode* searchBST(TreeNode* root, int val) {
if(!root) return NULL;
while(root != NULL){
if(val > root->val) root = root->right;
else if(val < root->val) root = root->left;
else break;
}
return root ? root : NULL;
}
递归法
TreeNode* searchBST(TreeNode* root, int val) {
if(!root || root->val == val) return root;
if(val > root->val) return searchBST(root->right, val);//若函数有返回值,这里一定要return
if(val < root->val) return searchBST(root->left, val);
return NULL;
}
没什么好说的,就是注意root != NULL的判断,必须非空才可以用root -> val
验证二叉搜索树
陷入了一个陷阱:以为只要当前结点的左结点<当前值,当前结点的右结点>当前值就可以了,但是要求的是当前结点的左子树上所有值均<当前值,当前结点的右子树上的所有值均>当前值呀。想过求左子树的最大值和右子树的最小值。但有更巧妙的方法——只要中序遍历然后判读数组是否有序即可了呀。(这都没想到果然是迷糊中)
递归法
vector<int> res;
void solve(TreeNode* root){
if(root->left) solve(root->left);
res.push_back(root->val);
if(root->right) solve(root->right);
}
bool isValidBST(TreeNode* root) {
solve(root);
int s = res.size();
for(int i = 0; i < s - 1; i++){
if(res[i+1] <= res[i]) return false;
}
return true;
}
或者不必开数组,直接在中序时候就判断了(顺手记录前一个,拿来和当前值比较)
不可判断root->left时就return,因为一颗树为搜索二叉树那么其左右子树都是搜索二叉树。
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if(!root) return true;
bool left = isValidBST(root->left);
if(pre && pre->val >= root->val) return false;
pre = root;
bool right = isValidBST(root->right);
return left && right;
}
迭代法
用栈模拟中序遍历?忘得干干净净了(因为当时并没有理解透彻)
注意:用栈模拟中序遍历一开始是不需要把根结点push进去的(因为中序并不是从跟结点开始的)
bool isValidBST(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL;
while(!st.empty() || cur){
if(cur){
st.push(cur);
cur = cur->left;
}else{
cur = st.top();
st.pop();
if(pre && pre->val >= cur->val) return false;
pre = cur;
cur = cur->right;
}
}
return true;
}
二叉搜索树的最小绝对差
递归法
vector<int> v;
int res = INT_MAX;
void solve(TreeNode* root){
if(root->left) solve(root->left);
v.push_back(root->val);
if(root->right) solve(root->right);
}
int getMinimumDifference(TreeNode* root) {
solve(root);
int s = v.size();
for(int i = 0; i < s-1; i++) res = min(res, v[i+1] - v[i]);
return res;
}
或者不必开数组,在中序时顺便记录前一结点的值。
注意:这个pre指针须在全局处定义
int res = INT_MAX;
TreeNode* pre = NULL;
void solve(TreeNode* cur){
if(cur->left) solve(cur->left);
if(pre) res = min(res, cur->val - pre->val);
pre = cur;
if(cur->right) solve(cur->right);
}
int getMinimumDifference(TreeNode* root) {
solve(root);
return res;
}
迭代法
继续复习用栈模拟中序
注意一开始并不把根结点压栈
int getMinimumDifference(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL;
int res = INT_MAX;
while(!st.empty() || cur){
if(cur){
st.push(cur);
cur = cur->left;
}else{
cur = st.top(); st.pop();
if(pre) res = min(res, cur->val - pre->val);
pre = cur;
cur = cur->right;
}
}
return res;
}
总结
二叉搜索树求最值、求差值之类,都要考虑到二叉树是有序的,利用好这一特点。
二叉搜索树中的众数
递归法
map<int, int> count;
int max_count;
void solve(TreeNode* root){
if(root->left) solve(root->left);
count[root->val]++;
max_count = max(max_count, count[root->val]);
if(root->right) solve(root->right);
}
vector<int> findMode(TreeNode* root) {
solve(root);
vector<int> v;
map<int,int>::iterator iter;
for(iter = count.begin(); iter != count.end(); iter++){
if(iter->second == max_count) v.push_back(iter->first);
}
return v;
}
或者只需遍历一遍,还是老套路,记录之前的结点,由于中序遍历二叉搜索树是有序的,所以如果是众数必定集中在相邻区域。
vector<int> res;
int max_count, count;
TreeNode* pre = NULL;
void solve(TreeNode* cur){
if(cur->left) solve(cur->left);
if(!pre) count = 1; //第一个结点
else if(pre->val == cur->val) count++;
else count = 1;
pre = cur;
if(count == max_count) res.push_back(cur->val);
if(count > max_count){
max_count = count;
res.clear();
res.push_back(cur->val);
}
if(cur->right) solve(cur->right);
}
vector<int> findMode(TreeNode* root) {
solve(root);
return res;
}
迭代法
vector<int> res;
int max_count, count;
vector<int> findMode(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = NULL;
while(!st.empty() || cur){
if(cur){
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 == max_count) res.push_back(cur->val);
if(count > max_count){
max_count = count;
res.clear();
res.push_back(cur->val);
}
cur = cur->right;
}
}
return res;
}
二叉树的最近公共祖先
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == p || root == q || root == NULL) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if(left && right) return root;
else if(left) return left;
else if(right) return right;
else return NULL;
}
此题解法甚妙!我只有抚掌称赞的份儿了!
自己在那里搞什么中序遍历、取中间元素搞得满头大汗的,果然又剑走偏锋了(lll¬ω¬)
二叉搜索树的最近公共祖先
递归法
从下到上遍历搜索二叉树,一旦发现cur的值处于p和q之间就算找到了最近公共祖先。
因为搜索二叉树的特性,中序有序,那么值处于二者之间必定是其父。
TreeNode* res;
void solve(TreeNode* root, TreeNode* p, TreeNode* q){
if(!root) return;
if(root->val >= min(p->val, q->val) && root->val <= max(p->val, q->val)){
res = root;
return;
}
lowestCommonAncestor(root->left, p, q);
lowestCommonAncestor(root->right, p, q);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
solve(root, p, q);
return res;
}
和标程不太一样,这是我自己想的,符合前序的逻辑,而且我觉得也不需要返回值(不需要遍历整棵树)
来看看迭代的解法(再次利用性质)
迭代法
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while(root){
if(root->val > p->val && root->val > q->val) root = root->left;
else if(root->val < p->val && root->val < q->val) root = root->right;
else return root;
}
return NULL;
}
二叉搜索树的修改与构造
二叉树搜索树的插入
个人认为还是蛮简单的(它给的数据不强,而且有很多种方法,我这是最简单的直接往叶子那里插)
迭代法
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root) return new TreeNode(val);
TreeNode* cur = root;
TreeNode* pre = NULL;
while(cur){
pre = cur;
if(val > cur->val) cur = cur->right;
else cur = cur->left;
}
TreeNode* newNode = new TreeNode(val);
newNode->left = NULL, newNode->right = NULL;
if(val < pre->val) pre->left = newNode;
else pre->right = newNode;
return root;
}
递归法
void solve(TreeNode* cur, int val){
if(!cur) return;
if(!cur->left && cur->val > val){
TreeNode* newNode = new TreeNode(val);
cur->left = newNode;
}
if(!cur->right && cur->val < val){
TreeNode* newNode = new TreeNode(val);
cur->right = newNode;
}
if(val > cur->val) solve(cur->right, val);
else solve(cur->left, val);
}
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root) return new TreeNode(val);
solve(root, val);
return root;
}
删除二叉搜索树的节点
啊啊啊啊删除好难!
但是务必掌握!
终止条件:没有找到要删除的节点,直接返回root
单层递归逻辑:
①左右孩子均为空(目标节点为叶子节点),直接删掉
②左孩子为空,右孩子补位
③右孩子为空,左孩子补位
④左右孩子均不为空,将待删结点的左孩子放到右孩子的最左叶子的左孩子处,然后右孩子补位
发现自己在魔改了就要及时止损,回头想想逻辑
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return root;
if(root->val == key){
if(!root->left && !root->right) return NULL;
else if(!root->left) return root->right;
else if(!root->right) return root->left;
else{
TreeNode* cur = root->right;
while(cur->left) cur = cur->left;
cur->left = root->left;
TreeNode* tmp = root;
root = root->right;
delete tmp;
return root;
}
}
if(root->val < key) root->right = deleteNode(root->right, key);
if(root->val > key) root->left = deleteNode(root->left, key);
return root;
}
这里最难的就是左右孩子都有的处理问题(记得要删除原root节点)
还有就是修改的地方只管返回就是了,递归的话最终会返回到根上去,要注意的就是左右没有修改的地方还是用根的左右孩子接住,否则返回值不全。
修剪二叉搜索树
也不是很难嘛(掌握了逻辑)
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root) return NULL;
if(root->val < low){//如果它的值都比low小,那么它的左子更小
if(root->right){//右子还有些希望
root = root->right;
return trimBST(root, low, high);
}else return NULL;
}
if(root->val > high){//如果它的值都比high小,那么它的右子更大
if(root->left){//左子还有些希望
root = root->left;
return trimBST(root, low, high);
}else return NULL;
}else{
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
}
return root;
}
构造一颗二叉搜索树
TreeNode* solve(vector<int> nums, int begin, int end){
if(begin > end) return NULL;
int mid = (begin + end) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root->left = solve(nums, begin, mid-1);
root->right = solve(nums, mid+1, end);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
int s = nums.size();
if(s == 0) return NULL;
else return solve(nums, 0, s-1);
}
利用二叉搜索树的有序性,我们可以发现它比之前的构造二叉树要简单了一倍!
把二叉搜索树转换为累加树
我知道顺序是右中左,但是因为肚子饿了,想不出来啦~
需要用pre记录前一个结点的值诶,这个我知道的,如果吃饱饱并且有精神的话绝对迎刃而解(都知道遍历顺序了就没有题目可以难倒我)
int pre;
void solve(TreeNode* cur){
if(!cur) return;
solve(cur->right);
cur->val += pre;
pre = cur->val;
solve(cur->left);
}
TreeNode* convertBST(TreeNode* root) {
pre = 0;
solve(root);
return root;
}
棒棒哒!次饭饭去~最近很能吃是真@_@