拿到这个题就想到了暴力算法,先把数组的每个元素平方之后再用个排序。
这种方法最快时间复杂度只能达到o(nlogn),本题的目标是达到o(n)
import "sort"
func sortedSquares(nums []int) []int {
for i:=0;i<len(nums);i++{
nums[i]*=nums[i]
}
sort.Slice(nums,func (i,j int) bool{
return nums[i]<nums[j]
})
return nums
}
在学go语言时这个库函数我用的不多,这里做个详细的学习:
sort.Slice()可以根据自定义规则对切片进行排序,并且特别适合用于元素类型不是基本类型(int,string),可以对结构体进行排序。
例1:
sort.Slice(nums,func (i,j int) bool{
return nums[i]<nums[j]
})
这个例子是对int切片进行排序 例2: 解法思路:利用题目的性质,有序数组,数组中元素平方,在一般情况下,平方后的最小值都在中间,两端可能取到最大值。 我最开始想的就是这么做的,一个for循环负责元素起点,另一个for循环在第一个for循环的指向进行累加判断。 这个写法前19个例子都能过,但是leetcode更新例子了 该算法的时间复杂度是O(N),一个for循环做了两个for循环的事。 代码中有两个关键点是我写滑动窗口时做错的 这个地方第一次写的时候我不用for循环用了if,这就是对这个过程理解的还不够。 其次就是特判:数组所有元素之和小于target,这个时候要返回0 一点改进:虽然说影响不大,这个result初始值:这里一开始我写的math.MaxInt32,虽然说都一样,既然为了取不到,那可以写的更好理解一点,在result能取到的最大值+1即可。所以这里我后来改成了len(nums)+1 本题没什么算法,就是一个模拟题。当时遇到的难点就是边界的处理,和这个转的过程我的脑子就很乱。 这个题要模拟出来就要理清楚这个过程。 最后就是一个特判,就拿上面这个图举例子,转的圈loop = n/2 = 1;那这样内部就会剩下一个格子,这个格子就直接特殊处理赋值。 写的时候的问题 第一个是先进行第一步分配内存,然后再进一步对内部声明切片 2.关于go库函数,没用min()函数,这个函数要自己写。
sort.Slice()需要两个参数,nums是待排序的数组,后面的匿名函数是比较函数,需要接收两个索引i和j,并返回一个bool值。表示在排序中是否将所有i的元素排在索引j之前。这个匿名函数体定义了排序的方式。
当nums[i]
这个例子是对结构体切片排序type Person struct {
Name string
Age int
}
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Clara", 35},
}
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
双指针解法
因此设置left和right指针指向两端,进行比较后构造目标切片,两个指针不断往中间靠拢。最后扫描完所有元素,当left和right相遇后错开就停止。
时间0(n)func sortedSquares(nums []int) []int {
i,j:=0,len(nums)-1
result := make([]int,len(nums))
k:=len(nums)-1
for i<=j{
if nums[i]*nums[i]>nums[j]*nums[j]{
result[k] = nums[i]*nums[i]
i++
k--
}else{
result[k] = nums[j]*nums[j]
j--
k--
}
}
return result
}
209.长度最小的子数组
两个for循环暴力解法
这个就是我第一次写出来的代码,纯粹是按这个想法去写。func minSubArrayLen(target int, nums []int) int {
l := len(nums)
testnum :=0
for k:=0;k<l;k++{
testnum+=nums[k]
}
if testnum < target{
return 0
}
result :=0
for i:=0;i<l;i++{
temp:=0
count:=0
for j:=i;j<l;j++{
if temp>=target{
break
}
temp+=nums[j]
count++
}
if result ==0 {
result = count
}else if count<result && temp >=target{
result = count
}
}
return result
}
会在这个例子爆时间
1000000000
[10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,10000,1000…
所以不用在这个暴力算法浪费太多时间。由于是两个for,所以这里时间是o(n^2)的复杂度滑动窗口做法(双指针)
func min(a, b int)int{
if a > b{
return b
}else{
return a
}
}
func minSubArrayLen(target int, nums []int) int {
result :=len(nums)+1
sum:=0
count:=0
for i,j:=0,0;j<len(nums);j++{
sum+=nums[j]
for sum >= target{
count = j-i+1
result = min(count,result)
sum-=nums[i]
i++
}
}
if result == len(nums)+1{
return 0
}else{
return result
}
}
思路:滑动窗口也就是双指针,这个窗口和上面暴力维护的窗口道理是一样的,但是维护的思路是不一样的。暴力算法维护的双指针是起始指针移动,然后终止指针再往后维护区间。
滑动窗口是终止指针向后移动,当满足条件之后,其实指针再往后移动。for sum >= target{
count = j-i+1
result = min(count,result)
sum-=nums[i]
i++
}
例子:target=100,nums={1,1,1,1,1,1,100}
如果用if就会出现问题,用if我的这个i++就只会移动一次,然后直接到下次循环j又往后移动了。
用循环,这个i就会不断地向后移动,这个滑动窗口才是想要的。
当时虽然写了,但是我觉得我写的太垃圾了,硬生生又去搞了个求和特判。主要是对过程理解的不够,这里特判应该这么写:当你全部元素求和都小于target,那么里面那个内层for循环是根本进不去的,此时这个result就还是保持原值不变,而且这个result本身就是一个不可能取到的值。所以这样特判就写出来了。
59.螺旋矩阵II
所以本题的关键:
1.转多少圈
2.边界如何处理
3.转圈的时候行和列的维护
4.当n为奇数时中间会剩一个位置func generateMatrix(n int) [][]int {
startx,starty := 0,0
count:=1
loop:=n/2
offset := 1
res := make([][]int,n)
for k:=0;k<n;k++{
res[k] = make([]int,n)
}
for loop>0{
i,j:=startx,starty
for ;j<n-offset;j++{
res[i][j]=count
count++
}
for ;i<n-offset;i++{
res[i][j]=count
count++
}
for ;j>starty;j--{
res[i][j]=count
count++
}
for ;i>startx;i--{
res[i][j]=count
count++
}
offset++
startx++
starty++
loop--
}
if n%2 == 1{
res[n/2][n/2]=count
}
return res
}
就是从0,0这个位置开始转圈,在这个正方形每个要处理的边上都必须按同一个规则来,如图所示
每条边上的区间处理都是按左闭右开来处理,按这样来处理代码就会很好写,此时对于边界的处理,此时的边界,我们输入的是n,那么这个外圈1这个地方的边界就应该是n-offset,也就是3-1=2,此时i<2就是刚好处理前面两个格子。对于2这条边也是n-offset,3这条边的边界调节就用startx,4这条边的边界就是starty。
在转圈的过程中,这个二维切片,就统一用i,j代表行列进行维护。
转完一圈之后,要进入内圈,这个时候循环圈数loop–,offset也要相应的往内部缩一格offset++,起始位置都要往内缩一格startx++,starty++,这些都是总结出来的。
1.go语言的二维数组和c++的初始化方式还是有点区别的,我一开始在这里就卡住了。res := make([][]int,n)
for k:=0;k<n;k++{
res[k] = make([]int,n)
}