Leetcode 977. 有序数组的平方
题目链接:977. 有序数组的平方
思路:
想到了用双指针,即一个指针指向第一个负数,另一个指针指向第一个非负数。这种解法就是将元素从小到大开始填充,但是这种解法写起来太麻烦,因为需要判断两个指针分别走到尽头的情况,不然就会有数组越界的错误。后面想了下,其实用双向指针更好。
目前看来,双指针的题,对于我来说,易错点或者说把握不好的点在于两个指针分别指向的位置。写代码前,还是得多想想指针指向的位置。
P.S. 本题暴力解法时间复杂度为O(nlogn)
解题:成功
代码实现(√)
时间复杂度:O(n)
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int[] result = new int[n];
int left = 0;
int right = n - 1;
int idx = n - 1;
while(left <= right){
if(Math.abs(nums[left]) <= Math.abs(nums[right])){
result[idx] = nums[right] * nums[right];
right--;
}
else{
result[idx] = nums[left] * nums[left];
left++;
}
idx--;
}
return result;
}
}
另一个版本的双指针(这种太复杂了,但是逻辑上没问题)
// 可以跳过不看
class Solution {
public int[] sortedSquares(int[] nums) {
int n = nums.length;
int positive = 0;
int[] arr = new int[n];
while(positive < n && nums[positive] < 0){
positive++;
}
if(positive == 0){
for(int i = 0; i < n; i++){
nums[i] = nums[i] * nums[i];
}
return nums;
}
else{
int negative = positive - 1;
int idx = 0;
while(negative >= 0 && positive < n){
if(Math.abs(nums[negative]) <= nums[positive]){
arr[idx] = nums[negative] * nums[negative];
negative--;
}
else{
arr[idx] = nums[positive] * nums[positive];
positive++;
}
idx++;
}
while(negative < 0 && positive < n){
arr[idx] = nums[positive] * nums[positive];
positive++;
idx++;
}
while(negative >= 0 && positive >= n){
arr[idx] = nums[negative] * nums[negative];
negative--;
idx++;
}
}
return arr;
}
}
Leetcode 209.长度最小的子数组
题目链接:209.长度最小的子数组
思路:
看完题的第一想法是前缀和,但是想了下,这样不好解。而且印象中遇到过类似的题,不是用的前缀和。提示用滑动窗口,试了下,可行。滑动窗口,本质上还是双指针。
解题:成功
代码实现
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int n = nums.length;
int left = 0;
int len = Integer.MAX_VALUE;
int sum = 0;
for (int right = 0; right < n; right++){
sum += nums[right];
while(sum >= target){
len = Math.min(len, right - left + 1);
sum -= nums[left];
left++;
}
}
return len == Integer.MAX_VALUE ? 0 : len;
}
}
时间复杂度:O(n)
主要是看每一个元素被操作的次数,每个元素在进入滑动窗口时操作一次,出去时操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。
Leetcode 59. 螺旋矩阵 II
题目链接:59. 螺旋矩阵 II
思路:
看完题的第一想法是前缀和,但是想了下,这样不好解。而且印象中遇到过类似的题,不是用的前缀和。提示用滑动窗口,试了下,可行。滑动窗口,本质上还是双指针。
解题:成功(方法一)
代码实现
方法一
class Solution {
public int[][] generateMatrix(int n) {
int[] x = {0, 1, 0, -1};
int[] y = {1, 0, -1, 0};
int idx = 0;
int[][] res = new int[n][n];
int num = 1;
int total = n * n;
int i = 0;
int j = 0;
while(num <= total){
res[i][j] = num;
num++;
int nextI = i + x[idx];
int nextY = j + y[idx];
if(nextI < 0 || nextI >= n || nextY < 0 || nextY >= n || res[nextI][nextY] != 0){
idx = (idx + 1) % x.length;
}
i += x[idx];
j += y[idx];
}
return res;
}
}
方法二:关键点在于保持区间定义
class Solution {
public int[][] generateMatrix(int n) {
int startx = 0;
int starty = 0;
int loop = 1;
int offset = 1;
int[][] res = new int[n][n];
int count = 1;
int i = 0;
int j = 0;
//坚持左闭右开区间定义
while(loop <= n/2){
//上方从左往右遍历
for(j = starty; j < n - offset; j++){
res[startx][j] = count++;
}
//右方从上往下遍历
for(i = startx; i < n - offset; i++){
res[i][j] = count++;
}
//下方从右往左遍历
for(; j > starty; j--){
res[i][j] = count++;
}
//左方从下往上遍历
for(; i > starty; i--){
res[i][j] = count++;
}
startx++;
starty++;
offset++;
loop++;
}
if(n % 2 == 1){
res[n/2][n/2] = count;
}
return res;
}
}
数组篇总结
- 二分法(循环不变量原则,严格遵守区间定义)
- 双指针法
- 滑动窗口