Go 算法
每天5道,开心快乐每一天
一点都不开心 哈哈哈哈哈哈
-2.1
day 1 1.22(1.23 1.25 1.29)
1.23 已复习
704. 二分查找
力扣题目链接
//左闭右开
func search(nums []int, target int) int {
right := len(nums)
left := 0;
for left<right{mid := left+(right-left)/2if nums[mid]==target{return mid}else if nums[mid]<target{left = mid+1}else{right = mid}
}
return -1
}
/*
思路:
区间不变量:左闭右开,说明right所占的数是没有意义的
所以right= mid不矛盾(接下来寻找right左边的一切数)
而如果左边的数小于target,那么left = mid+1,这样在之后的代码中就不需要进行比较了
*/
35.搜索插入位置
力扣题目链接
//左闭右开
func searchInsert(nums []int, target int) int {left,right := 0,len(nums)-1for left<=right{mid := left+(right-left)/2if(target==nums[mid]){return mid}else if(target>nums[mid]){left = mid+1}else{right = mid-1 }}//for 循环要能出来就是right<leftreturn right+1//如果num在数组中间//right +1会变成left,而left满足条件大于nums[mid]//如果是左闭右开就是right
}
//暴力解法
//无非就是几种情况,所以还是比较简单
func searchInsert(nums []int, target int) int {for i:= 0; i<len(nums); i++{if i==0&&nums[i]>target{return i}else if nums[i]==target{return i}else if(nums[i]> target){return i}if i==len(nums)-1&&target>nums[i]{return len(nums)}}return -1
}
34. 在排序数组中查找元素的第一个和最后一个位置
力扣链接
func searchRange(nums []int, target int) []int {if len(nums)==1&&target == nums[0]{return []int{0,0}}
for i := 0;i<len(nums);i++{if target == nums[i] {if i==len(nums)-1{return []int{i,i}}else{flag := ifor j:=flag ;j<len(nums);j++{if(nums[j]!=target){flag2 := j-1return []int{flag,flag2}break}else if(len(nums)==j+1&&nums[j] == target){flag2:=jreturn []int{flag,flag2}}}}}
}
return []int{-1,-1}
}
//方法二:
func searchRange(nums []int, target int) []int {left := leftBoard(nums,target)right := rightBoard(nums,target)if(left==-2||right==-2){return []int{-1,-1}}else if(right-left>1){return []int{left+1,right-1}}else{return []int{-1,-1}}
}
func rightBoard(nums[]int,target int) int{//寻找第一个大于它的数,或者超出边界board :=-2right := len(nums)-1left := 0for left<=right{mid := left+(right-left)>>1if(nums[mid]<=target){left = mid+1//其实最终这里的left就不符合if的条件了,我们的for循环终止条件是left>right//想一下,如果if条件一直成立,到最后nums[board]肯定会大于targetboard = left//最终会大于这个数}else {right=mid -1}}return board
}
func leftBoard(nums[]int,target int) int{//寻找最大的小于target的数
board :=-2right := len(nums)-1left := 0for left<=right{mid := left+(right-left)>>1if(nums[mid]>=target){right=mid -1//最终小于这个数board =right}else {left = mid+1}}return board
}
69.x 的平方根(opens new window)](https://leetcode.cn/problems/sqrtx/)
func mySqrt(x int) int {
left := 0
right := x
temp := -1
for left<=right{mid := left+(right-left)>>1if mid*mid<=x{//到最后temp会等于这个数左边最接近的数temp = mid//到达等于的时候,后面就只会加载else部分了,直到for循环的条件结束//其实这里可以优化一下,但是感觉优化的余地还是有限//可以确定的是我们在不断逼近最靠近它且小于它的数left = mid+1}else {right = mid-1}
}
return temp
}
367.有效的完全平方数(opens new window)](https://leetcode.cn/problems/valid-perfect-square/)
func isPerfectSquare(num int) bool {left,right := 0,numfor left<=right{mid := left+(right-left)>>1square := mid*midif(square>num){right = mid-1}else if(square<num){left = mid+1}else{return true}}return false
}
day 2 1.23(1.24 1.26 1.30)
27. 移除元素
力扣题目链接
func removeElement(nums []int, val int) int {res := 0//这个是用来赋值的指针for i := 0;i<len(nums);i++{if nums[i] != val{nums[res] = nums[i]res++//指针移动}}return res
}
func removeElement(nums []int, val int) int {slow := 0//这个是用来赋值的指针fast := 0for i := 0 ;i<len(nums);i++{if(nums[i]!=val){nums[slow] = nums[i]slow++fast++}else if(nums[i] == val){fast++}}return slow
}
26.删除排序数组中的重复项(opens new window)
//这个是自己写的暴力方法
func removeDuplicates(nums []int) int {index := 0temp := nums[0]for i:=0;i<len(nums);i++{if i==0{nums[index] = nums[i]index++}if temp!=nums[i] {temp = nums[i]nums[index] = nums[i]index++}}nums = nums[:index]return index
}//改进一下func removeDuplicates(nums []int) int {slow := 0fast := 1for i:= 0;i<len(nums);i++{if nums[fast] != nums[slow]{slow++nums[slow] = nums[fast]}}
}
283.移动零(opens new window)](https://leetcode.cn/problems/move-zeroes/)
func moveZeroes(nums []int) {
index := 0for i:=0;i<len(nums);i++{if nums[i]!=0 {nums[index] = nums[i]index++}
}
for i:= index;i<len(nums);i++{nums[i] = 0
}
}
844.比较含退格的字符串(opens new window)](https://leetcode.cn/problems/backspace-string-compare/)
string的底层数据结构就是byte数组。
func backspaceCompare(s string, t string) bool {
s1 := build(s)
t1 := build(t)
if s1==t1 {return true}else {return false}
}
func build(s string)string{str := []byte{}for i:= range s{if s[i]!='#'{str = append(str,s[i])}else if(len(str)!=0){str = str[:len(str)-1]}}return string(str)/*这个代码是做不到的,因为bt[index]指定了数组中的某个序列,但事实上这个序列是未知的,越界的可能性很大,所以违法bt := []byte{}index :=0for i := range s{if s[i]!='#'{bt[index] = s[i]}else if(index >0){bt = bt[:index--]}}*/
}
在Go语言中,i++
和j--
是后缀操作符,它们返回的是操作前的值。但是在赋值语句中,我们需要使用操作后的值进行交换。因此,在这种情况下,i++
和j--
不能替代i = i+1
和j = j-1
。
具体来说,在可变函数中,arr[i], arr[j] = arr[j], arr[i]
的交换操作需要使用i+1
和j-1
的值进行赋值。如果使用后缀操作符,那么i+1
和j-1
会在交换之前进行操作,导致赋值的结果出现错误。
因此,在上述代码中,使用i++, j--
代替i = i+1, j = j-1
是不正确的,因为后者提供了正确的操作顺序,确保了交换的正确性。
func sortedSquares(nums []int) []int {
result :=[]int{}
i,j:=0,len(nums)-1
for i<=j{if nums[i]*nums[i]>nums[j]*nums[j]{result = append(result,nums[i]*nums[i])i++}else{result = append(result,nums[j]*nums[j])j--}
}
reverseArray(result)
return result
}
func reverseArray(nums []int)[]int{for i,j:=0,len(nums)-1;i<j;i,j= i+1,j-1{nums[i],nums[j] = nums[j],nums[i]}return nums
}
for i, j := 0, len(arr)-1; i < j; i = i+1,j=j-1 {arr[i], arr[j] = arr[j], arr[i]}为什么在go语言中这么写是错误的
在Go语言中,使用i = i+1, j = j-1
的方式在这种具体情况下是错误的,因为它不会按预期更新i
和j
的值。
在Go语言中,多重赋值是同时进行的,而不是按顺序执行的。依据语言规范,多重赋值操作中的右侧表达式中的变量需要在左侧表达式求值之前计算。因此,在表达式i = i+1, j = j-1
中,i
的新值(i+1
)会在右侧的j = j-1
表达式求值之前计算,这就导致了错误的结果。
977.有序数组的平方(opens new window)](https://leetcode.cn/problems/squares-of-a-sorted-array/)
func sortedSquares(nums []int) []int {
result :=[]int{}
i,j:=0,len(nums)-1
for i<=j{if nums[i]*nums[i]>nums[j]*nums[j]{result = append(result,nums[i]*nums[i])i++}else{result = append(result,nums[j]*nums[j])j--}
}
reverseArray(result)
return result
}
func reverseArray(nums []int)[]int{for i,j:=0,len(nums)-1;i<j;i,j= i+1,j-1{nums[i],nums[j] = nums[j],nums[i]}return nums
}/*
题目说是从小到大,但我们要找到最小值比较难
所以找最大值好一点
思路:左右两边肯定有最大值,我们安插两个指针放在两边
然后再让两个指针往中间移动
再定义一个数组来储存我们比较后得到的值
*/
day3 1.24(1.25 1.27 1.31)
1.25 have reviewed
59.螺旋矩阵II
力扣题目链接
func generateMatrix(n int) [][]int {res := make([][]int,n)//这个是设置n行for i:=0;i<n;i++{res[i] = make([]int,n)}loop := n/2count :=1x ,y :=0,0hinder := 1//圈圈的范围是由x,y和hinder构成的for loop>0{//等会试下loop--看行不行i,j := x,yfor ;j<n-hinder;j++{res[i][j] = countcount++}for ;i<n-hinder;i++{res[i][j] = countcount++}for ;j>y;j--{res[i][j] = countcount++}for ;i>x;i--{res[i][j] = countcount++}loop--x++y++hinder++
}
if n%2!=0{res[n/2][n/2] = count
}
return res
}
54. 螺旋矩阵
//这道题目与上一道题目的不同点就在于设置了四个边界,但是没有设置loop
//循环条件就变成了,left<right and up <down
func spiralOrder(matrix [][]int) []int {
if(len(matrix)==0){return []int{}//我们是将其转为一行,并且是按照顺序排列的
}
left,right,up,down := 0,len(matrix[0])-1,0,len(matrix)-1
res := []int{}
for left<right&&up<down{for i:= left;i<right;i++{res = append(res,matrix[up][i])} for i:=up;i<down;i++{res = append(res,matrix[i][right])}for i:=right;i>left;i--{res = append(res,matrix[down][i])}for i:=down;i>up;i--{res = append(res,matrix[i][left])}left++right--up++down--
}//如果是奇数就会出现这么一个情况
if(left == right){for i:= up;i<=down;i++{res = append(res,matrix[i][left])}
}else if(up == down ){for i:=left;i<=right;i++{res = append(res,matrix[down][i])}
}//可以发现如果为偶数,反而是不会相等的0,3 ->2,1
//只有奇数会相同,相同也就意味着要补一行或者一列
//列为奇数补行,行为奇数补列
return res
}
203. 移除链表元素
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
func removeElements(head *ListNode, val int) *ListNode {dummyHead := &ListNode{}dummyHead.Next = headcur := dummyHeadfor cur!=nil && cur.Next!=nil{//cur 为空,说明当前链表已经遍历完成//cur.Next为空说明,不需要再检查下一个节点的值了//->cur.Next为空是为了防止head为空链表if cur.Next.Val == val{cur.Next = cur.Next.Next//这里就不需要移动了,因为cur.Next is new}else{cur = cur.Next}}return dummyHead.Next//删除头节点的情况,dummyHead变成头节点
}
/*
这种处理方式是为了保证在链表操作中不会访问到空指针,从而提高代码的鲁棒性。
*/
707. 设计链表
type SingleNode struct{Val intNext *SingleNode
}
type MyLinkedList struct {dummyHead *SingleNodeSize int
}func Constructor() MyLinkedList {
//创造一个新节点,赋初始值为-999 下一个值为nil
//注意赋值的时候需要打逗号
newNode := &SingleNode{-999,nil,
}
return MyLinkedList{dummyHead: newNode,Size: 0,
}
}func (this *MyLinkedList) Get(index int) int {
//和数组一样不能等于
if(this == nil||index<0||index>=this.Size){return -1
}
cur := this.dummyHead.Next
for i:=0;i<index;i++{cur = cur.Next
}
return cur.Val
}func (this *MyLinkedList) AddAtHead(val int) {
//增加Size
newNode := &SingleNode{Val:val}
newNode.Next = this.dummyHead.Next
this.dummyHead.Next = newNode
this.Size++
}func (this *MyLinkedList) AddAtTail(val int) {
//只要下一个不是空的,就可以连接上
//注意Size
newNode := &SingleNode{Val:val}
cur := this.dummyHead
for cur.Next != nil{cur = cur.Next
}
cur.Next = newNode
this.Size++
}func (this *MyLinkedList) AddAtIndex(index int, val int) {
//判断index的条件
if index<0{index = 0
}else if index > this.Size{return
}
newNode := &SingleNode{Val:val}
cur := this.dummyHead
for i:= 0;i<index;i++{cur = cur.Next
}
newNode.Next = cur.Next
cur.Next = newNode
this.Size++
}func (this *MyLinkedList) DeleteAtIndex(index int) {
//同样对index进行判断首先
//别忘了size
if index<0||index>=this.Size{return
}
cur := this.dummyHead
for i:=0;i<index;i++{cur = cur.Next
}
cur.Next = cur.Next.Next
this.Size--
}/*** Your MyLinkedList object will be instantiated and called as such:* obj := Constructor();* param_1 := obj.Get(index);* obj.AddAtHead(val);* obj.AddAtTail(val);* obj.AddAtIndex(index,val);* obj.DeleteAtIndex(index);*/
//其实还是挺细的,这道题要多做
type SingleNode struct{Val intNext *SingleNode
}
type MyLinkedList struct {//我需要虚拟头节点,所以要设置一个额外的struct,这个struct包含val 和 next//Size在这个里面dummyNode *SingleNodeSize int
}func Constructor() MyLinkedList {
//创造一个新节点,赋初始值为-999 下一个值为nil
//注意赋值的时候需要打逗号
//注意需要返回一个节点
newNode := &SingleNode{Val:-999,Next:nil,
}
return MyLinkedList{dummyNode: newNode,Size:0,
}
}func (this *MyLinkedList) Get(index int) int {
//和数组一样不能等于边界值,size并不从零数起
if index<0 || index>=this.Size|| this==nil{return -1
}
cur := this.dummyNode.Next
for i:= 0;i<index;i++{cur = cur.Next
}
return cur.Val
}func (this *MyLinkedList) AddAtHead(val int) {
//增加Size
newNode := &SingleNode{Val:val}
newNode.Next = this.dummyNode.Next
this.dummyNode.Next = newNode
this.Size++
}func (this *MyLinkedList) AddAtTail(val int) {
//只要下一个不是空的,就可以连接上
//注意Size
cur := this.dummyNode
for cur.Next!=nil{cur = cur.Next
}
newNode := &SingleNode{Val:val}
cur.Next = newNode
this.Size++
}func (this *MyLinkedList) AddAtIndex(index int, val int) {
//判断index的条件
if index<0{index = 0
}else if(index>this.Size){return
}
cur := this.dummyNode
for i:=0;i<index;i++{cur = cur.Next
}//这个是要遍历到index前面的一个节点
newNode := &SingleNode{Val:val}
newNode.Next = cur.Next
cur.Next = newNode
this.Size++
}func (this *MyLinkedList) DeleteAtIndex(index int) {
//同样对index进行判断首先
//别忘了size
if(index<0||index>=this.Size){return
}
cur := this.dummyNode
for i:=0;i<index;i++{cur = cur.Next
}//到index前面那个节点
if cur.Next!=nil{
cur.Next = cur.Next.Next
}
this.Size--
}/*** Your MyLinkedList object will be instantiated and called as such:* obj := Constructor();* param_1 := obj.Get(index);* obj.AddAtHead(val);* obj.AddAtTail(val);* obj.AddAtIndex(index,val);* obj.DeleteAtIndex(index);*/
day 4| 1.25 (1.26 1.28 2.1)
1.28 got it
206. 反转链表
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
func reverseList(head *ListNode) *ListNode {var pre *ListNode//这个是虚拟头节点//因为我们一开始不能假定有两个节点 题目中说的cur := headfor cur !=nil{//我们不能指定它的下一个node不是nil,原因就是最后一个节点就是nil,也需要反转temp := cur.Nextcur.Next = prepre = curcur = temp}return pre
}
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
func swapPairs(head *ListNode) *ListNode {cur := &ListNode{Next:head,}pre := cur //为了减少资源浪费,我们直接用head,为什么不用cur是因为后来的head不是原来的head了,地址发生了转变,但是我们第三行的head是头节点,这毋庸置疑for head!=nil && head.Next!=nil{temp := head.Next.Nextpre.Next = head.Nexthead.Next.Next = headhead.Next = temppre = headhead = temp}return cur.Next}
//需要画图理解
2.1 就相当于我cur固定一下头节点的位置,保证我反转之后海能找到头节点,当然头节点的位置已经改变了
9. 删除链表的倒数第 N 个结点
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {dummyHead := &ListNode{Next:head}cur := headprev :=dummyHead//count :=1//因为本身差了一个//就相当于前面有个牛,你和牛的距离一定要是n+1位置,这样你才能到倒数第n+1个位置//count 是我与牛之间的距离//最终牛会到达nil->len+1for cur!=nil{cur = cur.Nextif count>n{//此时count == n+1,满足条件,我们被牛牵动prev = prev.Next}count++}prev.Next = prev.Next.Nextreturn dummyHead.Next
}
面试题 02.07. 链表相交
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
//这道题还考察了审题
//我们需要发现在交点之后,剩下的一段链表是相同的
//也就是说两端链表是包含或真包含的一个关系
//所以从距离相同的起点开始检索
func getIntersectionNode(headA, headB *ListNode) *ListNode {//我们可以发现从后往前面数,更容易得到我们想要的答案//但是太浪费空间了//换个思路就是把两者的长度计算出来,计算出差值,让长度长的//从表头自动增加差值,这样两个链表就可以对齐curA := headAcurB := headBcountA :=0countB := 0for headA!=nil{countA++headA = headA.Next}for headB!=nil{countB++headB = headB.Next} var gap intvar fast,slow *ListNodeif countA>countB{gap = countA - countBfast = curAslow = curB}else{gap = countB - countAfast = curBslow = curA}for i:=0;i<gap;i++{fast = fast.Next}for fast!=slow{fast = fast.Nextslow = slow.Next}return fast
}
/*
for slow!=nil{if fast==slow{return fast}fast = fast.Nextslow=slow.Next*/
142.环形链表II
/*** Definition for singly-linked list.* type ListNode struct {* Val int* Next *ListNode* }*/
func detectCycle(head *ListNode) *ListNode {slow,fast:=head,headfor fast!=nil && fast.Next!=nil{//走两步要保证自己本身那一步没问题,然后第一部没问题,第二步有问题没关系,不违法slow = slow.Nextfast = fast.Next.Nextif slow == fast{for slow!=head{slow = slow.Nexthead = head.Next}return slow}}return nil
}
242.有效的字母异位词
在Go语言中,rune
类型实际上是一个别名,底层类型是 int32
。因此,rune('a')
其实就是将字符 ‘a’ 转换为对应的 Unicode 码点,这个码点的类型是 int32
。
在Go中,字符是一个整数值,代表 Unicode 码点。当您将字符与整数相减时,实际上是在进行 Unicode 码点之间的减法操作。这种操作是合法的,因为字符在底层是整数类型(rune
类型)。
例如,字符 ‘a’ 的 Unicode 码点是 97,所以 rune('a')
的结果就是 97
。因此,表达式 r-rune('a')
就是字符 r
的 Unicode 码点减去字符 ‘a’ 的 Unicode 码点。
这种操作在实现字符之间的相对位置关系时非常有用,因为 Unicode 码点的顺序通常与字母的顺序一致。在实现字母异位词检查时,这样的计算可以方便地映射字符到数组索引。
字符遇到Unicode码点是可以相加减的
Unicode码点就是一个常量,不是一个int类型的值
func isAnagram(s string, t string) bool {record := [26]int{}for _,r := range s{record[r-97]++}for _,r := range t{record[r-97]--}return record == [26]int{}
}
349. 两个数组的交集
func intersection(nums1 []int, nums2 []int) []int {count := make(map[int]int)res := make([]int,0)for _,j := range nums1{count[j]++}for _,j := range nums2{if(count[j]!=0){res = append(res,j)count[j] = 0//保证每个元素唯一}}return res
}
day 5|1.26 (1.27 1.29 2.2)
第202题. 快乐数
func isHappy(n int) bool {
//我的想法
//将int转为数组
//对数组里面的求和
//对求和进行判断
//再对求和进行分组//我的疑问:
//会有无限循环变不到1的值怎么办//answer:无限循环也就意味着sum会重复出现,只要sum重复出现,那么设置为false//卡尔的想法:
//用map类型将值设置为bool,键设置为n
//我们可以利用算法将sum提取出来,不断提取sumcount := make(map[int]bool)
temp :=0
for n!=1&&!count[n]{//这里为什么可以直接访问count[n]//这是因为count是map类型的,即便没有这个键,我们也可返回零值//map是用来检测键是否出现过,出现过的话就不是零值//count[n]是用来去重的// //在Go语言中,bool 类型的零值是 false。//如果之前这个数就出现过,count[n]返回true,for循环执行不下去的n,count[n] =getSum(n),true
}
return n==1
}
func getSum(n int) int{sum := 0for n>0{sum += (n%10)*(n%10)n = n/10}return sum
}
1. 两数之和
func twoSum(nums []int, target int) []int {
//我的想法:
//下面的target是正在遍历的数
//将全部数目都作为map的键,然后将sum-target作为map的值
//然后我们在遍历map,如果刚好map[n] = sum-n
//刚好有这个键存在,其实这个代码还是不好实现//哈希表:元素是否出现过
//将元素全部存入哈希表,如果sum-target出现过,就over//我们并不需要全部存入哈希表,只需要遍历一次就好
//遍历target,在map中搜索sum-target
//如果有返回,如果没有,存入到map中
temp := make(map[int]int)
for i,key := range nums{if prev,ok :=temp[target-key];ok{//如果temp中没有对应项,返回falsereturn []int{i,prev}//注意格式if 赋值操作;条件{}}else{temp[key] = i}
}
return []int{}
}
454. 四数相加 II
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {/*思路:我们遍历nums1和nums2,将他们各元素的相加值存入map中接着遍历num3 and nums4,如果我们的键中有-nums3-nums4存在,count++*/count1 := make(map[int]int)count :=0 for _,j := range nums1{for _,h:=range nums2{count1[j+h]++}}for _,i:=range nums3{for _,j := range nums4{if _,ok:=count1[-i-j];ok{count += count1[-i-j]}}}return count
}
383. 赎金信
func canConstruct(ransomNote string, magazine string) bool {ranMap := make(map[rune]int)for _,j:=range magazine{ranMap[j]++}for _,j := range ransomNote{if ranMap[j]==0{return false}ranMap[j]--}return true
}
15. 三数之和
func threeSum(nums []int) [][]int {
//首先,对数组进行排序操作
//接下来的思路,i,j,k已经不是重点了
//我们首先固定a,(a<0并且要去重)然后固定b和c的起始位置
//找到之后,设置边界l,r
//因为数组中元素会有重复,找到之后在原来数组当中去重
//对于相等的元素直接跳过,l和r都移动到不能再左边和不能再右边
sort.Ints(nums)
res := [][]int{} //用来存储结果值
for i:=0;i<len(nums)-2;i++{//注意i的范围a:=nums[i]if a>0{break}if i>0 && a==nums[i-1]{continue}//如果a==nums[i-1],那么在三元组中,作为a的这个元素重复了//举个例子//target = 10//{-1,-1,9,2,3}//是不是秒懂{-1,9,2}会出现重复//我们对前一个元素进行去重,意味着我们对一个遍历的过程去重//这个要自己在脑子里面转一下l,r :=i+1,len(nums)-1for l<r{b,c := nums[l],nums[r]//到for循环里面再去定义,省空间if a+b+c == 0{//对b,c去重res = append(res,[]int{a,b,c})for l<r && nums[l] == b{//注意这里是for,我们要直接把重复的元素全部省略,再到上层for循环才有意义l++}for l<r && nums[r] == c{r--}}else if a+b+c<0{l++}else{r--}}//我们这里要从i入手,这个是起点,我们要从i前面入手,因为b在后面,我们不能将他们省略了 {-1,-1,2}
}
return res
}
day 6(1.28 1.30 2.3)
18. 四数之和
花费时间:26:16(就是因为一开始的错误)
以后要想一下:
我现在的这个错误会不会影响之后的流程,是不是后面的流程野同样具有一样的错误思路
顺藤摸瓜
func fourSum(nums []int, target int) [][]int {
sort.Ints(nums)
var res [][]intfor i := 0; i < len(nums)-3; i++ {
a := nums[i]
if i > 0 && a == nums[i-1] {
continue
}for j := i + 1; j < len(nums)-2; j++ {
b := nums[j]
if j > i+1 && b == nums[j-1] {
continue
}l, r := j+1, len(nums)-1
for l < r {
c, d := nums[l], nums[r]
sum := a + b + c + dif sum == target {
res = append(res, []int{a, b, c, d})for l < r && nums[l] == c {
l++
}
for l < r && nums[r] == d {
r--
}
} else if sum > target {
r--
} else {
l++
}
}
}
}return res
}
重新写了一下设计链表那道题目,
对于nil和结构体之间有疑问
-
结构体类型的变量:
-
如果你声明了一个结构体类型的变量,该变量在默认情况下会被初始化为零值。零值取决于结构体字段的类型,对于数值类型,零值通常是 0,而对于引用类型,零值通常是
nil
。goCopy codetype MyStruct struct {Field1 intField2 string }var myVar MyStruct // 结构体变量,会被初始化为 MyStruct 的零值 fmt.Println(myVar) // 输出: {0 ""}
-
-
结构体类型的指针:
-
当你使用
new
或&
操作符创建一个结构体类型的指针时,如果没有为结构体字段分配具体的值,那么指针的值会是nil
。goCopy codetype MyStruct struct {Field1 intField2 string }var myPointer *MyStruct // 结构体指针,初始值为 nil fmt.Println(myPointer) // 输出: <nil>
-
总体而言,结构体变量的零值是根据字段类型确定的,而结构体指针的零值是 nil
。在使用结构体时,特别是使用指针时,请确保在访问结构体字段之前,先对结构体或结构体指针进行适当的初始化,以避免访问未分配的内存导致运行时错误
344. 反转字符串
func reverseString(s []byte) {
for i:=0;i<len(s)/2;i++{//注意这里是反转一半就可以了swap(&s[i],&s[len(s)-1-i])
}
}
func swap(s1 *byte,s2 *byte){//传入指针temp := *s1//改变指针所指向的值*s1 = *s2*s2 = temp
}
//我们也可以通过传入指针的指针来修改原始指针的值,但这并不推荐
541. 反转字符串 II
这道题审题过程很容易把我们带偏
func reverseStr(s string, k int) string {/*其实也就两种情况,这个题目说的很复杂每次i跳2k个单位i后面有k个单位我就反转i后面没有满k个单位,我就全部反转*/sb := []byte(s)for i:=0;i<len(s);i+=2*k{if i+k<=len(s){reverse(sb[i:i+k])}else{reverse(sb[i:])//剩余字符串小与k 全部反转}}return string(sb)
}
func reverse(b []byte){l,r:=0,len(b)-1for l<r{b[l] ,b[r]= b[r],b[l]l++r--}
}
替换数字
在Go语言中,...
(三个点)是一个扩展语法,用于将切片或数组的元素展开。在这种情况下,...
用于将一个切片插入到另一个切片中。
package mainimport "fmt"
func main(){var s []bytefmt.Scanln(&s)for i:=0;i<len(s);i++{if s[i] >='0' && s[i]<='9'{num := []byte{'n','u','m','b','e','r'}s = append(s[:i],append(num,s[i+1:]...)...)i = i+len(num)-1//因为我们去掉了一个字符}}fmt.Println(string(s))
}
151. 反转字符串中的单词
func reverseWords(s string) string {b := []byte(s)cur := 0for i:=0;i<len(b);i++{if b[i]!=' '{if cur!=0{b[cur] = ' 'cur++}//如果这个if片段在for下面的话,当我们最后一个单词复制完成之后,他还会继续加‘ ’for i<len(b) && b[i]!=' '{b[cur] =b[i]cur++i++ }//复制单词结束}}b = b[0:cur]reverse(b)pre := 0for i:=0;i<=len(b);i++{if i == len(b)||b[i] == ' '{reverse(b[pre:i])pre = i+1}}return string(b)
}
func reverse(s []byte){//因为这是个切片,切片本身就是引用类型,我们在副本上面动手脚,最终反映到底层数组,但是底层数组和原始的切片的底层是一样的,所以不需要取地址符
//切片参数本身就是对原始数据的引用,而不是复制数据。for i:=0;i<len(s)/2;i++{s[i],s[len(s)-1-i] = s[len(s)-1-i],s[i]}
}
day 7 | 1.29 1.31 2.4
右旋字符串
package main
import "fmt"
func main(){var s stringvar k intfmt.Scanln(&k)fmt.Scanln(&s)ss := []byte(s)if k> len(ss){return }ns := s[:len(ss)-k]nss := s[len(ss)-k:]res :=append([]byte(nss),[]byte(ns)...)fmt.Println(string(res))
}
对整体进行翻转,然后对局部进行反转
package main
import "fmt"
func main(){var s stringvar k intfmt.Scanln(&k)fmt.Scanln(&s)//输入格式不要忘记了ss := []byte(s)if k>len(s){return}reverse(ss)reverse(ss[:k])reverse(ss[k:])fmt.Println(string(ss))
}
func reverse(s []byte){l,r := 0,len(s)-1for l<r{s[l],s[r] = s[r],s[l]l++r--}
}
str()
前缀:不包括最后一个字符的
后缀:不包括第一个字符的
最长相同前后缀
模式串与前缀表对应位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
使用前缀表的主要优势在于:
- 避免回溯: 在普通的字符串匹配算法中,一旦出现不匹配的情况,通常需要将模式串回溯到某个位置重新开始匹配。而KMP算法通过前缀表,可以跳过一些不可能匹配的位置,减少回溯的次数,提高效率。
- 不再匹配的信息利用: 前缀表中记录了之前已经匹配的部分信息,这些信息被充分利用,避免了重复的比较工作。
文本串里面,当我们匹配到第j个字符时,发现与模式串中的第i个字符不相同的时候,我们观察第i-1个字符,如果它的最长相等前后缀是q,我们要回到next[q]的位置,继续进行匹配
我们首先要把next数组给求出来
我们规定i是后缀末尾,也就是正在遍历的数
j是前缀末尾,也就是当前最长相等前后缀的值
//可以先在脑子里面想一下aabaafa
//前后缀相同肯定是要头和尾是相同的
func getNext(s string,next []int){j:=0next[0] = 0//初始化for i:=1;i<len(s);i++{//s[j]是我当前最长前后缀的字符,最长前后缀的字符肯定是唯一的for j>0 && s[j]!=s[i]{j = next[j-1]}//注意是for,持续的向后移动if s[i] == s[j]{j++}next[i] = j}
}
//可以先在脑子里面想一下aabaafa
//前后缀相同肯定是要头和尾是相同的
//而且我们是单个字符的前后缀,所以没那么复杂,abab最长前后缀是0
func getNext(s string,next []int){j:=0next[0] = 0//初始化for i:=1;i<len(s);i++{for j>0 && s[j]!=s[i]{j = next[j-1]}//注意是forif s[i] == s[j]{j++}next[i] = j}
}
func strStr(haystack string, needle string) int {next := make([]int,len(needle))getNext(needle,next)j:=0//到底有没有匹配成功,取决于我的j可不可以到达末尾// j:indexfor i:=0;i<len(haystack);i++{for j>0 && haystack[i]!=needle[j]{j = next[j-1]}//这里体现的是我们前缀表的作用,减少遍历次数,但是i继续向前if haystack[i]==needle[j]{j++}if j==len(needle){return i-j+1//注意需要+1}}return -1
}
459.重复的子字符串
/*
这道题目用暴力暂时,暴力我都是看代码学会的
首先,审题,设子字符串长度为n1,则len(num)%n1 ==0
然后我们n1可以进入函数,用for循环遍历后面的每一个长度为n1的字符串(需要比较len(num)/n1次)
所以我们可以从头字符开始依次遍历,如果重复 回true
*/func repeatedSubstringPattern(s string) bool {
n:=len(s)
for i:=1;i<=len(s)/2;i++{if(n%i==0){if repeatedTest(s,i){return true}}
}
return false
}
func repeatedTest(s string,length int) bool{repeatedCount := len(s)/length//着重点部分在于repeatCountfor i:=1;i<repeatedCount;i++{//注意这里只能小于start:=i*lengthend := start+lengthif s[start:end]!=s[:length]{return false}}return true
}
在Go语言中,结构体字段和方法名的首字母是否大写决定了它们的可见性。如果字段或方法名的首字母是大写的,那么它们可以被其他包中的代码访问;如果首字母是小写的,那么它们只能在同一个包内被访问。
232. 用栈实现队列
/*
首先,构建一个包含两个数组(栈)的结构体
然后,用make初始化
最后,其他方法都要使用到append
*/type MyQueue struct{stackIn []intstackOut []int
}func Constructor() MyQueue {
return MyQueue{stackIn: make([]int,0),stackOut: make([]int,0),
}
}//往输入栈做push
func (this *MyQueue) Push(x int) {
this.stackIn = append(this.stackIn,x)
}//在输出栈做pop,如果输出栈为空,需要将输入栈的所有元素导入
func (this *MyQueue) Pop() int {lenIn,lenOut := len(this.stackIn),len(this.stackOut)if lenOut==0{if lenIn==0{return -1}//else{for i:=lenIn-1;i>=0;i--{this.stackOut = append(this.stackOut,this.stackIn[i]) }this.stackIn =[]int{}lenOut = len(this.stackOut)//return this.stackOut[lenOut-1]//导入之后最后一个元素是要删除的}val :=this.stackOut[lenOut-1]this.stackOut = this.stackOut[:lenOut-1]return val
}//先使用pop将队列首元素提出来,然后再加入到stackout栈中
func (this *MyQueue) Peek() int {val := this.Pop()if val==-1{return -1}this.stackOut = append(this.stackOut,val)return val
}func (this *MyQueue) Empty() bool {return len(this.stackOut)==0 && len(this.stackIn)==0
}/*** Your MyQueue object will be instantiated and called as such:* obj := Constructor();* obj.Push(x);* param_2 := obj.Pop();* param_3 := obj.Peek();* param_4 := obj.Empty();*/
用队列实现栈
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。
在Go语言中,当结构体只有一个字段时,可以将结构体变量与nil
进行比较。这是因为在这种情况下,结构体变量的零值就是nil
。
例如,考虑以下结构体:
goCopy codetype MyStruct struct {Value int
}
如果创建一个变量 var myVar MyStruct
,此时 myVar
的零值是 {0}
,可以将其与 nil
进行比较:
goCopy codeif myVar == nil {fmt.Println("myVar is nil")
} else {fmt.Println("myVar is not nil")
}
请注意,这仅适用于结构体只有一个字段的情况。如果结构体有多个字段,即使其中一个字段为零值,整个结构体变量也不会等于 nil
。
type MyStack struct {//创建两个队列queue1 []intqueue2 []int
}func Constructor() MyStack {return MyStack{ //初始化queue1:make([]int,0),queue2:make([]int,0),}
}func (this *MyStack) Push(x int) {//先将数据存在queue2中this.queue2 = append(this.queue2,x) //将queue1中所有元素移到queue2中,再将两个队列进行交换this.Move()
}func (this *MyStack) Move(){ if len(this.queue1) == 0{//交换,queue1置为queue2,queue2置为空this.queue1,this.queue2 = this.queue2,this.queue1}else{//queue1元素从头开始一个一个追加到queue2中this.queue2 = append(this.queue2,this.queue1[0]) this.queue1 = this.queue1[1:] //去除第一个元素this.Move() //重复}
}func (this *MyStack) Pop() int {val := this.queue1[0]this.queue1 = this.queue1[1:] //去除第一个元素return val}func (this *MyStack) Top() int {return this.queue1[0] //直接返回
}func (this *MyStack) Empty() bool {
return len(this.queue1) == 0
}/*** Your MyStack object will be instantiated and called as such:* obj := Constructor();* obj.Push(x);* param_2 := obj.Pop();* param_3 := obj.Top();* param_4 := obj.Empty();*/
day 8| 1.29 2.1 2.5
20. 有效的括号
/*
我的思路:
/*思路:
用栈对([{进行压入
如果不是,首先对stack进行判断是否为空,
然后如果是),要对上(,其他的同理
*/func isValid(s string) bool {//我们后面的代码要通过')'匹配'('magazine := map[byte]byte{')':'(','}':'{',']':'[',}stake := make([]byte,0)if s==""{return true}for i:=0;i<len(s);i++{if s[i]=='('||s[i]=='{'||s[i]=='['{stake = append(stake,s[i])}else if len(stake)!=0 && stake[len(stake)-1] == magazine[s[i]]{stake = stake[:len(stake)-1]}else{return false}}return len(stake) == 0}
1047. 删除字符串中的所有相邻重复项
/*
如果当前一个和前一个字符相同,删去
*/
func removeDuplicates(s string) string {stack := make([]byte,0)//我们通常用make将切片空间赋值for i:=0;i<len(s);i++{//注意string可以直接访问if len(stack)>0 && stack[len(stack)-1] == s[i]{stack = stack[:len(stack)-1]}else{stack = append(stack,s[i])}}return string(stack)
}
150. 逆波兰表达式求值
func evalRPN(tokens []string) int {stack := make([]int,0)for i:=0;i<len(tokens);i++{if num,err := strconv.Atoi(tokens[i]); err==nil{stack = append(stack,num)}else if len(stack)>1{length := len(stack)num1 := stack[length-1]num2 := stack[length-2]stack = stack[:length-2]switch tokens[i]{case "*":stack = append(stack,num2*num1)case "+":stack = append(stack,num2+num1)case "-":stack = append(stack,num2-num1)case "/":stack = append(stack,num2/num1)}}}return stack[0]
}
type MyQueue struct{queue []int
}
func NewMyQueue() *MyQueue{return &MyQueue{queue : make([]int,0),}
}//他只是为这个queue提供一个空间func (m *MyQueue) Back() int{return m.queue[len(m.queue)-1]
}func (m *MyQueue) Empty() bool{return len(m.queue) == 0
}func (m *MyQueue) Push(val int){//我们只要保证队列中有至少一个元素就行for !m.Empty() && val > m.Back(){//每次我们push一遍,代表着我们对三个元素的最大值遍历一遍m.queue = m.queue[:len(m.queue)-1]//删除没有用的元素}m.queue = append(m.queue,val)
}func (m *MyQueue) Pop(val int){if !m.Empty()&&val== m.Front(){m.queue = m.queue[1:]}
}func (m *MyQueue) Front() int{return m.queue[0]
}
func maxSlidingWindow(nums []int, k int) []int {queue := NewMyQueue()length :=len(nums)res := make([]int,0)//先将前k个元素放入队列for i:= 0;i<k;i++{queue.Push(nums[i])}//记录前k个元素的最大值res = append(res,queue.Front())for i:=k;i<length;i++{queue.Pop(nums[i-k])queue.Push(nums[i])res = append(res,queue.Front())}
return res
}
239. 滑动窗口最大值
/*
我们需要创造一个队列
底层是数组的append方法应用
创造一个结构体包含这个数组字段
创造结构体指针的方法
*/type Queue struct{queue []int
}
func MyQueue() *Queue{return &Queue{queue : make([]int,0),}
}
//我们使用指针能准确改变地址结构,不需要复制整个结构体,节省内存开销
func (que *Queue) Empty()bool{return len(que.queue) == 0
}func (que *Queue) Front()int{//刚才遍历过的最大元素,不是窗口最前面的元素return que.queue[0]
}
func (que *Queue) Back() int{return que.queue[len(que.queue)-1]
}
func (que *Queue) Push(val int){for !que.Empty()&&val>que.Back(){que.queue = que.queue[:len(que.queue)-1]}//我找了好久好久好久好久que.queue = append(que.queue,val)
}
func (que *Queue) Pop(val int){if !que.Empty() && val == que.Front(){que.queue = que.queue[1:]}}
func maxSlidingWindow(nums []int, k int) []int {m := MyQueue()res := make([]int,0)for i:=0;i<k;i++{m.Push(nums[i])}res = append(res,m.Front())for i :=k;i<len(nums);i++{m.Pop(nums[i-k])m.Push(nums[i])res = append(res,m.Front())}return res
}
day 9|1.30 2.2 2.6
347. 前 K 个高频元素
这道题目我们相当于做两道题目
涉及一个sort.Slice(arr,func(a,b int){})
还有一个小顶堆知识小顶堆就是一个完全二叉树,父节点小于左右节点 其中左子节点小于右子节点
sort.Slice(数组,func(a,b int)bool{
return 依据什么来排序数组
})
/*我的想法:设置一个map[int]int遍历数组
每遍历一次,所对应的值++
再遍历一遍map,对于值>=k的赋值到res数组中
*/func topKFrequent(nums []int, k int) []int {tkMap := make(map[int]int)res := make([]int, 0)// 统计每个数字的频率for i := 0; i < len(nums); i++ {temp := nums[i]tkMap[temp]++}for key,_ := range tkMap{res = append(res,key)}// 将频率大于等于 k 的数字添加到结果数组sort.Slice(res,func(a,b int)bool{return tkMap[res[a]]>tkMap[res[b]]})//依照map中的值 频率来决定res数组中元素的顺序return res[:k]
}
heap.Init
在 Go 语言的 heap
包中,Init
函数的源代码如下:
goCopy code// Init initializes a heap with an arbitrary ordering and contents.
// The complexity is O(n) where n = h.Len().
func Init(h Interface) {n := h.Len()// Build heap with calls to siftdownfor i := n/2 - 1; i >= 0; i-- {siftDown(h, i, n)}
}
这个 Init
函数的作用是初始化一个堆,它接受实现了 heap.Interface
接口的对象 h
作为参数。在初始化堆时,会调用 siftDown
函数进行堆的构建,时间复杂度为 O(n),其中 n 是堆的元素个数。
注意:这里的 siftDown
函数是堆排序算法的一部分,用于调整堆的结构,确保堆的性质得以维持。
在 Go 中,如果一个类型要满足 heap.Interface 接口,它必须实现以下方法:
Len() int
Less(i, j int) bool
Swap(i, j int)
Push(x interface{})
Pop() interface{}
//方法一:小顶堆
func topKFrequent(nums []int, k int) []int {map_num:=map[int]int{}//记录每个元素出现的次数for _,item:=range nums{map_num[item]++}h:=&IHeap{}//就是切片,不是数组指针,切片本身就是一个引用了heap.Init(h)//堆//所有元素入堆,堆的长度为k/*每次循环迭代都会维持小顶堆中的元素是当前遍历到的前 k 个高频元素。这是一个很有效的方式来实现 Top K 问题,而不需要在整个数组上进行排序。*/for key,value:=range map_num{heap.Push(h,[2]int{key,value})if h.Len()>k{heap.Pop(h)}}res:=make([]int,k)//按顺序返回堆中的元素for i:=0;i<k;i++{res[k-i-1]=heap.Pop(h).([2]int)[0]}return res
}//构建小顶堆
type IHeap [][2]intfunc (h IHeap) Len()int {return len(h)
}func (h IHeap) Less (i,j int) bool {return h[i][1]<h[j][1]
}func (h IHeap) Swap(i,j int) {h[i],h[j]=h[j],h[i]
}func (h *IHeap) Push(x interface{}){*h=append(*h,x.([2]int))
}
func (h *IHeap) Pop() interface{}{old:=*hn:=len(old)x:=old[n-1]*h=old[0:n-1]return x
}
144. 二叉树的前序,中序,后序遍历
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*///可以和外部函数共享资源,不需要去浪费复制的内存空间
/*
这种实现方式不需要创建新的切片,而是直接在外部函数中共享切片资源,避免了额外的内存分配。这种做法在递归遍历时是比较常见的,可以有效减少内存开销。
*/
func preorderTraversal(root *TreeNode) (res[]int) {var traversal func(node *TreeNode)traversal = func(node *TreeNode){if node == nil{return }res = append(res,node.Val)traversal(node.Left)traversal(node.Right)}traversal(root)return res
}
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func inorderTraversal(root *TreeNode) (res[]int) {var traversal func(node *TreeNode)traversal = func(node *TreeNode){if node==nil{return }traversal(node.Left)res = append(res,node.Val)traversal(node.Right)}traversal(root)return res
}
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func postorderTraversal(root *TreeNode) (res []int) {var traversal func(node *TreeNode)traversal= func(node *TreeNode){if node==nil{return }traversal(node.Left)traversal(node.Right)res = append(res,node.Val)}traversal(root)return res
}
迭代法
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func preorderTraversal(root *TreeNode) []int {ans := []int{}if root == nil{return ans}st := list.New()//我们定义一个链表st.PushBack(root)//将根节点打入链表中for st.Len()>0{node := st.Remove(st.Back()).(*TreeNode)//将中间元素移除,并且转换为节点形式ans = append(ans,node.Val)//因为链表是先入后出的形式,所以先右后左//之后就会先左后右if node.Right !=nil{st.PushBack(node.Right)}if node.Left !=nil{st.PushBack(node.Left)}}return ans
}
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func inorderTraversal(root *TreeNode) []int {res := make([]int,0)if root==nil{return res}st := list.New()cur := rootfor cur!=nil || st.Len() >0{if cur!=nil{st.PushBack(cur)cur = cur.Left}else{cur= st.Remove(st.Back()).(* TreeNode)res = append(res,cur.Val)cur = cur.Right}}return res
}
/*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *TreeNode* Right *TreeNode* }*/
func postorderTraversal(root *TreeNode) (res []int) {if root==nil{return res}st := list.New()st.PushBack(root)for st.Len()>0{node := st.Remove(st.Back()).(*TreeNode)res = append(res,node.Val)if node.Left!=nil{st.PushBack(node.Left)}if node.Right !=nil{st.PushBack(node.Right)}}reverse(res)return res
}
func reverse(res []int){i,j := 0,len(res)-1for i<j{res[i],res[j] = res[j],res[i]i++j--}
}
今天就做3道吧做不动了