目录
LeetCode669.修剪二叉搜索树
给你二叉搜索树的根节点
root
,同时给定最小边界low
和最大边界high
。通过修剪二叉搜索树,使得所有节点的值在[low, high]
中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
思路:这里利用二叉搜索树本身的有序性这个特性,将不再区间范围内的元素去除,最后便能返回最终结果。
1. 递归法
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return NULL;
if(root -> val < low){
//当值小于low时,在其右子树去寻找满足题意的结点
TreeNode* right = trimBST(root -> right, low, high);
return right;
}
if(root -> val > high){
//当其值大于high时,在其左子树中寻找满足题意的结点
TreeNode* left = trimBST(root -> left, low, high);
return left;
}
root -> left = trimBST(root -> left, low, high);
root -> right = trimBST(root -> right, low, high);
return root;
}
};
这是对上面代码的精化。
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return NULL;
if(root -> val < low) return trimBST(root -> right, low, high);
if(root -> val > high) return trimBST(root -> left, low, high);
root -> left = trimBST(root -> left, low, high);
root -> right = trimBST(root -> right, low, high);
return root;
}
2. 迭代法
采用迭代法的话,思路就是先处理根节点,然后处理左子树,接着最后是右子树。但是需要注意一些细节,否则容易出错。
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(root == NULL) return NULL;
//开始处理根结点,使其在[low,high]范围内
while(root != NULL &&(root -> val < low || root -> val > high)){
if(root -> val < low) root = root -> right;
else root = root -> left;
}
TreeNode* cur = root;
//开始处理左子树
while(cur != NULL){
while(cur -> left != NULL && cur -> left -> val < low){
//这里为什么不判断左子树的值是否大于high呢?
//因为之前已经找到了根节点,根节点范围在[low,high];
//所以左子树的值必然是在(xx,high)这样一个区间里面,二叉树是有序的
//下面也同理
cur -> left = cur -> left -> right;
}
cur = cur -> left;
}
cur = root;//将cur指向根结点
//开始处理右子树
while(cur != NULL){
while(cur -> right != NULL && cur -> right -> val > high){
cur -> right = cur -> right -> left;
}
cur = cur -> right;
}
return root;
}
LeetCode108.将有序数组转化为二叉搜索树
给你一个整数数组
nums
,其中元素已经按 升序 排列,请你将其转换为一棵平衡二叉搜索树。
思路:将有序数组转化为二叉搜索树,一般使用递归即可,生成左右子树,这里还介绍了一种迭代法,使用三个队列来模拟构造过程。
1. 递归法
1.1 创建数组
这里使用的是每次迭代时创建新的分割数组,根据分得的左右数组来继续构造树。
TreeNode* sortedArrayToBST(vector<int>& nums) {
if(nums.size() == 0) return NULL;//当数组的大小为空时,直接返回NULL
int mid = nums.size() / 2;//这里取数组的中间结点为根节点创建新的元素
TreeNode* root = new TreeNode(nums[mid]);
if(nums.size() == 1) return root;//当为叶子结点时直接返回
vector<int> left(nums.begin(), nums.begin() + mid);//划分左子树
vector<int> right(nums.begin() + mid + 1, nums.end());//划分右子树
root -> left = sortedArrayToBST(left);
root -> right = sortedArrayToBST(right);
return root;
}
1.2 下标索引
使用下标索引的话能够相对于上面创建数组能够节省时间和空间成本,但是需要注意中间结点的查找方式,否则会出现越界现象。
TreeNode* createBST(vector<int>& vec, int begin, int end){
if(end - begin == 0) return NULL;
int mid = begin + (end - begin) / 2;//注意这里的mid的求法,容易出错,始终记得这是使用的索引下标
TreeNode* root = new TreeNode(vec[mid]);
if(end - begin == 1) return root;
int leftBegin = begin;
int leftEnd = mid;
int rightBegin = mid + 1;
int rightEnd = end;
root -> left = createBST(vec, leftBegin, leftEnd);
root -> right = createBST(vec, rightBegin, rightEnd);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return createBST(nums, 0, nums.size());
}
2. 迭代法
迭代法采用三个队列模拟递归构造树的过程,一个队列存储遍历结点,一个队列存储左区间下标,一个队列存储右区间下标。需要注意的是在构造构成中需要能够使结点之间能够连接上,注意衔接。
TreeNode* sortedArrayToBST(vector<int>& nums) {
if(nums.size() == 0) return NULL;
TreeNode* root = new TreeNode(0);//创建根节点
queue<TreeNode*> nodeQue;//存储遍历结点
queue<int> leftQue;//存储左下标
queue<int> rightQue; //存储右下标
nodeQue.push(root);
leftQue.push(0);
rightQue.push(nums.size() - 1);
while(!nodeQue.empty()){
TreeNode* cur = nodeQue.front(); nodeQue.pop();
int left = leftQue.front(); leftQue.pop();
int right = rightQue.front(); rightQue.pop();
int mid = left + (right-left) / 2;
cur -> val = nums[mid]; //为中间结点赋值
if(left <= mid - 1){//处理左子树
cur -> left = new TreeNode(0);
nodeQue.push(cur -> left);
leftQue.push(left);
rightQue.push(mid - 1);
}
if(right >= mid + 1){//处理右子树
cur -> right = new TreeNode(0);
nodeQue.push(cur -> right);
leftQue.push(mid + 1);
rightQue.push(right);
}
}
return root;
}
LeetCode538.把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点
node
的新值等于原树中大于或等于node.val
的值之和。提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
思路:二叉搜索树本身通过中序遍历是有序的,这样的话,依据题意想要累加,那么就直接反中序遍历,使用pre记录累加值,采用右、中、左的顺序遍历结点,不断更新,最后能够将所有结点完成累加,转换成累加树。
1. 递归法
这里的递归法是很暴力的解法,先使用一个数组通过中序遍历将二叉搜索树的元素放入vector中,然后两层循环完成累加,更新对应位置元素,然后再中序遍历一下二叉搜索树,将累加值加入替换原来的值,最后返回根节点。
不建议使用这种方法,只是展示一下一般思路,因为这种方法太费时费空间了。
vector<int> vec;//存储结点值
int index = 0;//标记索引下标
void traversal(TreeNode* cur){
if(cur == NULL) return;
traversal(cur -> left);//左
vec.push_back(cur -> val);//中
traversal(cur -> right);//右
}
void traversal_replace(TreeNode* cur){
if(cur == NULL) return;
traversal_replace(cur -> left);//左
cur -> val = vec[index ++];//中
traversal_replace(cur -> right);//右
}
TreeNode* convertBST(TreeNode* root) {
if(root == NULL) return root;
traversal(root);
for(int i = 0; i < vec.size(); i ++){
for(int j = i + 1; j < vec.size(); j ++){
vec[i] += vec[j];//求各个结点的累加和
}
}
traversal_replace(root);
return root;
}
推荐使用下面的方法,使用pre记录累加值,反中序遍历二叉搜索树,完成累加,不断更新pre,最后返回根节点。
int pre = 0;//记录前面的累加值
void traversal(TreeNode* cur){
if(cur == NULL) return;
traversal(cur -> right);//右
cur -> val += pre;
pre = cur -> val;//中
traversal(cur -> left);//左
}
TreeNode* convertBST(TreeNode* root) {
if(root == NULL) return root;
traversal(root);
return root;
}
2. 迭代法
迭代法也就是采用了中序遍历的迭代遍历的方法,只不过也是反中序遍历的,思路和递归的一样。
TreeNode* convertBST(TreeNode* root) {
if(root == NULL) return root;
stack<TreeNode*> st;
TreeNode* cur = root;
int pre = 0;//记录累加值
while(cur != NULL || !st.empty()){
if(cur != NULL){
st.push(cur);
cur = cur -> right;//右
}else{
cur = st.top();//中
st.pop();
cur -> val += pre;//结点值进行累加
pre = cur -> val;//记录累加值
cur = cur -> left;//左
}
}
return root;
}
感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。
如果有什么问题欢迎评论区讨论!