一、530.二叉搜索树的最小绝对差
利用二叉搜索树的有序性,每一层遍历时候,最小差一定是在相邻的两个节点间产生的,因此做递归的时候,记录一个pre和cur节点,用来比较差值,迭代更新时候,记录最小值。二叉搜索树中序遍历,能有效利用有序性。
int result;
struct TreeNode* pre;
void trans(struct TreeNode* root)
{if(root == NULL){return;}/**左子节点**/trans(root->left);//取值判断if(pre){result = (root->val - pre->val) < result? root->val - pre->val : result;}pre = root;//右子节点trans(root->right);
} int getMinimumDifference(struct TreeNode* root) {pre = NULL;result = INT_MAX;trans(root);return result;
}
迭代法:根据之前的中序遍历,进行修改:这里也需要记录pre和cur节点,方便进行比较最小差值。
int getMinimumDifference(struct TreeNode* root) {// pre = NULL;// result = INT_MAX;// trans(root);// return result;//迭代法struct TreeNode** nodeStk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);int top = -1;int result = INT_MAX;struct TreeNode* cur = root;struct TreeNode* pre = NULL;while(cur || top > -1){if(cur)//当前左边一直有节点{nodeStk[++top] = cur;cur = cur->left;}else{cur = nodeStk[top];//遇到空节点了,pop出来叶子节点top--;if(pre){result = (cur->val - pre->val) < result? cur->val - pre->val : result;}pre = cur;//更新上一个节点cur = cur->right;//更新cur节点}}return result;
}
二、501.二叉搜索树中的众数
本题的二叉搜索树:
- 结点左子树中所含结点的值小于等于当前结点的值
- 结点右子树中所含结点的值大于等于当前结点的值
- 左子树和右子树都是二叉搜索树
众数可能不止一个,这点就需要注意,比如二叉搜索树众数为3 4,他们的节点个数都是5个,那么这两个值都会作为返回值。但此时如果再遍历,发现另一个数的次数为6,那么这个数就是最大的众数,前面保存的众数的数字会被清空,这一点需要注意!!!!
回退条件:1 pre为空 说明是根节点,次数(众数)为1;
2 如果pre的值是和cur的值一样,那么这个数的次数+1;
3 其他情况 次数为1;
递归: 先遍历左子树,
记录pre = cur
如果maxcount == count
如果 count > maxcount,需要清空之前的结果;
int maxcount;
int count;
int size;
struct TreeNode* pre;
int* result;
void trans(struct TreeNode* cur)
{if(cur == NULL){return;}trans(cur->left);//遍历左子树if(pre == NULL){count = 1;//说明到根节点 还没更新}else if(pre->val == cur->val){count++;}else{count = 1;}pre = cur;if(maxcount == count){//count = maxcount;result[size++] = cur->val;}if(count > maxcount){//清空resultmaxcount = count;size = 0;result[size++] = cur->val;}trans(cur->right);}
int* findMode(struct TreeNode* root, int* returnSize) {maxcount = 0;count = 0;size = 0;pre = NULL;result = (int*)malloc(sizeof(int) * 10001);trans(root);*returnSize = size;return result;
}
迭代方式,亦用栈的方式保存,类似中序遍历写法:
int* findMode(struct TreeNode* root, int* returnSize) {// maxcount = 0;// count = 0;// size = 0;// pre = NULL;// result = (int*)malloc(sizeof(int) * 10001);// trans(root);// *returnSize = size;// return result;//迭代法int* result = (int*)malloc(sizeof(int) * 10001);int maxcount = 0;int count = 0;*returnSize = 0;struct TreeNode* pre = NULL;struct TreeNode* cur = root;struct TreeNode** nodeStk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);int top = -1;while(cur || top > -1){if(cur){nodeStk[++top] = cur;cur = cur->left;}else{cur = nodeStk[top];top--;if(pre == NULL){count = 1;//说明到根节点 还没更新}else if(pre->val == cur->val){count++;}else{count = 1;}pre = cur;if(count == maxcount){result[*returnSize] = cur->val;(*returnSize)++;}if(count > maxcount){(*returnSize) = 0;//清空result[(*returnSize)++] = cur->val;maxcount = count;}cur = cur->right;}}return result;
}
三、236 二叉树的最近公共祖先
后序遍历可以回溯,递归函数返回值,可以作为判断:
1 递归的回退条件:root为空,即遇到空节点;root为p或者q,说明root递归到题目指定p或者q节点了,此时应该返回当前节点。
2 递归调用
3 判断祖先节点,如果递归回来的返回值都存在,那么root就是两个的公共祖先;
如果left为空,right不为空,那么返回right;
这里需要这样理解:二叉树节点数值是不重复的,而且一定存在 q 和 p。
但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {if(!root || root == p || root == q ){return root;}struct TreeNode* left = lowestCommonAncestor(root->left, p, q);struct TreeNode* right = lowestCommonAncestor(root->right, p, q);if(left && right){return root;}else if(left && !right){return left;}else if(!left && right){return right;}else{return NULL;}
}
四、701.二叉搜索树中的插入操作
插入操作需要根据插入值,去递归遍历,找到指定位置进行插入。
通过中序遍历,如果root为空,说明递归到空节点了,如果当前val大于当前节点值,那么插入到右边,否则左边。
struct TreeNode* insertIntoBST(struct TreeNode* root, int val) {if(root == NULL){struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));newNode->val = val;newNode->left = NULL;newNode->right = NULL;return newNode;}struct TreeNode* left, right;if(root->val > val){root->left = insertIntoBST(root->left, val);//把新创建的node给当前node的left}if(root->val < val){root->right = insertIntoBST(root->right, val);}return root;
}
通过一个parent和cur,记录之前的节点作为父节点,当遍历到可以插入的位置时候,进行赋值。
struct TreeNode* insertIntoBST(struct TreeNode* root, int val) {//迭代if(root == NULL){struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));newNode->val = val;newNode->left = NULL;newNode->right = NULL;return newNode;}struct TreeNode* parent = root;struct TreeNode* cur = root;while(cur){ parent = cur;if(cur->val > val){cur = cur->left;}else{cur = cur->right;}}struct TreeNode* newNode = (struct TreeNode*)malloc(sizeof(struct TreeNode));newNode->val = val;newNode->left = NULL;newNode->right = NULL;if(parent->val > val){parent->left = newNode;}else{parent->right = newNode;}return root;
}
五、450.删除二叉搜索树中的节点
删除节点需要考虑节点的位置,如果找到了key对应的节点
1 当前节点无后继节点,即它是叶子节点,直接删除;
2 如果当前节点无左子树,那么当前节点的右节点作为返回值;
3 如果当前节点无右子树,那么当前节点的左节点作为返回值;
4 就是左右子树都存在的情况,这个复杂一些:
1)把当前节点的左右子节点保存;
2)遍历当前节点右节点所在的右子树,找到最左边节点left;
3)把当前节点的左节点,挂到left的左孩子位置(见code)
struct TreeNode* deleteNode(struct TreeNode* root, int key){/**情况1 root为空**/if(root == NULL){return NULL;}if(root->val == key){//情况2 无子树if(!root->left && !root->right){//直接删除root,返回NULLfree(root);return NULL;}else if(!root->left){//情况3 left不存在 删除root,right作为新根结点struct TreeNode* tmp = root->right;free(root);return tmp;}else if(!root->right){//情况4 left不存在 删除root,right作为新根结点struct TreeNode* tmp = root->left;free(root);return tmp;}else{//情况5 //头结点的左右子树都存在//找到当前删除节点的右子树的最左边节点struct TreeNode* cur_left = root->left;struct TreeNode* cur_right = root->right;struct TreeNode* cur = root->right;//当前节点右子节点while(cur->left){cur = cur->left;}//删除节点的右子树的最左节点->left = curcur->left = cur_left;//删除root(当前节点)free(root);return cur_right;}}if(key < root->val)root->left = deleteNode(root->left, key);if(key > root->val)root->right = deleteNode(root->right, key);return root;
}
迭代方式:把左右子节点都存在的情况封装成一个函数;
1 首先找到等于key的节点和它的父节点;
2 如果pre父节点为空,说明是根节点,直接返回;
3 因为只是知道pre是父节点,但是cur是左还是或者还是右孩子,是不清楚的,因此需要判断,把cur节点记录,pre的节点处理,就和递归时候的左右孩子都在的情况一样进行处理。
struct TreeNode* deleteOneNode(struct TreeNode* cur)
{if(cur == NULL) return cur;if(!cur->right) return cur->left;//if(!cur->left) return cur->right;struct TreeNode* cur_left = cur->left;struct TreeNode* cur_right = cur->right;struct TreeNode* curNode = cur->right;//当前节点右子节点while(curNode->left){curNode = curNode->left;}//删除节点的右子树的最左节点->left = curcurNode->left = cur_left;//删除root(当前节点)free(cur);return cur_right;
}struct TreeNode* deleteNode(struct TreeNode* root, int key){
//迭代法if(root == NULL) return root;struct TreeNode* pre = NULL;struct TreeNode* cur = root;while(cur){if(cur->val == key){break;}pre = cur;if(cur->val > key) {cur = cur->left;}else{cur = cur->right;}}if(pre == NULL) {//只有头结点return deleteOneNode(cur);}//pre是当前的节点作为左或者右孩子的父节点if(pre->left && pre->left->val == key){pre->left = deleteOneNode(cur); }if(pre->right && pre->right->val == key){pre->right = deleteOneNode(cur); }return root;
}
六、669. 修剪二叉搜索树
这里需要注意一个点,利用返回值来移除节点。
1 首先找不符合的节点,当前节点值小于low,那么去右边子树找(右比左大),返回符合条件的节点;
2 递归左子树,返回的是左子树上符合条件的节点,当前节点左节点就是它。
struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {//递归if(!root) 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;
}
迭代法:1 节点移动到[low, high]区间;
2 如果当前节点的左节点小于low,说明是不符合条件的节点,需要删除此节点,将当前节点left用当前left右节点替代,并一直遍历下去,因为需要保证右节点所在的树,里面也没有不满足范围的值;
struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {//递归// if(!root) 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;//迭代法:if(!root) return NULL;//遍历到范围内的节点while(root &&(root->val < low || root->val > high)){if(root->val < low){root = root->right;//if值小于low,往右走}else{root = root->left;}}struct TreeNode* cur = root;//删除节点while(cur){while(cur->left &&(cur->left->val < low)){cur->left = cur->left->right;}cur = cur->left;}cur = root;while(cur){while(cur->right &&(cur->right->val > high)){cur->right = cur->right->left;}cur = cur->right;}return root;
}
七、108.将有序数组转换为二叉搜索树
这个题目需要注意区间的封闭原则,保证在处理过程中左闭右闭;
1 如果左闭右闭,那么left和right都能被取到,因此left 大于right才会退出;
2 递归时候,mid -1能被取到,mid + 1能被取到
struct TreeNode* trans(int* nums, int left, int right){/**左闭右闭原则**/if(left > right) return NULL;int mid = left + (right - left)/2;struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));root->val = nums[mid];root->left = NULL;root->right = NULL;root->left = trans(nums, left, mid - 1);root->right = trans(nums, mid + 1, right);return root;}
struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {if(numsSize == 0){return NULL;}return trans(nums, 0, numsSize - 1);//左闭右闭
}
迭代方式稍微复杂,需要使用3个数组,保存节点和左右边界。
八、把二叉搜索树转换为累加树
使用反中序遍历,需要记录pre和cur节点,这里可以只是结论pre的value,因为只是改变节点的值,首先遍历右节点,拿到当前节点的值,加上pre的值,更新当前值;然后pre的值更新成当前的value,这样每一次拿到节点后,pre上面存到的都是之前的累加值。
满足:1 更新当前值;
2 累加值加入到当前值;
int pre;void trans(struct TreeNode* cur){if(!cur)return ;trans(cur->right);cur->val += pre;//把上一个节点的值加到当前节点pre = cur->val;//更新当前节点值到pretrans(cur->left); }
struct TreeNode* convertBST(struct TreeNode* root) {pre = 0;//记录前一个节点的值if(root == NULL){return NULL;}trans(root);return root;
}
利用遍历写法,更新pre和cur的value。
struct TreeNode* convertBST(struct TreeNode* root) {//pre = 0;//记录前一个节点的值if(root == NULL){return NULL;}// trans(root);// return root;//迭代法struct TreeNode** stk = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);int top = -1;struct TreeNode* cur = root;//struct TreeNode* pre = NULL;int pre = 0;while(cur || top > -1){if(cur){stk[++top] = cur;//入栈cur = cur->right;}else{ cur = stk[top];top--;//出栈cur->val +=pre;pre = cur->val;cur = cur->left;}}return root;
}