版本说明
当前版本号[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的长度m int m = num1.length(); // 获取num2的长度n int 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;
}
}