代码随想录刷题复习day01

day01

数组-二分查找

class Solution {public int search(int[] nums, int target) {// 左闭右闭int left = 0;int right = nums.length - 1;int mid = 0;while (right >= left) {mid = left + (right - left) / 2;if (nums[mid] > target)right = mid - 1;else if (nums[mid] < target)left = mid + 1;elsereturn mid;}return -1;}
}

数组要用[]

数组长度nums.length

中文分号

1.当nums[mid] != target时,应该更改区间范围,当前这个mid不应该考虑了,因为当前这个mid已经没有等于
target的可能性了**(左闭右闭 的时候 right=mid不正确)**

2.考虑到算法逻辑上的合理性,在查找之前应该判断我们的目标元素在不在数组之中,不在的话直接返回-1,在的话才开始二分查找

// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
if (target < nums[0] || target > nums[nums.length - 1]) {
return -1;
}

3.(max- min) >> 1相当于(min + max) / 2

  if (nums[mid] > target)right = mid - 1;

链表-移除链表元素

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode removeElements(ListNode head, int val) {ListNode dummyhead=new ListNode(-1);dummyhead.next=head;ListNode cur=dummyhead;while(cur.next!=null) {if(cur.next.val!=val) cur=cur.next;else cur.next=cur.next.next;}return dummyhead.next;}
}

1.移除是 cur.next=cur.next.next;而不是cur=cur.next.next;

2.定义虚拟头节点dummyhead统一操作

3.终止条件cur.next!=null,一直往下走是一个持续的过程 用while

哈希表-有效字母异位词

定义一个存放26个小写字母的整数数组 int hash[26]

i=1,若索引1为字符’b’,则’b’-‘a’=1,则hash[1]++;hash索引1存放的是b出现的次数,最后比较数组 hash[26]时候
全0即可

class Solution {public boolean isAnagram(String s, String t) {int[] hash = new int[26];// 定义一个存放26个小写字母的整数数组hashif (s.length() == t.length()) {for (int i = 0; i < s.length(); i++) {hash[s.charAt(i) - 'a']++;hash[t.charAt(i) - 'a']--;}}else return false;for (int i = 0; i < 26; i++) {if (hash[i] != 0) {return false;}}return true;}
}

String类相关的API
s.charAt(i) 返回指定索引处char值
s.length() 返回字符串长度

字符串-反转字符串

取巧 调用库函数

将字符数字遍历使用StringBuilder拼接 然后reverse 再调用 s[i] = sb.charAt(i);

class Solution {public void reverseString(char[] s) {StringBuilder sb = new StringBuilder();for (char c : s) sb.append(c);sb.reverse();for (int i = 0; i < s.length; i++) {s[i] = sb.charAt(i);}}
}

如果一道题目使用库函数可以很简单的做出来,建议不要使用 不然题目没啥意义

双指针

class Solution {public void reverseString(char[] s) {int left=0;int right=s.length-1;while(right>=left){char temp=s[left];s[left] = s[right];s[right] = temp;left++;right--;}}
}

栈与队列-用栈实现队列

class MyQueue {Stack<Integer> stackIn;Stack<Integer> stackOut;// 初始化两个栈public MyQueue() {stackIn = new Stack<>(); // 负责进栈stackOut = new Stack<>(); // 负责出栈}public void push(int x) {stackIn.push(x);// 压入入栈stackIn的栈顶}// pop 和push之前需要先判断栈是否为空public int pop() {// 一旦有输出操作,就需要把入栈内的元素全部压入出栈,然后在做相关操作while (!stackIn.empty()) {stackOut.push(stackIn.pop());}return stackOut.pop();}public int peek() {// 一旦有输出操作,就需要把入栈内的元素全部压入出栈,然后在做相关操作while (!stackIn.empty()) {stackOut.push(stackIn.pop());}return stackOut.peek();}public boolean empty() {// 两个栈为空才为空return (stackIn.empty() && stackOut.empty());}
}/*** Your MyQueue object will be instantiated and called as such:* MyQueue obj = new MyQueue();* obj.push(x);* int param_2 = obj.pop();* int param_3 = obj.peek();* boolean param_4 = obj.empty();*/

一开始将stackInstackOut 在构造函数中被定义为局部变量,这样它们在构造函数结束时就会被销毁。应该将它们定义为类的成员变量。

在这里插入图片描述

二叉树1-二叉树的递归遍历

以下以前序遍历为例:

  1. 确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入vector来放节点的数值,除了这一点就不需要再处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:
public void front(TreeNode root,List<Integer> res){//确定递归函数的参数及返回值

2.确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:

if(root==null) return;

3.确定单层递归的逻辑:前序遍历是中左右的顺序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:

//确定单层递归逻辑res.add(root.val);//中front(root.left,res);//左front(root.left,res);//右

单层递归的逻辑就是按照中左右的顺序来处理的,这样二叉树的前序遍历,基本就写完了,再看一下完整代码:

/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
// 前序遍历顺序:中-左-右 递归
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<Integer>();front(root, res);return res;}public void front(TreeNode root,List<Integer> res){//确定递归函数的参数及返回值//确定终止条件if(root==null) return;//确定单层递归逻辑res.add(root.val);//中front(root.left,res);//左front(root.left,res);//右}
}

递归要严格按照三部曲来

res定义为全局变量,共同维护

搞清楚前序遍历为 中-左-右

后序遍历:

public void back(TreeNode root, List<Integer> res) {// 1.确定递归函数的参数和返回值
if (root == null) return;// 2.确定终止条件
// 3.确定单层递归的逻辑
back(root.left, res);// 左
back(root.right, res);// 右
res.add(root.val);// 中
}

中序遍历:

public void mid(TreeNode root,List<Integer> res){// 1.确定递归函数的参数和返回值
if(root==null) return;// 2.确定终止条件
// 3.确定单层递归的逻辑
mid(root.left,res);//左
res.add(root.val);//中
mid(root.right,res);//右
}

回溯-组合

回溯基本介绍
递归+for循环
回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案
回溯可以解决哪些问题?
组合、切割、子集、排列、棋盘问题
如何理解回溯法?
抽象成一种图形结构,所有的回溯法都可以抽象成一个树形结构(N叉树 )
回溯算法通过递归来控制有多少层for循环,递归里的每一层就是一个for循环

严格按照回溯三部曲来

回溯法的搜索过程就是一个树型结构的遍历过程,在如下图中,可以看出for循环用来横向遍历,递归的过程是纵向遍历

77.组合1

class Solution {public List<List<Integer>> combine(int n, int k) {LinkedList<Integer> path = new LinkedList<>();// 用于存放符合条件的单一结果List<List<Integer>> res = new ArrayList<>();// 用于存放最后的结果集backtracking(n,k,1);return res;}void backtracking(int n, int k, int startIndex) { // 1.确定递归参数及返回值// path这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了,在图中path存的就是根节点到叶子节点的路径。if (path.size() == k) { // 2.回溯函数终止条件res.add(new ArrayList<>(path));return;}// 3.单层递归逻辑for (int i = startIndex; i <= n; i++) {path.add(i);backtracking(n, k, i + 1);//递归下一层path.removeLast();//回溯的操作了,撤销本次处理的结果}}
}

1.pathres 应该是类的成员变量,这样在递归调用过程中可以正确地共享和更新它们。

2.res 的类型应该是 List<List<Integer>>,这样可以匹配 combine 方法的返回类型。

3.大多数剪枝操作都在for循环中的i的范围做文章的

for(int i=startIndex; i<=n-(k-path.size())+1;i++)

77.组合4

4.在回溯过程中不能直接将 path 添加到 res,这样没有创建 path 的副本。这会导致 res 中的所有列表元素最终都引用同一个 path 对象。因此,最后 res 中的所有元素都会是相同的,且等于最后一个 path 的状态

5.注意startIndex,代表下一层递归的起始搜索位置

二叉树2-二叉树的迭代遍历

我们先看一下前序遍历。

前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。

为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。

动画如下:

二叉树前序遍历(迭代法)

// 前序遍历顺序:中-左-右,入栈顺序:中-右-左
class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}Stack<TreeNode> stack = new Stack<>();// 定义一个栈用于存放节点stack.push(root);//把根节点压入栈while (!stack.isEmpty()) {// 非空时执行TreeNode node = stack.pop();//中节点出栈才会将子节点压入栈res.add(node.val);// 中if (node.right != null) {stack.push(node.right);// 右节点压入栈}if (node.left != null) {stack.push(node.left);// 左节点压入栈}}return res;}
}

后序

// 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果
class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}Stack<TreeNode> stack = new Stack<>();// 定义一个栈用于存放节点stack.push(root);//把根节点压入栈while (!stack.isEmpty()) {// 非空时执行TreeNode node = stack.pop();//中节点出栈才会将子节点压入栈res.add(node.val);// 中if (node.left != null) {// 左节点压入栈stack.push(node.left);}if (node.right != null) {// 右节点压入栈stack.push(node.right);}}Collections.reverse(res);//调用 Collections工具类反转数组return res;}
}

在这里插入图片描述

在这里插入图片描述

中序

中序:左右中

分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点。

那么再看看中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的。

那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。

动画如下:

二叉树中序遍历(迭代法)

先中节点左节点全部加进去,然后逐个弹出做处理

指针 cur 从根节点开始,沿着左子树不断深入,当无法继续深入时,从栈中弹出节点,记录节点值,然后转向右子树。

class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()) {if (cur != null) {stack.push(cur); // 将访问的节点放进栈cur = cur.left;  // 左} else {cur = stack.pop(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)result.add(cur.val); // 中cur = cur.right;     // 右}}return result;}
}

二叉树3-二叉树的层序遍历

层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。

需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。

而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。

使用队列实现二叉树广度优先遍历,动画如下:

102二叉树的层序遍历

这样就实现了层序从左到右遍历二叉树。

代码如下:这份代码也可以作为二叉树层序遍历的模板

简洁形式

/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();if (root == null) {return res; // 如果根节点为空,直接返回空的结果列表}Queue<TreeNode> que = new LinkedList<>();//双端队列que.offer(root); // 将根节点加入到队列中while(!que.isEmpty()){//非空时执行List<Integer> itemList = new ArrayList<>(); // 当前层的节点值列表int len=que.size();for(int i=1;i<=len;i++){TreeNode tempNode=que.poll();itemList.add(tempNode.val);if(tempNode.left!=null) que.offer(tempNode.left);if(tempNode.right!=null) que.offer(tempNode.right);}res.add(itemList);//将当前层的节点值列表加入到结果列表中}return res;}
}

为空的时候返回[];而不是null;

因为是每一层遍历的时候都生成了一个itemList;所以后面可以直接res.add(itemList)

Queue

在这里插入图片描述

贪心-分发饼干

贪心算法一般分为如下四步:
1.将问题分解为若干个子问题
2.找出适合的贪心策略
3.求解每一个子问题的最优解
4.将局部最优解堆叠成全局最优解
这个四步其实过于理论化了,我们平时在做贪心类的题目 很难去按照这四步去思考,真是有点“鸡肋”。
做题的时候,只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了

img

class Solution {// 思路2:优先考虑胃口,先喂饱大胃口     public int findContentChildren(int[] g, int[] s) {Arrays.sort(g);//胃口Arrays.sort(s);//饼干尺寸int count = 0;int start = s.length - 1;// 遍历胃口   for 控制 胃口,里面的 if 控制饼干。for (int index = g.length - 1; index >= 0; index--) {if (start >= 0 && g[index] <= s[start]) {start--;count++;}}return count;}
}

动态规划1-斐波那契数

动规五部曲

动规五部曲:
这里我们要用一个一维dp数组来保存递归的结果
1.确定dp数组以及下标的含义
dp[i]的定义为:第i个数的斐波那契数值是dp[i]
2.确定递推公式
为什么这是一道非常简单的入门题目呢?
因为题目已经把递推公式直接给我们了:状态转移方程 dp[i] = dp[i - 1] + dp[i - 2];
3.dp数组如何初始化
题目中把如何初始化也直接给我们了,如下:
4.确定遍历顺序
递归公式dp[i] = dp[i - 1] + dp[i - 2];中可以看出,dp[i]是依赖 dp[i - 1] 和 dp[i - 2],那么遍历的顺序一定是从前到
后遍历的
5.举例推导dp数组
按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:
0 1 1 2 3 5 8 13 21 34 55

class Solution {public int fib(int n) {if (n <= 1)return n;int[] dp = new int[n + 1];//一定是n+1个数,因为有一个0dp[0] = 0;dp[1] = 1;for (int index = 2; index <= n; index++) {dp[index] = dp[index - 1] + dp[index - 2];}return dp[n];}
}

//一定是n+1个数,因为有一个0

动态规划2-爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

1.确定dp数组以及下标的含义

dp[i]: 爬到第i层楼梯,有dp[i]种方法

2.确定递推公式

如何可以推出dp[i]呢?

从dp[i]的定义可以看出,dp[i] 可以有两个方向推出来。

首先是dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,那么再一步跳一个台阶不就是dp[i]了么。

还有就是dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,那么再一步跳两个台阶不就是dp[i]了么。

那么dp[i]就是 dp[i - 1]与dp[i - 2]之和!

所以dp[i] = dp[i - 1] + dp[i - 2] 。

在推导dp[i]的时候,一定要时刻想着dp[i]的定义,否则容易跑偏。

这体现出确定dp数组以及下标的含义的重要性!

3.dp数组如何初始化

再回顾一下dp[i]的定义:爬到第i层楼梯,有dp[i]种方法。

那么i为0,dp[i]应该是多少呢,这个可以有很多解释,但基本都是直接奔着答案去解释的。

例如强行安慰自己爬到第0层,也有一种方法,什么都不做也就是一种方法即:dp[0] = 1,相当于直接站在楼顶。

但总有点牵强的成分。

那还这么理解呢:我就认为跑到第0层,方法就是0啊,一步只能走一个台阶或者两个台阶,然而楼层是0,直接站楼顶上了,就是不用方法,dp[0]就应该是0.

其实这么争论下去没有意义,大部分解释说dp[0]应该为1的理由其实是因为dp[0]=1的话在递推的过程中i从2开始遍历本题就能过,然后就往结果上靠去解释dp[0] = 1

从dp数组定义的角度上来说,dp[0] = 0 也能说得通。

需要注意的是:题目中说了n是一个正整数,题目根本就没说n有为0的情况。

所以本题其实就不应该讨论dp[0]的初始化!

我相信dp[1] = 1,dp[2] = 2,这个初始化大家应该都没有争议的。

所以我的原则是:不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推,这样才符合dp[i]的定义。

class Solution {// 常规方式public int climbStairs(int n) {int[] dp = new int[n + 1];dp[0] = 1;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}
}

单调栈-每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

//单调栈:适合求解当前元素左边或者右边第一个比当前元素大/小的元素。强调找到

递增:找第一个比他大的

递减:找第一个比他小的

栈内存放下标,记录我们遍历过的元素,然后和当前遍历的元素作对比T[i] ?T[st.pop()]

思路

在这里插入图片描述

在这里插入图片描述

  • 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况
class Solution {// 版本 1public int[] dailyTemperatures(int[] temperatures) {int lens = temperatures.length;int[] res = new int[lens];/*** 如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,* 所以弹出 栈顶元素,并记录* 如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系* 否则的话,可以直接入栈。* 注意,单调栈里 加入的元素是 下标。*/Deque<Integer> stack = new LinkedList<>();stack.push(0);for (int i = 1; i < lens; i++) {if (temperatures[i] <= temperatures[stack.peek()]) { // 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况,直接加入stack.push(i);} else {while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {res[stack.peek()] = i - stack.peek();// 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况,表示栈顶元素的右边的最大的元素就是当前遍历的元素,所以弹出栈顶元素,并记录stack.pop();}stack.push(i);}}return res;}
}

多态:

Deque<Integer> stack=new LinkedList<>();

Deque<Integer> stack = new LinkedList<>(); 确实是一个多态的例子。在Java中,多态性(Polymorphism)是指对象可以表现为多种形态,这通常通过继承和接口实现。在这个例子中,Deque 是一个接口,而 LinkedList 是一个实现了 Deque 接口的具体类。

具体分析如下:

  1. 接口和实现Deque 是一个接口,定义了双端队列的行为。LinkedList 是一个类,实现了 Deque 接口(以及其他接口如 ListQueue),并提供了具体的实现。
  2. 多态性:通过使用 Deque<Integer> stack,你可以将任何实现了 Deque 接口的类的实例赋值给 stack。在这个例子中,stack 被赋值为 LinkedList<Integer> 的实例,但它也可以是其他实现了 Deque 接口的类的实例,如 ArrayDeque<Integer>。这种设计允许代码更加灵活和可扩展,因为你可以在不改变代码其他部分的情况下,改变 stack 的具体实现。
  3. 面向接口编程:这是一种良好的编程实践,称为面向接口编程。通过依赖接口而不是具体实现,代码变得更具灵活性和可维护性。

如何判断是递增还是递减的单调栈呢?

递增:找第一个比他大的

递减:找第一个比他小的

栈内存放下标,记录我们遍历过的元素,然后和当前遍历的元素作对比T[i] ?T[st.pop()]

图论-所有可能路径

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序

graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

示例 1:

img

输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
private List<List<Integer>> result = new ArrayList<>(); // 收集符合条件的路径
private List<Integer> path = new ArrayList<>(); // 0节点到终点的路径

DFS深度优先搜索

1.确认递归函数,参数

首先我们dfs函数一定要存一个图,用来遍历的,还要存一个目前我们遍历的节点,定义为x,下标

至于 单一路径,和路径集合可以放在全局变量,那么代码是这样的:

private List<List<Integer>> result = new ArrayList<>(); // 收集符合条件的路径
private List<Integer> path = new ArrayList<>(); // 0节点到终点的单一路径// x:目前遍历的节点// graph:存当前的图private void dfs(int[][] graph, int x)

2.确认终止条件

什么时候我们就找到一条路径了?

当目前遍历的节点 为 最后一个节点的时候,就找到了一条,从 出发点到终止点的路径。

当前遍历的节点,我们定义为x,最后一点节点,就是 graph.length - 1(因为题目描述是找出所有从节点 0 到节点 n-1 的路径并输出)。

所以 但 x 等于 graph.size() - 1 的时候就找到一条有效路径。 代码如下:

  // 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.length - 1if (x == graph.length - 1) { // 找到符合条件的一条路径result.add(new ArrayList<>(path));// 收集有效路径return;}

3.处理目前搜索节点出发的路径

接下来是走 当前遍历节点x的下一个节点。

首先是要找到 x节点链接了哪些节点呢? 遍历方式是这样的:

 for (int i = 0; i < graph[x].length; i++) { // 遍历节点n链接的所有节点

接下来就是将 选中的x所连接的节点,加入到 单一路径来。

path.add(graph[x][i]); // 遍历到的节点加入到路径中来

一些录友可以疑惑这里如果找到x 链接的节点的,例如如果x目前是节点0,那么目前的过程就是这样的:

img

二维数组中,graph[x] [i] 都是x链接的节点,当前遍历的节点就是 graph[x][i]

进入下一层递归

dfs(graph, graph[x][i]); // 进入下一层递归

最后就是回溯的过程,撤销本次添加节点的操作。 该过程整体代码:

 for (int i = 0; i < graph[x].length; i++) { // 遍历节点n链接的所有节点path.add(graph[x][i]); // 遍历到的节点加入到路径中来dfs(graph, graph[x][i]); // 进入下一层递归path.remove(path.size() - 1); // 回溯,撤销本节点//或者这么写: path.removeLast(); // 回溯,撤销本节点}

DFS

//dfs
class Solution {private List<List<Integer>> result = new ArrayList<>(); // 收集符合条件的路径private List<Integer> path = new ArrayList<>(); // 0节点到终点的路径public List<List<Integer>> allPathsSourceTarget(int[][] graph) {path.add(0); // 无论什么路径已经是从0节点出发dfs(graph, 0); // 开始遍历return result;}// x:目前遍历的节点// graph:存当前的图private void dfs(int[][] graph, int x) {// 要求从节点 0 到节点 n-1 的路径并输出,所以是 graph.length - 1if (x == graph.length - 1) { // 找到符合条件的一条路径//   从0---->3result.add(new ArrayList<>(path));// 收集有效路径return;}for (int i = 0; i < graph[x].length; i++) { // 遍历节点n链接的所有节点path.add(graph[x][i]); // 遍历到的节点加入到路径中来 0->1  //21行回来 变成0->1->3 r 到了15行 result加入0->1->3路径 return回21,撤销dfs(graph, graph[x][i]); // 进入下一层递归 进入节点1path.removeLast(); // 回溯,撤销本节点}}
}

打断点debug看具体的运行流程是怎么样的

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

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

相关文章

ATFX汇市:英央行6月利率决议来袭,大概率按兵不动

ATFX汇市&#xff1a;昨日英国统计局刚公布5月CPI年率数据&#xff0c;今日英国央行就要公布利率决议结果&#xff0c;两项重磅数据同一周出现&#xff0c;GBPUSD或迎来高波动期。今日19:00&#xff0c;英国央行将公布6月利率决议结果&#xff0c;市场普遍预期其将维持5.25%的基…

双网卡设置路由网络不通原因之一:静态ip设置失败

1.主要现象&#xff1a; 外网通&#xff0c;内网不通 外网IP设置 内网IP设置 路由表设置 内网不通 2.主要原因&#xff1a;在适配器中设置的内网静态IP没有成功 设置静态IP失败 在命令行使用ipconfig命令看到内网适配器的静态IP为192.168.0.55&#xff0c;并不是我们设置的1…

【Esp32连接微信小程序蓝牙】附Arduino源码《 返回10007 相同特征id冲突问题》

前言 最近接了一个外包&#xff0c;发现了esp32连接小程序会有很多bug&#xff0c;所以接下来会慢慢更新解决方案&#xff0c;还是需要多接触项目才能进步呀兄弟们&#xff01; 附上uuid的生成链接&#xff1a; // See the following for generating UUIDs: // https://www.uu…

adb 查看哪些应用是双开的

adb shell pm list users 得到 这 里有 user 0 ,11,999 其中0是系统默认的&#xff0c;11是平行空间的&#xff0c;999是双开用户 pm list packages --user 999 -3 得到了999用户安装第三方应用的包名 pm list packages --user 11 -3 得到了隐私空间用户安装第三方应用的…

Linux源码阅读笔记02-进程原理及系统调用

进程和进程的生命周期 进程&#xff1a;指计算机中已运行的程序。进程本身不是基本的运行单位&#xff0c;而是线程的容器。程序本身不是基本的运行单位&#xff0c;而是线程的容器。程序是指令、数据和组织形式的描述&#xff0c;进程才是程序的真正运行实例。Linux内核把进程…

优思学院|IT行业学习六西格玛的价值

提到六西格玛&#xff08;Six Sigma&#xff09;&#xff0c;很多人可能首先想到的是制造业。六西格玛确实在制造业中有着广泛的应用和显著的效果&#xff0c;如提高产品质量、降低缺陷率、减少浪费等。那么&#xff0c;六西格玛在信息技术&#xff08;IT&#xff09;行业是否同…

医学图像预处理之z分数归一化

在医学图像处理中&#xff0c;Z分数标准化&#xff08;Z-score normalization&#xff09;是一种常用的数据标准化方法&#xff0c;其目的是将数据集中的每个图像像素值转换为具有均值为0和标准差为1的标准化值。这种标准化方法有助于改善图像的质量&#xff0c;便于后续图像处…

Window使用Hyper-V进行显卡直通

一、环境配置 处理器 Intel Xeon CPU E5-2680 v3 @ 2.50GHz 2.50 GHz 机带 RAM 64.0 GB 二、安装Hyper-V 控制面板–>程序和功能->启用或关闭winodws功能 三、打开Hyper-V安装windows 安装windows略过,记住(禁用检查点) 四、在本机Winows上以及管理员打开Powe…

山东华素制药有限公司:素心做药,感恩回报

在山东威海这片美丽的土地上,有一颗璀璨的明珠——山东华素制药有限公司。自2013年成立以来,这家企业以其深厚的制药底蕴、卓越的研发实力和坚定的社会责任,赢得了社会各界的广泛赞誉。它不仅是化学药品制剂制造的佼佼者,更是“素心做药,感恩回报”的典范。 一、素心做药,品质为…

MySQL快速安装(mysql8.0.30区别之前yum安装)

目录 一.初始化环境并解压 二.创建程序用户管理 三.修改mysql目录和配置文件的权限 四.修改配置文件 五.设置环境变量&#xff0c;申明/宣告mysql命令便于系统识别 六.初始化数据库 七.设置系统识别&#xff0c;进行操作 八.初始化数据库密码 九.用户并设置密码 十.赋…

18 Shell编程规范与变量

目录 18.1 Shell脚本概述 18.1.1 Shell的作用 18.1.2 编写第一个Shell脚本 18.1.3 重定向与管道操作 18.2 Shell变量的作用、类型 18.2.1 自定义变量 18.2.2 特殊的Shell变量 18.1 Shell脚本概述 可以批量处理、自动化地完成一系列维护任务&#xff0c;大大减轻管理员的负担。…

[leetcode hot 150]第十五题,三数之和

题目&#xff1a; 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复…

电脑怎么恢复出厂设置?系统还原怎么操作?就看这5个方法!

电脑怎么恢复出厂设置&#xff1f;如果您的电脑出现问题&#xff0c;在电脑上恢复出厂重置非常有用。它基本上可以重置电脑&#xff0c;使其恢复到下线时的状态&#xff0c;给你一个全新的开始。众所周知&#xff0c;我们使用电脑的时间越长&#xff0c;电脑上的文件和程序就会…

Vue3 + Element-plus + TS —— 动态表格自由编辑

前期回顾 《 穿越时空的代码、在回首&#xff1a;Evil.js两年后的全新解读 》-CSDN博客 Vue3 TS Element-Plus 封装Tree组件 《亲测可用》_ https://blog.csdn.net/m0_57904695/article/details/131664157?spm1001.2014.3001.5501 态表格 自由编辑 目录 ♻️ 效果图…

AtomicInteger原理和CAS与Synchronized(juc编程)

AtomicInteger原理 4.6.1 原理介绍 AtomicInteger的本质&#xff1a;自旋锁 CAS算法 CAS的全成是&#xff1a; Compare And Swap(比较再交换); 是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。CAS可以将read-modify-write转换为原子操作&#xff0c;这…

关于椭圆的方程(有Python画的动图)

关于椭圆的方程&#xff08;有Python画的动图&#xff09; flyfish 几何定义 椭圆是平面上所有到两个固定点&#xff08;焦点&#xff09;的距离之和为常数的点的集合。这两个固定点叫做焦点。 解析几何描述 设椭圆的两个焦点为 F 1 F_1 F1​ 和 F 2 F_2 F2​&#xff…

【高等数学】傅里叶级数

最近刷了会抖音&#xff0c;看到一个非常有趣的现象&#xff1a;傅里叶级数&#xff0c;今天挑了几个视频来供大家学习。 1.傅里叶级数概念 【小崔说数】傅里叶级数专题https://www.bilibili.com/video/BV1Uq4y1q7xk?t117.4 2.傅里叶级数动画 【谜之舒适】12分钟的傅立叶级…

【docker】Dockerfile制作基础镜像 python 底层镜像制作 | 打包所有的requirement依赖

一、Dockerfile思想 我们正常的对一个项目进行打包 docker image 通常是在CI工具编译时进行对依赖的安装&#xff0c;比如golang的go get、python的pip install、node的npm install 等等 好处&#xff1a;我们更新了依赖可以动态的再编译时进行一个对依赖的更新 坏处&#xf…

转速传感器频率信号整形方波输出隔离变送器 地线干扰抑制 200mV~10V/0-12V/0-24V转0-5v/0-12v/0-24v/集电极输出

特点 转速传感器信号直接输入&#xff0c;方波信号输出正弦波、锯齿波信号输入&#xff0c;方波信号输出200mV峰值微弱信号的放大与整形不改变原波形频率&#xff0c;响应速度快电源、信号&#xff1a;输入/输出 3000VDC三隔离辅助电源&#xff1a;5V、12V、15V或24V直流单电源…

在4面体空间内2点结构占比

有一个4面体状空间&#xff0c;由3层甲烷状分子堆积而成&#xff0c;单个甲烷4面体边长10. 内有30个点&#xff0c;在30个点中取2点&#xff0c;有30*29/2435种取法。这里要求两个点的距离必须为6.123 在435个结构中只有40个符合要求 序数 结构 序数 结构 3 1 282 3 7…