这个专题中的题目是我跟随代码随想录的刷题计划,在LeetCode上做的与数组相关的题目,用于加深对数组的理解!
下面的内容将会有每一道题目的题意、在代码随想录中对应的参考文章、我的思路以及我所写的Java代码,希望对你有帮助!
目录
1 - LeetCode 704 二分查找 – 二分查找法
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/binary-search
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的
target,如果目标值存在返回下标,否则返回 -1。
示例 1:输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例 2:输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
提示:你可以假设 nums 中的所有元素是不重复的。 n 将在 [1, 10000]之间。 nums 的每个元素都将在 [-9999, 9999]之间。
思路:
简单的二分题,主要注意点在于区分 while ( left < right ) 与 while ( left <= right ) 写法上的不同。
while ( left < right ) 对应的是左闭右开的区间的写法,while ( left <= right ) 对应的是左闭右闭的区间的写法,导致的结果是当 nums[middle] > target 时right的取值的不同:
当写 while ( left < right ) 时,注意到此时是左闭右开区间,那么right位置的值是不会考虑到的,因此当 nums[middle] > target 时,既然middle位置上的值比target大,就无须考虑middle这个位置上的值了,更新 right 值时应该是 right = middle
当写 while ( left <= right ) 时,注意到此时是左闭右闭区间,那么right位置的值是会考虑到的,因此当 nums[middle] > target 时,既然middle位置上的值比target大,同样无须考虑middle这个位置上的值了,更新 right 值时应该是 right = middle - 1
详细内容请见参考文章
本题Java代码(使用的是写法二):
class Solution {
public int search(int[] nums, int target) {
int l = 0, r = nums.length - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (target > nums[mid]) l = mid + 1;
else if (target < nums[mid]) r = mid - 1;
else return mid;
}
return -1;
}
}
2 - LeetCode 27 移除元素 – 双指针法
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-element
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
说明:为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
示例 1:输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
提示:0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
思路:
主要是使用到双指针法。双指针法在数组和链表的操作中很常见,在本题中使用方法主要是设置一个快指针和一个慢指针。
下面这一图很形象地描绘出本题中快慢指针的用途(图片引用自代码随想录)
本题代码:
class Solution {
public int removeElement(int[] nums, int val) {
int fast = 0, slow = 0;
while (fast < nums.length) {
if (nums[fast] != val) {
nums[slow] = nums[fast];
slow++;
}
fast++;
}
return slow;
}
}
3 - LeetCode 977 有序数组的平方 – 双指针法
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/squares-of-a-sorted-array
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]
示例 2:输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]
提示:1 <= nums.length <= 104
-104 <= nums[i] <= 104 nums 已按 非递减顺序 排序
进阶:请你设计时间复杂度为 O(n) 的算法解决本问题
思路:
除了使用暴力解法之外,本题的进阶写法可以考虑使用双指针法实现复杂度为O(N)的解法。
由于题目预先就提供非递减的数组,所以我们可以考虑在数组的头和尾各设置一个指针,通过比较两个指针所指的值的绝对值大小,将绝对值较大的值的平方值加入到结果数组中。
本题代码:
class Solution {
public int[] sortedSquares(int[] nums) {
int left = 0, right = nums.length - 1;
int[] ans = new int[nums.length];
int index = nums.length - 1;
while(left<=right){
if(Math.abs(nums[left])>Math.abs(nums[right])){
ans[index--]=nums[left]*nums[left];
left++;
}else{
ans[index--]=nums[right]*nums[right];
right--;
}
}
return ans;
}
}
4 - LeetCode 209 长度最小的子数组 – 滑动窗口法
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回 0 。
示例 1:输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:输入:target = 4, nums = [1,4,4]
输出:1
示例 3:输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
思路:
使用滑动窗口的方法。滑动窗口与双指针法有点类似,使用前指针与后指针,前指针与后指针中间的部分即为滑动窗口。
以下图片很形象地说明了本题中滑动窗口的作用(图片转载自代码随想录)
本题Java代码:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int sum = 0, ans = 122222, back = 0;
for (int fast = 0; fast < nums.length; fast++) {
sum += nums[fast];
while (sum >= target) {
int tmp = fast - back + 1;
ans = ans <= tmp ? ans : tmp;
sum -= nums[back++];
}
}
if (ans == 122222) ans = 0;
return ans;
}
}
5 - LeetCode 59 旋转矩阵II – 模拟
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/spiral-matrix-ii
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:输入:n = 1
输出:[[1]]
提示:1 <= n <= 20
思路:
本题是模拟题,突破点在于将每一步操作的区域视为一个左闭右开的区间,如下图
这样基本写起来就没什么问题。设当前圈的起始位置为(x,x),每转一圈后,新的一圈的起始位置为(x+1, x+1)。
本题Java代码:
class Solution {
public int[][] generateMatrix(int n) {
int[][] ans = new int[n][n];
int start = 0;//起始位置
int loop = n / 2;//绕圈次数
int count = 1;//计数
while (loop-- != 0) {
//从左到右
for (int i = start; i < n - 1 - start; i++) {
ans[start][i] = count++;
}
//从上到下
for (int i = start; i < n - 1 - start; i++) {
ans[i][n - 1 - start] = count++;
}
//从右到左
for (int i = n - 1 - start; i > start; i--) {
ans[n - 1 - start][i] = count++;
}
//从下到上
for (int i = n - 1 - start; i > start; i--) {
ans[i][start] = count++;
}
start++;
}
if (n % 2 == 1) ans[start][start] = count;//当n为奇数时注意对中间的数字进行赋值
return ans;
}
}
6 - LeetCode 15 三数之和 – 双指针法
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:输入:nums = []
输出:[]
示例 3:输入:nums = [0]
输出:[]
提示:0 <= nums.length <= 3000
-105 <= nums[i] <= 105
思路:
这道题如果考虑使用哈希法来做的话,去重是比较麻烦的,因为题目要求不可以包含重复的三元组,用哈希法的话,一个是去重过程比较繁琐,还有一个是做法很容易超时。哈希法在参考文章中有写。
这道题可以考虑使用双指针法来做,就变得简单很多。
以下这个图很形象地说明了双指针法的解法是怎么进行的(图片转自代码随想录):
先将数组排序,然后按顺序对于每一个值,在它之后的元素的头尾各设一个指针,然后将三个指针指向的值相加,如果和小于0,则左指针向右移动;如果和大于0,则右指针向左移动;如果和等于0,则将三个值存入结果数组,然后左右指针同时向中间移动。
同时还要注意去重问题,当下一个指针指向的值与之前指向的值相同时,就要注意应该跳过这个值。
本题Java代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (nums[i] > 0)
return ans;
if (i > 0 && nums[i] == nums[i - 1])
continue;
int left = i + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
while (right > left && nums[left] == nums[left + 1])
left++;
while (right > left && nums[right] == nums[right - 1])
right--;
left++;
right--;
} else if (sum < 0) {
left++;
} else {
right--;
}
}
}
return ans;
}
}
7 - LeetCode 18 四数之和 – 双指针法
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/4sum
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):
- 0 <= a, b, c, d < n
- a、b、c 和 d 互不相同
- nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
提示:1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
思路:
这道题与《三数之和》类似,都是使用双指针来解,比使用哈希法来的更高效和简单,可以看做是《三数之和》的升级版,做这道题之前可以先把《三数之和》做一下。
这道题与《三数之和》一样需要注意去重问题。与《三数之和》的不同点在于,它使用两个循环遍历数组中每一对数字组合的和来作为《三数之和》中的第一个数,然后剩下的两个数就一样是使用双指针来操作。
另一个不同点在于《三数之和》中判断第一个数如果 num[i] > 0 就直接返回答案,但是这道题不可以说第一、二个数 num[i] > target 或 nums[j] > target 就直接返回答案,为什么呢?因为在《三数之和》中和是0,是固定的,而在这道题中target是不固定的,target有可能是负数,如果target是负数,那么有可能它是由多个大于它的数相加得到的,所以不能进行 num[i] > target 的判断就直接返回答案。
本题Java代码:
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1])
continue;
for (int j = i + 1; j < nums.length; j++) {
if (j > i + 1 && nums[j] == nums[j - 1])
continue;
int left = j + 1;
int right = nums.length - 1;
while (right > left) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum == target) {
ans.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
while (right > left && nums[left] == nums[left + 1])
left++;
while (right > left && nums[right] == nums[right - 1])
right--;
left++;
right--;
} else if (sum < target) {
left++;
} else if (sum > target) {
right--;
}
}
}
}
return ans;
}
}