977.有序数组的平方
链接
思路
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
如果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]; 。
时间复杂度Ologn
代码
func sortedSquares(nums []int) []int {
lenth:=len(nums)
left,right:=0,lenth-1
res:=make([]int,lenth)
for left<=right{
if nums[left]*nums[left]<nums[right]*nums[right]{
res[lenth-1]=nums[right]*nums[right]
right--
}else{
res[lenth-1]=nums[left]*nums[left]
left++
}
lenth--
}
return res
}
困难
最大的平方数只会在数组两边,即头部和尾部出现,进而想到头尾双指针。
209.长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
链接
思路
接下来就开始介绍数组操作中另一个重要的方法:滑动窗口。
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
那么滑动窗口如何用一个for循环来完成这个操作呢。
首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。
如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?
此时难免再次陷入 暴力解法的怪圈。
所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。
那么问题来了, 滑动窗口的起始位置如何移动呢?
在本题中实现滑动窗口,主要确定如下三点:
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
解题的关键在于 窗口的起始位置如何移动。
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
代码
func minSubArrayLen(target int, nums []int) int {
i:=0
l:=len(nums)
sum:=0
res:=l+1
for j:=0;j<l;j++{
sum+=nums[j]
for sum>=target{
templ:=j-i+1
sum-=nums[i]
i++
if templ<res{
res=templ
}
}
}
if res==l+1{
return 0
}
return res
}
困难
滑动窗口要确定窗口内的内容,如何移动窗口的结束位置,如何移动窗口的起始位置。窗口的终止位置一般由外层循环的索引表示。
59.螺旋矩阵II
链接
思路
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
填充上行从左到右
填充右列从上到下
填充下行从右到左
填充左列从下到上
由外向内一圈一圈这么画下去。
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
时间复杂度On2
代码
func generateMatrix(n int) [][]int {
num:=1
res:=make([][]int,n)
for ri:=0;ri<n;ri++{
res[ri]=make([]int,n)
}
loop,center:=n/2,n/2
sx,sy:=0,0
offset:=1
for loop>0{
i,j:=sx,sy
for ;j<n-offset;j++{
res[i][j]=num
num++
}
for ;i<n-offset;i++{
res[i][j]=num
num++
}
for ;j>sy;j--{
res[i][j]=num
num++
}
for ;i>sx;i--{
res[i][j]=num
num++
}
sx++
sy++
offset++
loop--
}
if n%2==1{
res[center][center]=num
}
return res
}
困难
注意循环不变量,区间始终选择左闭右开。
要区分n是单数和双数的两种情况。
今日收获
了解了更多双指针的用法,同时加深了对循环不变量的理解和使用。