代码随想录训练营18day-二叉树7

一、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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/813667.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARMv8-A架构下的外部debug模型之外部调试事件(external debug events)概述

外部调试器与处理器之间的握手与external debug events 一&#xff0c;External Debug的使能二&#xff0c;外部调试器和CPU之间的握手三&#xff0c;外部调试事件 External debug events1. External debug request event2. Halt instruction debug event3. Halting step debug…

docker部署安装整理

centos下安装部署docker 在CentOS下部署Docker&#xff0c;你需要按照以下步骤进行操作&#xff1a; 更新系统&#xff1a; 首先&#xff0c;确保你的CentOS系统是最新的。打开终端&#xff0c;并运行以下命令来更新你的系统&#xff1a; sudo yum update -y安装所需的软件包…

[C++/Linux] UNIX域函数

目录 一.什么是UNIX域套接字&#xff1f; 二.如何使用UNIX域函数进行套接字编程&#xff1f; 三.利用socketpair函数进行文件描述符传递 3.1 socketpair函数 3.2 实例 3.3 补充消息结构知识 一.什么是UNIX域套接字&#xff1f; Unix域套接字&#xff08;Unix Domain Socke…

程序“猿”高阶函数

高阶函数是函数式编程的一个核心概念&#xff0c;它提供了强大的抽象能力&#xff0c;使得代码更加简洁和模块化。正如你所提到的例子&#xff0c;高阶函数可以接受其他函数作为参数&#xff0c;或者返回一个函数。这种特性让它们在处理列表操作、事件处理、异步编程等场景中非…

【力扣一刷】代码随想录day38(动态规划part1:509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼)

目录 【动态规划理论基础】 【509. 斐波那契数】简单题 方法一 用额外的数组存储每个状态 方法二 用2个遍历存储前两个状态&#xff08;减小空间复杂度&#xff09; 【70. 爬楼梯】简单题 【746. 使用最小花费爬楼】简单题 【动态规划理论基础】 1、定义&#xff1a;英…

代码随想录算法训练营第四十二天|leetcode121、122题

一、leetcode第121题 本题要求买卖股票一次获取最大利润&#xff0c;设置dp数组&#xff0c;其中dp[i][0]的含义是第i天持有股票的最大利润&#xff0c;dp[i][1]的含义是第i天不持有股票的最大利润&#xff0c;可得递推公式为dp[i][0]max(dp[i-1][0],-prices[i])&#xff0c;d…

A15 STM32_HAL库函数 之 FLASH扩展驱动 所有函数的介绍及使用

A15 STM32_HAL库函数 之 FLASH扩展驱动 所有函数的介绍及使用 1 FLASH扩展驱动 预览1.1 HAL_FLASHEx_Erase1.2 HAL_FLASHEx_Erase_IT1.3 HAL_FLASHEx_OBErase1.4 HAL_FLASHEx_OBProgram1.5 HAL_FLASHEx_OBGetConfig1.6 HAL_FLASHEx_OBGetUserData 该文档修改记录&#xff1a;总…

【从浅学到熟知Linux】环境变量详谈(含使用程序获取环境变量的3种方法、如何查看环境变量)

&#x1f3e0;关于专栏&#xff1a;Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程及数据库等内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 环境变量基本概念查看环境变量的方法环境变量相关命令环境变量组织方式及获取环境变量的3种方法验…

Cesium.js--》探秘Cesium背后的3D模型魔力—加载纽约模型

今天简单实现一个Cesium.js的小Demo&#xff0c;加强自己对Cesium知识的掌握与学习&#xff0c;先简单对这个开源库进行一个简单的介绍吧&#xff01; Cesium 是一个开源的地理空间可视化引擎&#xff0c;用于创建基于 Web 的三维地球应用程序。它允许开发人员在网页上呈现高度…

Java基础第十一课——类与对象(2)

由于类与对象这一部分的知识点很多&#xff0c;而且操作方法也有很多&#xff0c;所以这次将继续深入讨论一下关于类与对象中方法传参、方法重载、构造方法以及this关键字使用方面的知识。 一、方法传参 1.return关键字 return关键字作用 作用场景&#xff1a;方法内 作用…

天猫精灵要会员,不能听歌,还能用来干什么呢?榨干它的剩余价值

目录 起因&#xff1a;以听歌为主要功能的设备&#xff0c;却不能听歌了 1.蓝牙音箱 2.控制智能家电 3.万能遥控器&#xff0c;需要一个外接设备 4.倒计时/提醒&#xff0c;闹钟提醒&#xff0c;整点提醒&#xff08;这功能有人不喜欢&#xff0c;闲吵&#xff0c;还不能关…

LeetCode题练习与总结:最小路径和--64

一、题目描述 给定一个包含非负整数的 m x n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[4,2,1]] 输出…

UI设计规范

一套商城系统的诞生&#xff0c;除了代码的编写&#xff0c;UI设计也至关重要。UI设计关系到商城系统的最终呈现效果&#xff0c;关乎整体商城的风格展现&#xff0c;如果UI设计做不好&#xff0c;带来的负面影响也是不容小觑的。 1、在很多商城系统开发中&#xff0c;有时会有…

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建表模拟…

运放噪声评估的来龙去脉

运放噪声评估的来龙去脉 友情提示&#xff0c;运放电路的噪声分析还是比较复杂的&#xff0c;不论是基础理论还是对应的推导过程&#xff0c;都不是特别容易。考虑到兄弟们的基础参差不齐&#xff0c;所以我还是尽量说清楚点&#xff0c;这样导致看起来就有点罗里吧嗦&#xff…

10 Php学习:循环

在 PHP 中&#xff0c;提供了下列循环语句&#xff1a; while - 只要指定的条件成立&#xff0c;则循环执行代码块do…while - 首先执行一次代码块&#xff0c;然后在指定的条件成立时重复这个循环for - 循环执行代码块指定的次数foreach - 根据数组中每个元素来循环代码块 当…

【Java EE】获取Cookie和Session

文章目录 &#x1f38d;Cookie简介&#x1f340;理解Session&#x1f333;Cookie 和 Session 的区别&#x1f332;获取Cookie&#x1f338;传统获取Cookie&#x1f338;简洁获取Cookie &#x1f334;获取Session&#x1f338;Session存储&#x1f338;Session读取&#x1f33b;…

Fence同步

在《Android图形显示系统》没有介绍到帧同步的相关概念&#xff0c;这里简单介绍补充一下。 在图形显示系统中&#xff0c;图形缓存GraphicBuffer可以被不同的硬件来访问&#xff0c;如CPU、GPU、HWC都可以对缓存进行读写&#xff0c;如果同时对图形缓存进行操作&#xff0c;有…

mysql8.0高可用集群架构实战

MySQL :: MySQL Shell 8.0 :: 7 MySQL InnoDB Cluster 基本概述 InnoDB Cluster是MySQL官方实现高可用读写分离的架构方案,其中包含以下组件 MySQL Group Replication,简称MGR,是MySQL的主从同步高可用方案,包括数据同步及角色选举Mysql Shell 是InnoDB Cluster的管理工具,用…

java项目之校园兼职系统(ssm框架+mysql数据库+文档)

项目简介 校园兼职系统的主要使用者分为&#xff1a;管理员&#xff1a;首页、个人中心、专业管理、商家管理、热门兼职管理、学生管理、兼职接单管理、学生咨询管理、兼职任务管理、完成评价管理、管理员管理、系统管理等模块信息的查看及相应操作&#xff1b;学生&#xff1…