总结
数组问题可以使用滑动窗口,前缀和,双指针,动态规划, 模拟法技巧
二分查找
思路
- 分别求出左右边界
- 再求解
代码
class Solution {
public int[] searchRange(int[] nums, int target) {
if (nums.length == 0) {
return new int[]{-1,-1};
}
int left = searchLeft(nums, target);
int right = searchRight(nums, target);
return left == -1 ? new int[]{-1,-1} : new int[]{left,right};
}
/**
* 搜索右边界
* @param nums
* @param target
* @return
*/
private int searchRight(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int middle = left + (right - left) / 2;
if (nums[middle] == target) {
left = middle + 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle - 1;
}
}
if (left - 1 < 0) { // left=right+1, left 的区间是[0,num.length]
return -1;
}
return nums[left - 1] == target ? left - 1 : -1;
}
/**
* 搜索左边界
* @param nums
* @param target
* @return
*/
private int searchLeft(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) { //
int middle = left + (right - left) / 2;
if (nums[middle] == target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else if (nums[middle] > target) {
right = middle - 1;
}
}
if (right + 1 >= nums.length) {// right=left-1, left 的区间是[0,num.length], right的区间是[-1,num.length-1],right+1的区间是[0,num.length]
return -1;
}
return nums[right + 1] == target ? right + 1 : -1;
}
}
双指针
27. 移除元素
27. 移除元素
诸如「相同元素最多保留 k 位元素」或者「移除特定元素」的问题
总结
对于诸如「相同元素最多保留 k 位元素」或者「移除特定元素」的问题,更好的做法是从题目本身性质出发,利用题目给定的要求提炼出具体的「保留逻辑」,将「保留逻辑」应用到我们的遍历到的每一个位置。
参考资料 【宫水三叶】一题双解 : 「双指针」&「通用」解法
977.有序数组的平方
滑动窗口
209.长度最小的子数组
思路
对于连续子数组/连续子串,一般想到用滑动窗口方法或者动态规划方法进行优化。
代码
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int minLength = Integer.MAX_VALUE;
int left = 0;
int right = 0;
int sum = 0; // 窗口
while (right < nums.length) {
sum += nums[right];
right++;
while (sum >= target) {
minLength = Math.min(minLength, right - left);
sum -= nums[left];
left++;
}
}
return minLength == Integer.MAX_VALUE ? 0 : minLength;
}
}
前缀和
螺旋数组
54.螺旋矩阵
剑指Offer 29.顺时针打印矩阵
59.螺旋矩阵II
/**
* 按照右-下-左-上的顺序去生成数组
* 什么时候该改变方向?
* 碰到边界或者碰到已经生成的元素。
* 怎么控制方向?
* 定义一个方向数组与方向遍历direction direction%4表示当前方向,每次改变方向后,direction++
* 什么时候跳出循环?
* 当四个方向都行不通的时候
int[][] result = new int[n][n];
int[] dx = new int[]{0,1,0,-1};
int[] dy = new int[]{1,0,-1,0};
int direction = 0;
int val = 1;
int x = 0;
int y = 0;
while(val <= n*n){
result[x][y]=val;
int newX = x+dx[direction%4];
int newY = y+dy[direction%4];
if(newX<0||newX>=n||newY<0||newY>=n||result[newX][newY]!=0){
direction++;
newX = x+dx[direction%4];
newY = y+dy[direction%4];
}
x=newX;
y=newY;
val++;
}
return result;