20230912刷题记录
参考【代码随想录】来刷的。
4 排序数组中的两数之和、5 三数之和来自《剑指Offer专项突破版》。
关键词:双指针、数组
1 有序数组的平方
977.有序数组的平方
力扣题目链接
1.1 暴力法
将该数组平方后排序,显然时间复杂度O(nlogn)。
1.2 双指针
该数组在平方之前是有序的,故平方后数组中的最大值在数组的两端,即要么在最左边,要么在最右边。
此时可以考虑双指针法了。
定义一个新数组result,和原数组一样的大小,让k指向result数组的最后一个元素。
i、j分别指向原数组的第一个元素和最后一个元素。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
时间复杂度为O(n),因为创建了一个长度为n的新数组,故空间也是O(n)。
class Solution {
public int[] sortedSquares(int[] nums) {
int i = 0;
int j = nums.length - 1;
int k = nums.length - 1;
int[] result = new int[nums.length];
while (i <= j) {
if (nums[i]*nums[i] >= nums[j]*nums[j]) {
result[k] = nums[i] * nums[i];
++i;
} else {
result[k] = nums[j] * nums[j];
--j;
}
--k;
}
return result;
}
}
2 长度最小的子数组
209.长度最小的子数组
力扣题目链接
双指针
使用两个指针i、j,i指向子数组的第一个元素,j指向子数组的最后一个元素,初始值都为0。
如果子数组的总和满足条件,使 i 向右移动。
如果子数组的总和不满足条件,使 j 向右移动。
i 每向右移动一步,相当于从子数组的最左边删除一个元素,子数组的长度也减1。由于数组中都是正整数,删除子数组中的元素,会使子数组的总和减少。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int i = 0;
int sum = 0;
int minLength = Integer.MAX_VALUE;
for (int j = 0; j < nums.length; j++) {
sum += nums[j];
while (sum >= target) {
minLength = Math.min(minLength, j - i + 1);
sum -= nums[i];
i++;
}
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
}
3 螺旋矩阵II
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
按照固定规则来遍历,左闭右开。
class Solution {
public int[][] generateMatrix(int n) {
int[][] result = new int[n][n];
int count = 1;
int loop = 0;
while (loop <= n / 2) {
for (int i = loop; i < n - 1 - loop; i++) {
result[loop][i] = count++;
}
for (int i = loop; i < n - 1 - loop; i++) {
result[i][n - 1 - loop] = count++;
}
for (int i = n - 1 - loop; i > loop ; i--) {
result[n- 1 - loop][i] = count++;
}
for (int i = n - 1 - loop; i > loop ; i--) {
result[i][loop] = count++;
}
loop++;
}
// 对奇数阶矩阵的中心的处理
if (n % 2 == 1) {
result[n / 2][n / 2] = count;
}
return result;
}
}
4 排序数组中的两数之和
给定一个递增排序的整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 target 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [2,3,4], target = 6
输出:[1,2]
4.1 哈希表
扫描数组,假设扫描到数字i,如果哈希表中存在另一个数字target - i,那么就找到了;如果不存在则将i存入哈希表。
显然该算法的时间复杂度是O(n),空间复杂度是O(n)。
4.2 双指针
使用两个指针i、 j,分别指向数组的第一个元素和最后一个元素。
如果nums[i] + nums[j] > target
,i向右移动一位,
如果nums[i] + nums[j] < target
,j向左移动一位。
显然该算法的时间复杂度时O(n),空间复杂度是O(1)。
public class Order6 {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
while (i < j && numbers[i] + numbers[j] != target) {
if (numbers[i] + numbers[j] > target) {
j--;
} else {
i++;
}
}
return new int[]{i, j};
}
}
5 三数之和
力扣题目链接
如果数组是有序的,可固定一个数 i,然后找出两数之和为 -i 的两个数即可。
显然时间复杂度是O(n^2)。
如果该数组无序,则需要先把数组排序,时间复杂度为O(nlogn)+O(n^2),
仍然是O(n^2)。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if (nums.length < 3)
return null;
List<List<Integer>> result = new ArrayList<>();
int i = 0;
Arrays.sort(nums);
while (i < nums.length - 2) {
twoSum(nums, i, result);
int temp = nums[i];
while (temp == nums[i] && i < nums.length - 2)
++i;
}
return result;
}
private void twoSum(int[] nums, int i, List<List<Integer>> result) {
int j = i + 1;
int k = nums.length - 1;
while (j < k) {
if (nums[i] + nums[j] + nums[k] == 0) {
result.add(Arrays.asList(nums[i], nums[j], nums[k]));
int temp = nums[j];
while (temp == nums[j] && j < k)
++j;
} else if (nums[i] + nums[j] + nums[k] > 0)
--k;
else
++j;
}
}
}