- 博客(31)
- 收藏
- 关注
原创 450.删除二叉搜索树中的节点
右子树最左边的节点是右子树最小值,因此将左子树根节点放在右子树最左边节点的左孩子节点位置即可(左右子树逻辑一样,处理一种即可):根据目标值不断向左或向右递归,找到目标节点(或者最后没找到)之后对节点进行删除操作。根据二叉搜索树的性质,右子树每个节点的值都比该目标节点值大,也大于左子树的根节点。还是跟上题一样,通过返回值接收来达到父节点与孩子节点(或者树)之间的绑定关系。依然使用递归函数的返回值来完成把节点从二叉树中移除的操作。有以下五种情况:(这些都算是终止条件里的操作)
2025-04-20 17:53:10
141
原创 701.二叉搜索树中的插入操作
如果val比当前节点大,就递归进入到右子树,反之,递归进入到左子树,当进入到叶子节点时就可以插入新建的节点。利用二叉搜索树的有序性,以及插入的节点都可以在叶子节点上来写。绑定当前节点与新建节点之间的关系。
2025-04-20 16:11:36
95
原创 236. 二叉树的最近公共祖先与235. 二叉搜索树的最近公共祖先
细节很多,这里我是设置全局变量后两次调用方法,就会对同一个全局变量fatherList造成影响。本题我的思路是,1、分别找到给定的两个节点的父节点集合,2、在两个父节点集合中,按照短的集合的长度从后往前找。我的方法并不是很简单,比较粗暴直接。
2025-04-19 22:47:11
179
原创 530. 二叉搜索树的最小绝对差与501. 二叉搜索树中的众数
接着利用map找到出现次数最多的数字 即众数---->1找到最大的 value 2收集所有 value 等于 maxValue 的 key 3转为 int[]利用双指针不断在中序遍历的过程中更新最大值以及最大值集合:双指针表示的是前一个节点和后一个节点。二叉搜索树想到中序遍历,在中序遍历的过程中---即在中这个步骤查找最小值(中序遍历严格增)指针记录前一个访问的节点,以便比较当前节点值是否与前一个相同。定义一个全局的map,用于在中序遍历过程中收集数字和频率。,说明当前值也是一个众数,加入列表。
2025-04-18 19:06:50
276
原创 98. 验证二叉搜索树
因为不能满足二叉搜索树定义:节点的左子树只包含小于当前节点的数。节点的右子树只包含大于 当前节点的数。原来写的只满足了根、左孩子、右孩子之间的限定关系,而不是满足根,左子树,右子树之间的关系。这是二叉搜索树的核心性质之一。既然这个二叉搜索树是中序遍历单调递增,那么我们在可以在中序遍历的途中对是否单调进行判断、也就是把判断逻辑写在“中”的位置。类似数组双指针,一个表示前一个节点,另外一个表示后一个节点,这两个节点单调增。本来写的是比较根节点和左右节点的大小,但是这样写是错误的,
2025-04-17 23:56:37
124
原创 617、合并二叉树
第一,在分情况讨论时,我遇到空的时候new了新的节点直到root1 root2都为空才返回,导致复杂度高,下面这种方法更直接,更省事。本题思路:终止条件看似是分成三种情况,实际两种就可以,因为是同步遍历,一个为空,return另外一个就可以。第二: 可以不创建新树,直接在root1树更改,降低空间复杂度。参数就是两个节点,返回值是新生成树的根节点。单层逻辑:节点之和相加就可以,
2025-04-17 21:02:18
91
原创 654、最大二叉树
单层递归逻辑就是找最大值以及最大值的位置,最大值位置作为参数传入用于拆分数组,形成左右两个子树。单层递归逻辑就是找最大值以及最大值的位置用于拆分数组,形成左右两个子树。终止条件就是比较左右边界的值:(注意左闭右闭和左闭右开的区别)。方法一:自己写的部分:(这个是不断改变数组,不断传入新的数组)方法二 :数组不变,改变左右边界,不断传入的是新的边界。参数是数组和左右边界,返回值是根节点。这个的思路也很简单,跟上一题很类似。参数是数组,返回值是根节点。上:左闭右开 下:左闭右闭。终止条件就是数组为空。
2025-04-17 19:56:26
120
原创 106.从中序与后序遍历序列构造二叉树
如果中序遍历和后序遍历有相同的值,还需要进行多一步的判断,判断中序遍历中的值是否就是要找的根节点:下面是我的写法(复杂度略高)。在这里需要注意Java中的引用数据类型的传递方式,直接进行排序会对原数组造成影响。3、根据中序中分割完的左右子树对后序遍历进行分割,也分割为分为左右子树。2、根据根节点先对中序遍历(左中右)进行分割分为左右子树。1、从后序遍历(左右中)找根节点(最后一个元素)参数为中序遍历和后序遍历的数组,递归处理。终止条件:数组为空,反之返回根节点。下面也即递归中的单层处理逻辑。
2025-04-17 16:22:06
107
原创 112. 路径总和
计算从根节点到叶子节点路径上的数值之和,递归的终止条件就是叶子节点(与上面一个题的第一个区别是:上面一个题必须有一个节点,而这个题根节点可能为空,所以需要判断一下)明白在递归中类成员变量和方法变量的区别,比如:如果将sum置为全局变量,就需要在递归的过程中进行回溯(这也是刚开始出错的原因)。回溯是必要的:如果必须用成员变量,需要在递归返回时撤销修改(如 sum -= root.val),但会增加复杂度。成员变量在递归中慎用:递归应依赖参数和返回值传递状态,而非修改成员变量。
2025-04-16 21:51:13
118
原创 513. 找树左下角的值
--那么如何找最左边的呢?可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。思路误区:并不是一直向左遍历就可以找到左下角的值,因为要求是最后一层最左边,左下角的值有可能是右边的叶子节点 ----> 深度最大的叶子节点一定是最后一行。思路一: 迭代法:层序遍历简单一点 对其进行层序遍历,记住每一层最左边的节点的value值(也就是每一层第一个弹出的节点),下一层对上一层进行覆盖。
2025-04-16 19:33:32
126
原创 Day03运费微服务--运费模板表的实现
请求 → Controller → Service → Mapper → DB。响应 ← DTO ← Entity ← SQL结果。免写简单SQL,支持Lambda条件构造器。@TableName指定表名。直接映射数据库表,可能包含敏感字段。继承BaseEntity。继承BaseMapper。调用Mapper操作DB。Entity→DTO转换。:处理HTTP协议,参数校验。root((运费管理模块))Controller层。:数据库交互,SQL抽象。封装常见CRUD操作。
2025-03-29 23:30:57
244
原创 Day02用户端登录与统一网关
在这里出错了,Swagger的界面打不开,检查发现是因为配置文件yaml格式没弄好,已改正。child2: value # 2空格 ❌ 缩进不一致。child1: value # 4空格。-id: service2 # -后缺少空格。age : 30 # ❌ 冒号前不应有空格。id: service1 # 缺少 -key : value # 冒号前不应有空格。id: service1 # 缺少 -name:John # ❌ 冒号后无空格。key:value # 冒号后无空格。
2025-03-28 18:07:47
330
原创 Day01权限服务中心
在项目实现的过程中,可以将这套思想应用上,定义一个服务中心,实现登录、根据id查找,查看角色等基本的功能。下面的代码部分展示了本项目中权限管理中心的部分功能。将共同的业务代码逻辑抽离出来,进行统一的操作管理。
2025-03-24 17:18:02
133
原创 Nginx 反向代理却打不开页面//记录idea中常见错误
配置并检查了半天,最后发现是梯子没关,梯子关了就进去了。所以习惯挂梯子的以后要看看梯子是不是挂着🌚。如果看到 Gogs 的页面,说明 Nginx 反向代理配置成功。如果 Nginx 配置正确,请求会被转发到。,说明 Nginx 正在监听 80 端口。,说明 Nginx 正在运行。(通常是 Gogs 服务)。
2025-03-20 19:55:45
224
原创 257. 二叉树的所有路径
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。后序显然不妥,找到孩子节点了还得找父节点。这道题目,如图,找到第一条路径之后还得返回到父节点去找第二条路径,涉及到回溯算法。
2025-03-19 00:35:30
151
原创 110.平衡二叉树
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。当然是其左子树高度和其右子树高度的差值。如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0。所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。参数:当前传入节点。返回值:以当前传入节点为根节点的树的高度。是指该树所有节点的左右子树的高度相差不超过 1。
2025-03-18 15:31:18
243
原创 222.完全二叉树的节点个数
最后一步:确定单层递归的逻辑:先求它的左子树的节点数量,再求右子树的节点数量,最后取总和再加一 (加1是因为算上当前中间节点)就是目前节点为根节点的节点数量。对于情况二,分别递归左孩子,和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。这道题目的递归法和求二叉树的深度写法类似, 而迭代法,遍历模板稍稍修改一下,记录遍历的节点数量就可以了。完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。三部曲前两部暂时省略。方法一:普通二叉树的求法。
2025-03-17 22:02:48
155
原创 111.二叉树的最小深度(递归法和层序遍历的迭代法)
思路确实跟二叉树的最大深度很像,但是一提交发现出错了。当我们把最大深度的Math.max()转换为Math.min()就会发现出错了,原因是最小深度是从根节点到的最短路径上的节点数量,注意是,左右孩子都没有才叫叶子节点。本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个。
2025-03-17 15:25:15
144
原创 104.二叉树的最大深度 559.n叉树的最大深度
首先明确两个定义:深度和高度从该定义可以很明显看出,深度从上往下(从根节点开始),适合用前序遍历:中左右,高度从下往上(从叶子节点开始数),适合后序遍历:左右中。本题欲求最大深度,即指从根节点到最远叶子节点的最长路径上的节点数。此时,。所以采用的方法是根节点的后序遍历。不用前序遍历是因为前序遍历略复杂。
2025-03-17 14:24:48
208
原创 101. 对称二叉树
以上图为例,从第二层开始比较,比较左节点的左节点和右节点的右节点,以及左节点的右节点和右节点的左节点。因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。要通过递归函数的返回值来判断两个子树的内侧节点和外侧节点是否相等。一层一层向根节点return比较内外侧节点的返回值。此时才进入单层递归的逻辑,单层递归的逻辑就是处理左右节点都不为空,且数值相同的情况。把以上情况都排除之后,剩下的就是左右节点都不为空,且数值相同的情况。
2025-03-12 23:27:19
142
原创 226.翻转二叉树
深度遍历:(这里的翻转操作位置就相当于遍历中的根节点的位置,即 中左右,左右中里面的中)前序后序的递归遍历法,中序也可以,但是注意,在中序处理完左子树后实现翻转,再去处理右子树时相当于又把左子树处理一遍。观察题目很明显可以知道是遍历的过程中交换左右两个孩子节点。层次遍历:将节点加入到队列前实现交换。方法思路:遍历的套路+交换的套路。遍历用深度遍历和层次遍历都可以用。
2025-03-12 16:57:08
128
原创 102、二叉树的层序遍历 107.二叉树的层次遍历II 199. 二叉树的右视图637.二叉树的层平均值
为了降低在结果列表的头部添加一层节点值的列表的时间复杂度,结果列表可以使用链表的结构,在链表头部添加一层节点值的列表的时间复杂度是 O(1)。:用队列来保存遍历的二叉树node,同时用一个变量来记录每层的孩子节点(根节点也可以一起处理),从而可以判断出哪些是属于同一层的node,加入同一个集合中,最后进行循环即可。本来思考用层深与节点个数的关系是否能解决,但是无法很好的确定各种类型的孩子数量,所以这个想法排除(满二叉树应该可以用)。方法二:在遍历完一层节点之后,将存储该层节点值的列表添加到结果列表的头部。
2025-03-11 18:06:07
202
原创 144、94、145二叉树的前中后序遍历(迭代法)
然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。用也可以实现二叉树的前后中序遍历前序遍历是中左右,遍历访问的顺序与处理的顺序是一致的:每次先处理的是中间节点,通过栈pop出来的中找左右孩子。顺序:先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子(为什么要先加入右孩子,再加入左孩子呢?因为这样出栈的时候才是中左右的顺序)。
2025-03-11 00:29:00
405
原创 144、94、145二叉树的前中后序遍历(递归法)
确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。需要给出终止条件退出递归,不然退不出来,会遇到栈溢出的错误。一般该终止条件就写在方法下面,一调用方法就先判断是否满足条件,是否返回。有些是在进递归函数的过程中进行处理,有些是在出递归函数的过程中进行处理。一个大问题可以分为很多个重复相似的子问题,比如:阶乘、两两交换链表中的节点。递归算法的三个要素:(个人理解先判断终止条件)确定每一层递归需要处理的信息。
2025-03-09 23:38:42
217
原创 239. 滑动窗口最大值
大顶堆只能找到弹出最大值,但是对于滑动窗口移动的过程中,移出滑动窗口的元素无法很好的定位,如果用remove遍历,时间复杂度又提升了。前k个高频元素,同:找大值,异:后者只需要高频元素,不需要对位置关系有一定的了解;单调队列的实现,nums 中的每个元素最多也就被 add 和 pop各一次,没有任何多余操作,所以整体的复杂度还是 O(n)。只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的。该单调队列的作用是在滑动窗口移动的过程中告诉我们最大值是什么。队头位置即为最大值。
2025-03-09 21:15:50
162
原创 347.前 K 个高频元素
时间复杂度:将 Map 转换为 List :O(n),其中 n 是 Map 的大小。其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。因为要统计最大前k个元素,只有小顶堆每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。空间复杂度: 需要额外的 List 存储 entrySet,空间复杂度为 O(n)。:遍历 Map :O(n),维护堆:O(nlogk),其中 k 是堆的大小。总时间复杂度 :O(nlogk)。
2025-03-02 00:01:06
171
原创 150. 逆波兰表达式求值
4 + 13 / 5,这就是中缀表达式,计算机从左到右去扫描的话,扫到13,还要判断13后面是什么运算符,还要比较一下优先级,然后13还和后面的5做运算,做完运算之后,还要向前回退到 4 的位置,继续做加法。本题思路:用栈来实现,遇到数字放入栈中,遇到符号则弹出两个数字(字符串转化为数字)进行运算,运算结果压回栈中继续下一步的运算,最后弹出结果。注意点:1、java需要注意的是:由于内置jdk的原因,2、遇到 "-" 和 "/" 注意弹出的数字的顺序。比较的是对象的引用(内存地址),而。
2025-02-28 22:40:04
94
原创 1047. 删除字符串中的所有相邻重复项
双指针思想:快的指针fast用来遍历数组或者字符串,慢指针slow指向正在处理的位置,并且slow过的地方满足条件(比如达到去重要求,边移动指针边赋值)相对于20. 有效的括号来说其实也是匹配问题,20. 有效的括号 是匹配左右括号,本题是匹配相邻元素,最后都是做消除的操作。的问题,如有序数组去重或删除字符串中的相邻重复项,双指针的局限性在于无法回溯未知的情况,比如:{[() ]()}等情况。有效的括号有嵌套问题,而双指针适用于。本题中,消除相邻重复项,双指针可以指向确定的情况并单向遍历。
2025-02-28 12:20:35
93
原创 20. 有效的括号
方法一:遇到左括号、左中括号和左大括号都push入栈,遇到右(/中/大)括号时,判断栈顶元素与该括号是否匹配。在匹配左括号的时候,将与左括号对应的右括号入栈,遍历到右括号时,就只需要比较当前元素和栈顶相不相等就可以了。栈的典型应用:编译器在词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。:最坏情况下,每次替换只能消除一对括号,因此时间复杂度为 O(n2),其中 n 是字符串的长度。方法三:消除法:如果匹配,那么一定会出现成对的匹配的括号对。:O(1),只使用了常量空间。
2025-02-28 00:07:07
122
原创 225. 用队列实现栈
注意点:peek()的时候没有弹出只返回值,需要将peek()的元素弹出并入队(一个队列重新入队,两个队列在备用队列上重新入队),然后再计算队列长度进行实现。关于复杂度:时间复杂度: pop为O(n),top为O(n),其他为O(1);题目写的是两个队列,实际上一个队列就可以,思路一样,只不过是需要new几个队列的区别。思路:1、出队列的元素再返回到入队处重新进行入队,将后面的元素先弹出去。2、弹出的个数为队列中的元素个数减一:queue.size()-1。和上一个题一样,可以考虑代码的复用。
2025-02-26 23:56:52
130
原创 用栈实现队列
思路很好想:一个栈(栈1)用于入队(其对应的出栈进入到栈2中),一个栈(栈2)用于出队(栈2的入栈对应栈1的出栈),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。和pop()函数功能类似,代码实现上也是类似的---->函数的复用。在push数据的时候,只要数据放进输入栈就好。这个主要是帮助初学者理解栈、队列。如何判断队列为空呢?
2025-02-25 23:22:25
192
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人