目录
513.找树左下角的值
题目描述
解题思路
找深度最大的叶子节点
再用前中后序遍历(只要保证逻辑左在右的前面就可以,中只是个形式因为最左下角的值要么是左侧要么是右侧,后续是左右中)
代码
全局变量:
目前最大深度(maxDepth)
结果:记录当前节点的数值(result)(如果比目前最大深度大则更新result)
1. 方法传参:遍历(当前节点,深度)
2. 终止条件:当前节点为空则return
3. 单层递归的逻辑:
遍历到叶子节点(左孩子和右孩子都等于空)
【如果当前深度大于目前最大深度则更新maxDepth和result(这样才能使遍历完之后result存的是深度最大的叶子结点的值)】
【左在右前面】如果左节点不为空(否则操作空指针)则深度加加,再向左遍历(递归),递归完需要回溯到上层(也就是深度减减)然后再深度加加,再向右遍历(递归),递归完依然需要回溯到上层(也就是深度减减)
不需要写中的逻辑也是在于只要保证逻辑左在右的前面就可以,中只是个形式
depth++;
traversal(root-> left, depth);
depth--;
可以简化成traversal(root-> left, depth+1);
class Solution {
// 全局变量:最大深度和目标值
int maxDepth = -1;
int result = 0;
public int findBottomLeftValue(TreeNode root) {
findMaxDepthLeftNode(root, 0);
return result;
}
private void findMaxDepthLeftNode(TreeNode cur, int depth) {
// 终止条件:如果当前节点为空值则return
if (cur == null) return;
// 单层递归逻辑:判断是否是叶子节点(左右子节点都为空),如果是叶子结点,当前深度大于最大深度则更新,以及更新节点所对应的值
if (cur.left == null && cur.right == null) {
if (depth > maxDepth) {
maxDepth = depth;
result = cur.val;
}
}
// 单层递归逻辑:进行遍历(只要左子树遍历在右之前就可以,因为不需要中序遍历,只要找子节点)
// 排除空指针问题
// findMaxDepthLeftNode(cur.left, depth + 1) 由于递归回溯【每次递归调用中将当前深度加一,但不改变原有 depth 的值】其实等于
// depth ++
// findMaxDepthLeftNode(cur.left, depth)
// depth --
if (cur.left != null) findMaxDepthLeftNode(cur.left, depth + 1);
// 不能else if,因为遍历左子树和右子树独立,否则逻辑变成只有左子树不存在才会遍历右子树
if (cur.right != null) findMaxDepthLeftNode(cur.right, depth + 1);
}
}
209.长度最小的子数组
题目描述
解题思路
滑动窗口:一个for循环做两个for循环的任务(循环的索引,一定是表示滑动窗口的终止位置)
什么时候移动起始位置:当发现当前滑动窗口长度>=s则移动起始位置
引用代码随想录该题的思路动图,很清晰
滑动窗口主要考虑三点:
1. 窗口内是什么:满足和大于等于s的最小长度的连续子数组
2. 终止位置:窗口结束位置也就是遍历数组的指针(for循环)
3. 起始位置:如果当前的和s大于s则需要移动了(删除“左边”的元素以缩小窗口)
代码
全局变量:
start(窗口起始指针)、sum(记录求和)、result(记录当前最优子数组长度)【定义为最大值用来向下更新】
for循环由于是控制终止位置指针(finish=0, finish <= nums.length, finish++)
【每次循环求和sum += nums[finish],当求和【这里应该是while而不是if因为需要达到一个窗口持续移动的过程,否则会导致有可能缩减了1个长度并没有达到最优解】sum >= target则需要知晓对应的长度(也就是subLength = finish-start+1)以及更新最优子数组长度(result = min(result, subLength))。此时因为sum>=target说明需要缩小窗口因此sum -= nums[start](也就是删掉起始位置【右边】的元素),再把起始位置指针移动(start++)】
因为有可能无解所以需要return
return result == Integer.MAX_VALUE ? 0 : result;
class Solution {
public int minSubArrayLen(int target, int[] nums) {
// 全局变量:
// 窗口起始指针
int start = 0;
// 记录当前求和
int sum = 0;
// 记录最小长度(因为有可能不存在所以需要赋值一个无限大值)
int result = Integer.MAX_VALUE;
// 遍历窗口终止指针
for (int finish = 0; finish < nums.length; finish++) {
// 当前求和
sum += nums[finish];
while (sum >= target) {
// 满足窗口内条件则计算窗口长度
int subLength = finish - start + 1;
result = Math.min(subLength, result);
// 移动窗口起始指针以缩小窗口
sum -= nums[start];
start++;
}
}
// 因为有可能不存在答案因此要判断
return result == Integer.MAX_VALUE ? 0 : result;
}
}
24. 两两交换链表中的节点
题目描述
解题思路
交换的是指针而不是节点里的值
用dummyHead做头结点
指针需要指向需要反转的两个节点的前一个节点的上一个节点(方便改变指向)
例如1->2->3那么加在1的前面dummyHead->1->2->3然后变成dummyHead->2->1->3
交换链表什么时候终止?什么时候该交换节点?
引用代码随想录该题的思路图,很清晰
代码
加入dummyHead做头结点(dummyHead.next = head)
让当前节点也用dummyHead赋值(cur = dummyHead)
终止条件:
如果节点数是偶数,则当前节点的下一个为空;为奇数,则当前节点的下一个的下一个为空;(while (cur.next != null && cur.next.next != null))【因为这两个情况“或”满足则终止,因此反过来while后面跟的是循环成立的情况,补集则是&&然后两个==变成!=】
交换节点的逻辑:
第一步,首先需要把dummyHead指向节点2(cur.next = cur.next.next)
第二步,需要把节点2指向节点1。如果没有第一步则节点1对应cur.next,但因为第一步所以导致cur.next变节点2了,因此需要在第一步之前用一个临时节点接住节点1对应的指针(temp1 = cur.next)否则这回操作节点1并不是cur.next了
第三步,需要把节点1指向节点3。(temp1.next = temp2)同理因为第二步节点2指向节点1了。因此节点3的被指向断了,因此需要在第一步之前用temp2保存节点3的指针(temp2 = cur.next.next.next)
然后把cur往后移动两位(因为交换两个因此需要移动到需要交换的后一个节点的位置)(cur = cur.next.next)
最后return 头结点(dummyhead.next)
class Solution {
public ListNode swapPairs(ListNode head) {
// 新建一个虚拟头结点
ListNode dummyHead = new ListNode(-1);
// 把虚拟头结点的下一个设置为头结点
dummyHead.next = head;
// 把当前指针设置为虚拟头结点,这样才能方便操控后面的节点的交换
ListNode cur = dummyHead;
// 设置终止条件:节点奇数个时,下下个节点为空;偶数个时,下个节点为空
// 补集因为while后是循环成立条件因此||变成&&以及==变成!=
while (cur.next != null && cur.next.next != null) {
// 交换节点的逻辑
// 用临时节点把节点1、2、3保存。否则在后续next指针改变的时候会丢失原先的指针
// [这个暂存指针的三行代码必须在检查了cur.next以及cur.next.next都不是空值之后才能写,也就是不能写在循环外面,否则还是会报空指针错误]
ListNode temp1 = cur.next;
ListNode temp2 = cur.next.next;
ListNode temp3 = cur.next.next.next;
// 第一步:dummyHead指向节点2
cur.next = temp2;
// 第二步:节点2指向节点1
temp2.next = temp1;
// 第三步:节点1指向节点3
temp1.next = temp3;
// 移动cur指针进而帮助下一对节点交换(因为帮助了两个节点交换,也就是移动到需要交换的后一个节点)
cur = cur.next.next;
}
// 返回头结点(dummyHead.next而不是dummyHead)
return dummyHead.next;
}
}