977.有序数组的平方
题目:给你一个按 非递减顺序 排序的整数数组
nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
思路:
在非递减数列中可能存在负数,绝对值大的负数在平方后数值也大,使平方后的数列可能不是一个非递减的数列。
暴力解法:
- 遍历数组,并原地对每个数组元素进行平方
- 使用java的工具类对数组进行排序。
双指针法:
将数组元素都进行平方后,数组的单调性是先单调递减后单调递增的(原数组全为正数或全为负数也符合这种情况,只是递减或递增的部分长度为0)。
根据这个规律,所求数组的最大值从原数组的两端取得。因此,可以从原数组的两端出发进行遍历,不断比较两端指针指向元素的绝对值,并将较大值平方后记录到目标数组中。
时间复杂度:
空间复杂度:
class Solution {
public int[] sortedSquares(int[] nums) {
int[] result=new int[nums.length];
int slow=0,high=nums.length-1;
for(int i=result.length-1;i>=0;i--){
if(Math.abs(nums[slow])>Math.abs(nums[high])){
result[i]=nums[slow]*nums[slow];
slow++;
}else{
result[i]=nums[high]*nums[high];
high--;
}
}
return result;
}
}
209. 长度最小的子数组
题目:给定一个含有
n个正整数的数组和一个正整数target。 找出该数组中满足其和target的长度最小的 连续子数组[numsl, numsl+1, ..., numsr-1, numsr],并返回其长度。如果不存在符合条件的子数组,返回0。
思路:
题目中要求连续子数组,因此可以使用滑动窗口,窗口内的数组元素为组成连续子数组的元素。
对原数组进行遍历,使用total记录当前窗口元素的和,使用min记录题目所求的连续子数组的最小长度。
对于每一个数组元素,首先移入到窗口中,再判断新的total是否≥target,当total>=target条件满足时,不断重复以下操作:
判断total-窗口第一个元素>=target是否满足,若满足,移除窗口第一个元素,并更新total,并重复此过程,否则,从min和当前窗口长度中取较小者更新min,并结束这一轮操作。
时间复杂度:
示意图如下:
- 按照思路,窗口具有先进先出的特性,可以使用一个队列维持滑动窗口。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
Deque<Integer> window =new LinkedList<>();
Integer min=Integer.MAX_VALUE;
int total=0;
for(int i=0;i<nums.length;i++){
window.add(nums[i]);
total+=nums[i];
while(total>=target){
if(total-window.peek()>=target){
total-=window.poll();
}else{
min=min>window.size()?window.size():min;
break;
}
}
}
return min==Integer.MAX_VALUE?0:min;
}
}
- 由于窗口所维持的子数组是原数组中的连续的几个数组元素,可以通过维护窗口第一个元素和最后一个元素的下标,从而维护当前窗口,而无需要额外的内存空间。
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int start=0,end=0;
int total=0;
int min=Integer.MAX_VALUE;
while(end<nums.length){
total+=nums[end];
while(total>=target){
if(total-nums[start]>=target){
total-=nums[start];
start++;
}else{
min=min>(end-start+1)?(end-start+1):min;
break;
}
}
end++;
}
return min==Integer.MAX_VALUE?0:min;
}
}
59.螺旋矩阵
题目:给你一个正整数
n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix。
思路:
这道题解题的关键在于遍历的区间范围,将二维矩阵从外到里一圈一圈地进行遍历,每一圈的遍历分为四段,依次是从左到右、从上到下、从右到左和从下到上。
每完成一圈遍历,遍历的范围就向内收缩一圈,即给每一个遍历段的起点和终点都收缩一格,这种收缩表现为偏移对起点的偏移,使用offset表示偏移量。
当时,
- 当为偶数时,左右、上下方向的偏移量加起来都为,这意味着矩阵遍历完成;
- 当为奇数时,左右、上下方向的偏移量加起来都为,此时,只有
nums[n/2][n/2],即矩阵最中心的位置还未遍历,而这个值是遍历中最后一个值,即,将这个值填入矩阵最中心的位置,即可结束遍历。
示意图如下:
class Solution {
public int[][] generateMatrix(int n) {
int num=1;
int[][] res=new int[n][n];
for(int offset=0;offset<n/2;offset++){
//left->right
//i:offset
//j:offset->n-offset-2
for(int j=offset;j<=n-offset-2;j++){
res[offset][j]=num++;
}
//up->down:
//j:n-offset-1
//i:offset->n-offset-2
for(int i=offset;i<=n-offset-2;i++){
res[i][n-offset-1]=num++;
}
//right->left
// i:n-offset-1
// j:n-offset-1 -> offset+1
for(int j=n-offset-1;j>=offset+1;j--){
res[n-offset-1][j]=num++;
}
// down -> up
// j:offset
// i:n-offset-1 -> offset+1
for(int i=n-offset-1;i>=offset+1;i--){
res[i][offset]=num++;
}
}
if(n%2!=0){
res[n/2][n/2]=n*n;
}
return res;
}
}