文章目录
代码随想录刷题笔记-数组篇
27 移除元素(easy)
力扣地址
https://leetcode.cn/problems/remove-element/description/
题目描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:
更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
返回 k。
用户评测:
评测机将使用以下代码测试您的解决方案:
int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
// 它以不等于 val 的值排序。
int k = removeElement(nums, val); // 调用你的实现
assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
assert nums[i] == expectedNums[i];
}
如果所有的断言都通过,你的解决方案将会 通过。
题目实例
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:你的函数函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。
你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
解题思路
快慢指针,两个指针都从下标0开始。
快的指针负责遍历原始数组,每次+1。
慢的指针负责统计真实长度,当满足条件(快指针指向的小标 != 目标值)时,先把不满足的值赋给当前慢指针的下标,然后+1。
当快指针遍历结束时,此时的慢指针的长度就是期待的值。
举个栗子:
例如 3 2 2 3,目标值是3
快指针 为0,如果表示下标则指向3 ,慢指针不动,值还是0
快指针 为1,如果表示下标则指向2 ,满足不等于目标值,先给慢指针的当前值(0)对应的num[0] 赋值2,然后慢指针向后移动,慢指针变成1
快指针 为2,如果表示下标则指向2 ,满足不等于目标值,先给慢指针的当前值(1)对应的num[1] 赋值2,然后慢指针向后移动,慢指针变成2
快指针 为3,如果表示下标则指向3,慢指针不动,值还是2
数组遍历结束,返回慢指针的值2,也就是只有两个元素符合条件。
补充说明: 之所以用 “快指针 为0,如果表示下标则指向3” 是因为这个不完全是数组的下标,只是可以这样理解,因为实际场景的话,新数组的长度是2,下标2都越界了。
代码实现
public int removeElement(int[] nums, int val) {
// 慢指针
int slowIndex = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) {
// 也可以写成 slowIndex++
nums[slowIndex] = nums[i];
slowIndex++;
}
}
return slowIndex;
}
tips: 实际开发中,不要用i这样的表示,要有业务意义。
26. 删除有序数组中的重复项(easy)
力扣地址
https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/
题目描述
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。
题目示例
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
解题思路
有序,所以一样的一定在一起。
快慢指针
初始状态:慢指针为0,快指针为1
进行状态:当快慢指针指向的值不一致时,慢指针先加1,然后再赋值这个加一以后的位置的值。上一个题目是从慢指针开始一个一个填充不等于目标值的,这个是最开始的那一位就是需要的,然后才要筛选每一个不一样的。
结束状态:快指针遍历结束,返回慢指针 + 1,数组是从0开始的,长度要+1。
栗子:
1,1,2
初始状态:
快指针1,指向的值是1
慢指针0,指向的值是1
进行状态:
快指针+1,变成2,指向的值是2
慢指针是0,指向1,值不一样,慢指针先+1,变成1,然后1这个位置的值赋值2
结束状态:
返回长度,慢指针1,+1是2
代码实现
public int removeDuplicates(int[] nums) {
// 建议做个保护,实际你不知道别人会不会调你的方法,万一有问题,防止甩锅
if (nums == null || nums.length == 0) {
return 0;
}
int slowIndex = 0;
for (int fastIndex = 1; fastIndex < nums.length; fastIndex++) {
if (nums[fastIndex] != nums[slowIndex]) {
slowIndex++;
nums[slowIndex] = nums[fastIndex];
}
}
return slowIndex + 1;
}
283. 移动零(easy)
力扣地址
https://leetcode.cn/problems/move-zeroes/description/
题目描述
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
题目实例
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
解题思路
这个和27是一样的,可以理解成目标值是0,然后把不等于0的全部塞完以后,这个时候的慢指针从结束位置一直填充0。
代码实现
public void moveZeroes(int[] nums) {
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[index++] = nums[i];
}
}
// 上面和27完全一样,下面就一点点变种
for (int i = index; i < nums.length; i++) {
nums[i] = 0;
}
}
tips:实际开发中,不要把对象塞在一个void方法里,否则你压根不知道会改点什么东西,和对象有关的要放在初始化里,如果涉及状态改变,将这个对象建模成领域实体,再把状态改变的方法定义在领域实体中。
844 比较含退格的字符串(easy)
力扣地址
https://leetcode.cn/problems/backspace-string-compare/description/
题目描述
给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
题目实例
示例 1:
输入:s = "ab#c", t = "ad#c"
输出:true
解释:s 和 t 都会变成 "ac"。
解题思路
栈的解法比较基础,就不展开了,主要是看到卡哥的思路,直呼牛逼,就直接贴出来了
动图:
初始化:
定义s串中的#的数量 sSkipNum = 0
定义t串中的#的数量 tSkipNum = 0
定义指针 sIndex,从后往前遍历, sIndex = s.length() - 1
定义指针 tIndex,从后往前遍历, tIndex = t.length() - 1
进行状态:
两个指针的过程是一致的
while(指针 >= 0) {
if ( 当前指针指向的字符 = #) {
#的数量++;
} else {
// 进行回退操作,回退以后,这个#的数量就需要-1
if ( #的数量 > 0) {
#的数量--;
} else {
// 如果不需要进行回退操作,那么就进行下一个字符的处理
break;
}
}
指针--;
}
两个指针遍历以后,如果指向的字符不一样,那么说明还是不一样,就判断false,否则就继续下一个
if (t.charAt(tIndex) != s.charAt(sIndex)) {
return false;
}
tIndex--;
sIndex--;
结束状态:
当有一个指针结束,那么判断是不是两个指针都指向了-1,指向-1说明都遍历结束了,回退操作以后两个串是一样的
代码实现
public boolean backspaceCompare(String s, String t) {
// 初始状态
int sSkipNum = 0;
int tSkipNum = 0;
int sIndex = s.length() - 1;
int tIndex = t.length() - 1;
// 中间状态
while (true) {
while (sIndex >= 0) {
if (s.charAt(sIndex) == '#') {
sSkipNum++;
} else {
if (sSkipNum > 0) {
sSkipNum--;
} else {
break;
}
}
sIndex--;
}
while (tIndex >= 0) {
if (t.charAt(tIndex) == '#') {
tSkipNum++;
} else {
if (tSkipNum > 0) {
tSkipNum--;
} else {
break;
}
}
tIndex--;
}
// 结束状态:有一个已经遍历结束了,交给最后的审判,笑死,也算中间状态,看怎么理解
if (tIndex < 0 || sIndex < 0) {
break;
}
// 中间状态:#回退的已经结束了,后面就是判断剩下的是不是一致
if (t.charAt(tIndex) != s.charAt(sIndex)) {
return false;
}
tIndex--;
sIndex--;
}
// 结束状态: 看看是不是两个都遍历完了
if (sIndex == tIndex && sIndex == -1) {
return true;
}
return false;
}
977. 有序数组的平方(easy)
力扣地址
https://leetcode.cn/problems/squares-of-a-sorted-array/description/
题目描述
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
题目实例
示例 1:
输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
解题思路
实际开发中:最简单的当然用stream的map转成平方,然后统计list,再用工具类排序即可。
刷题学习建议还是双指针
初始状态:
前指针,指向0,从头遍历
后指针,指向nums.length -1,从后向前遍历
新数组的下标,默认指向最后,nums.length - 1
进行状态:
如果前指针指向的数的平方大,那么就放在最后,然后新数组的下标–,反之就是后指针的数放在后面
代码实现
public int[] sortedSquares(int[] nums) {
int i = 0;
int j = nums.length - 1;
int index = nums.length - 1;
int[] res = new int[nums.length];
while (i <= j) {
if (nums[i] * nums[i] > nums[j] * nums[j]) {
res[index--] = nums[i] * nums[i];
i++;
} else {
res[index--] = nums[j] * nums[j];
j--;
}
}
return res;
}
209 长度最小的子数组(mid)
力扣地址
https://leetcode.cn/problems/minimum-size-subarray-sum/description/
题目描述
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的
子数组
[numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
题目实例
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
解题思路
不定长的滑窗。
滑窗模板
不定长
int res = 最大值或者最小值,求最大值就选最小值,反之就最大值
// 这样方便计算长度
int start = -1;
int end = 0;
// 中间状态,可能是求和,求积或者什么
int midStatusNum = ?
while (end < num.length) {
midStatusNum = 操作num[end]
while (一个条件) {
// 也可能是最大值,因为初始化的时候给了-1,所以这个时候就直接减
res = Math.min(res, end - start);
start++;
midStatus - num[start]
}
end++;
}
// 如果还是初始化的值,那说明这个过程就取不到
res == 最大值或者最小值 ? 0 : res;
定长的话,判断窗口的时候就不能用while了,就用if,这样长度才能被限制住
int res = 最大值或者最小值,求最大值就选最小值,反之就最大值
// 这样方便计算长度
int start = -1;
int end = 0;
// 中间状态,可能是求和,求积或者什么
int midStatusNum = ?
while (end < num.length) {
midStatusNum = 操作num[end]
if (一个条件) {
// 也可能是最大值,因为初始化的时候给了-1,所以这个时候就直接减
res = Math.min(res, end - start);
start++;
midStatus - num[start]
}
end++;
}
// 如果还是初始化的值,那说明这个过程就取不到
res == 最大值或者最小值 ? 0 : res;
代码实现
套不定长的模板
public int minSubArrayLen(int target, int[] nums) {
int res = Integer.MAX_VALUE;
int start = -1;
int end = 0;
int sum = 0;
while (end < nums.length) {
sum += nums[end];
while (sum >= target) {
res = Math.min(res, end - start);
start++;
sum -= nums[start];
}
end++;
}
return res == Integer.MAX_VALUE ? 0 : res;
}