版本说明
当前版本号[20231112]。
| 版本 | 修改说明 | 
|---|---|
| 20231112 | 初版 | 
目录
文章目录
- 版本说明
- 目录
- 字符串相乘
- 题目
- 解题思路
- 代码思路
- 补充说明
- 参考代码
 
- 子集
- 题目
- 解题思路
- 代码思路
- 参考代码
 
- 删除链表的倒数第 N 个结点
- 题目
- 解题思路
- 代码思路
- 参考代码
 
字符串相乘
题目
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。
示例 1:
输入: num1 = “2”, num2 = “3”
 输出: “6”
示例 2:
输入: num1 = “123”, num2 = “456”
 输出: “56088”
说明:
num1 和 num2 的长度小于110。
 num1 和 num2 只包含数字 0-9。
 num1 和 num2 均不以零开头,除非是数字 0 本身。
 不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。
解题思路
- 首先判断输入的两个字符串是否为0,如果是则直接返回"0"。
- 获取两个字符串的长度m和n。
- 创建一个长度为m+n-1的整数数组intRes,用于存储乘积的结果。
- 使用两层循环遍历num1和num2的每一位数字,将它们相乘并累加到intRes数组中对应的位置。
- 从intRes数组的末尾开始向前遍历,如果当前位置的值大于等于10,则需要进位。将当前位置的值除以10,并将余数加到前一位上。
- 将intRes数组转换为字符串形式,即为最终的乘积结果。
代码思路
-  首先判断输入的两个字符串是否为"0",如果是,则直接返回"0",因为任何数与0相乘都等于0。 // 如果num1或num2为"0",则直接返回"0"if (num1.equals("0") || num2.equals("0"))return "0";
-  获取两个字符串的长度m和n,分别表示num1和num2的位数。 // 获取num1的长度mint m = num1.length();// 获取num2的长度nint n = num2.length();
-  创建一个长度为m+n-1的整型数组intRes,用于存储相乘结果的每一位数字。 // 创建一个长度为m+n-1的整型数组intRes,用于存储相乘结果的每一位数字int[] intRes = new int[m + n - 1];
-  使用两层循环遍历num1和num2的每一位数字,将它们相乘的结果累加到intRes数组中对应的位置上。这里需要注意的是,由于num1和num2是字符串表示的整数,所以在计算时需要将字符转换为对应的数字值(通过减去字符’0’的ASCII码值)。 // 使用两层循环遍历num1和num2的每一位数字for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {// 将num1的第i位数字与num2的第j位数字相乘,然后加上intRes数组中对应位置的值intRes[i + j] += (num1.charAt(i) - 48) * (num2.charAt(j) - 48);}}
-  对intRes数组进行进位处理。从数组的最后一位开始向前遍历,如果当前位的数字大于等于10,则需要将其除以10并加上前一位的值。同时,将当前位的值更新为除以10后的余数。 // 从intRes数组的最后一位开始向前遍历,如果当前位的数字大于等于10,则需要将其除以10并加上前一位的值for (int i = intRes.length - 1; i > 0; i--) {if (intRes[i] >= 10) {intRes[i - 1] += intRes[i] / 10;intRes[i] %= 10;}}
-  最后,将intRes数组中的每个元素转换为字符串,拼接起来得到最终的相乘结果。拼接起来,形成最终的乘积结果。这个结果被赋值给变量res,并作为方法的返回值。 
        // 遍历intRes数组,将每个元素转换为字符串并拼接到res中for (int i = 0; i < intRes.length; i++) {res += String.valueOf(intRes[i]);}// 返回最终的相乘结果return res;}
补充说明
1、为什么在代码思路第3的步骤里,创建一个整型数组intRes是长度为 m+n-1 的呢?
int[] intRes = new int[m + n - 1];
 在这段代码中,m + n - 1用于初始化一个长度为m + n - 1的整数数组intRes。这个数组用于存储两个字符串num1和num2相乘的结果。
 具体来说,如果num1的长度为m,num2的长度为n,那么num1和num2相乘的结果的最大位数就是m + n - 1。
 因此,我们需要创建一个长度为m + n - 1的数组来存储结果。
2、在代码思路第4的步骤里,在计算时该怎么将字符转换为对应的数字值呢,又是为什么要减去48呢?
  intRes[i + j] += (num1.charAt(i) - 48) * (num2.charAt(j) - 48);
 这段代码的目的是将两个字符串表示的数字相乘,并将结果存储在一个整数数组中。
(num1.charAt(i) - 48)/ (num2.charAt(j) - 48):将num1/num2的第i位字符转换为对应的数字值。
- 这里减去48是因为字符’0’的ASCII码值为48,
- 所以通过减去48可以将字符转换为对应的数字值
- (例如,字符’2’的ASCII码值为50,减去48后得到数字2)。
参考代码
这段代码是一个用于实现两个字符串表示的整数相乘的算法。
class Solution {public String multiply(String num1, String num2) {if (num1.equals("0") || num2.equals("0"))return "0";int m = num1.length();int n = num2.length();int[] intRes = new int[m + n - 1];for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {intRes[i + j] += (num1.charAt(i) - 48) * (num2.charAt(j) - 48);}}for (int i = intRes.length - 1; i > 0; i--) {if (intRes[i] >= 10) {intRes[i - 1] += intRes[i] / 10;intRes[i] %= 10;}}String res = "";for (int i = 0; i < intRes.length; i++) {res += String.valueOf(intRes[i]);}return res;}
}
子集
题目
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
 输出:[[],[0]]
提示:
1 <= nums.length <= 10
 -10 <= nums[i] <= 10
 nums 中的所有元素 互不相同
解题思路
- 创建一个空的结果列表 res,用于存储所有可能的子集。
- 创建一个临时列表 tmp,用于存储当前正在构建的子集。
- 将空列表添加到结果列表 res 中。
- 如果输入数组 nums 的长度为 0,直接返回结果列表 res。
- 调用辅助函数 helper,传入输入数组 nums、起始索引 0、临时列表 tmp 和结果列表 res。
- 在辅助函数 helper 中,遍历输入数组 nums 从起始索引 start 开始的所有元素。 a. 将当前元素添加到临时列表 tmp 中。 b. 递归调用辅助函数 helper,传入输入数组 nums、当前元素的下一个索引 i + 1、临时列表 tmp 和结果列表 res。 c. 将当前临时列表 tmp 的副本添加到结果列表 res 中。 d. 移除临时列表 tmp 中的最后一个元素,回溯到上一步。
- 返回结果列表 res。
代码思路
-  定义一个名为 Solution的类,其中包含两个方法:subsets和helper。
-  subsets方法是主方法,它接受一个整数数组nums作为输入,并返回一个包含所有子集的列表。public List<List<Integer>> subsets(int[] nums)
-  在 subsets方法中,首先创建一个空的结果列表res和一个临时列表tmp,然后将tmp添加到res中。List<List<Integer>> res = new ArrayList<List<Integer>>(); // 存储结果的列表List<Integer> tmp = new ArrayList<>(); // 临时列表,用于存储当前子集res.add(tmp); // 将空子集添加到结果列表中
-  如果输入数组 nums的长度为0,则直接返回结果列表res。if (nums.length == 0) // 如果输入数组为空,直接返回结果列表return res;
-  调用辅助方法 helper,传入输入数组nums、起始索引0、临时列表tmp和结果列表res。helper(nums, 0, tmp, res); // 调用辅助函数,生成子集
-  helper方法是一个递归方法,用于生成所有可能的子集。它接受四个参数:输入数组nums、当前处理的起始索引start、临时列表tmp和结果列表res。// 辅助函数,递归生成子集public void helper(int[] nums, int start, List<Integer> tmp, List<List<Integer>> res)
-  在 helper方法中,使用一个循环从起始索引start开始遍历输入数组nums。for (int i = start; i < nums.length; i++)
-  在每次循环中,将当前元素添加到临时列表 tmp中,然后递归调用helper方法,传入下一个索引i + 1、更新后的临时列表tmp和结果列表res。tmp.add(nums[i]); // 将当前元素添加到临时列表中helper(nums, i + 1, tmp, res); // 递归调用辅助函数,处理下一个元素
-  在递归调用返回后,将当前的临时列表 tmp复制一份,并将其添加到结果列表res中。res.add(new ArrayList<Integer>(tmp)); // 将当前临时列表复制一份,添加到结果列表中
-  最后,从临时列表 tmp中移除最后一个元素,以便在下一次循环中处理下一个元素。tmp.remove(tmp.size() - 1); // 移除临时列表中的最后一个元素,回溯到上一步
-  当循环结束时,所有的子集都已经生成并添加到结果列表 res中,最终返回该列表。
参考代码
这段代码是一个求解给定数组的所有子集的算法。它使用了回溯法来生成所有可能的子集,并将它们存储在一个列表中返回。
class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList<List<Integer>>();List<Integer> tmp = new ArrayList<>();res.add(tmp);if (nums.length == 0)return res;helper(nums, 0, tmp, res);return res;}public void helper(int[] nums, int start, List<Integer> tmp, List<List<Integer>> res) {for (int i = start; i < nums.length; i++) {tmp.add(nums[i]);helper(nums, i + 1, tmp, res);res.add(new ArrayList<Integer>(tmp));tmp.remove(tmp.size() - 1);}}
}
删除链表的倒数第 N 个结点
题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:

输入:head = [1,2,3,4,5], n = 2
 输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
 输出:[]
示例 3:
输入:head = [1,2], n = 1
 输出:[1]
提示:
链表中结点的数目为 sz
 1 <= sz <= 30
 0 <= Node.val <= 100
 1 <= n <= sz
解题思路
- 创建一个虚拟头节点v,并将其指向原链表的头结点head。这样做的目的是方便处理边界情况,例如当需要删除的是头结点时。
- 使用一个循环遍历整个链表,将每个节点添加到一个列表index中。这样我们可以方便地访问链表中的任意节点。
- 计算要删除的节点的前一个节点和后一个节点在列表中的索引位置。前一个节点的索引为index.size() - n - 1,后一个节点的索引为index.size() - n + 1。
- 根据计算出的索引位置,更新前一个节点的next指针,使其指向后一个节点或null(如果后一个节点不存在)。
- 返回虚拟头节点v的下一个节点,即删除倒数第n个节点后的链表头结点。
代码思路
-  创建一个虚拟头节点v,并将其指向原链表头节点head。这样做是为了方便处理边界情况,例如当需要删除的是头节点时。 // 移除链表中倒数第n个节点的方法public ListNode removeNthFromEnd(ListNode head, int n) {ListNode v = new ListNode(0, head); // 创建一个虚拟头节点,指向原链表头节点
-  创建一个名为handle的指针,指向虚拟头节点v。 ListNode handle = v; // 创建一个指针,指向虚拟头节点
-  创建一个名为index的列表,用于存储链表中所有节点的引用。 List<ListNode> index = new ArrayList<>(); // 创建一个列表,用于存储链表中所有节点的引用
-  使用while循环遍历链表,将每个节点的引用添加到index列表中。 // 遍历链表,将每个节点的引用添加到列表中while (v != null) {index.add(v);v = v.next;}
-  计算要删除的节点的前一个节点和后一个节点在index列表中的索引位置pre和next。 // 计算要删除的节点的前一个节点和后一个节点在列表中的索引位置int pre = index.size() - n - 1;int next = index.size() - n + 1;
-  根据pre和next的值,更新前一个节点的next指针,使其指向后一个节点或null(如果后一个节点不存在)。 // 更新前一个节点的next指针,使其指向后一个节点或null(如果后一个节点不存在)index.get(pre).next = next >= 0 && next < index.size() ? index.get(next) : null;
-  返回处理后的链表头节点handle.next。 
             // 返回处理后的链表头节点return handle.next;
参考代码
这段代码是用于删除链表中倒数第n个节点。
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 removeNthFromEnd(ListNode head, int n) {ListNode v = new ListNode(0, head);ListNode handle = v;List<ListNode> index = new ArrayList<>();while (v != null) {index.add(v);v = v.next;}int pre = index.size() - n - 1;int next = index.size() - n + 1;index.get(pre).next = next >= 0 && next < index.size() ? index.get(next) : null;return handle.next;}
}