二分查找-迭代法

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)/2
    if 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)-1
 for left<=right{
     mid := left+(right-left)/2
     if(target==nums[mid]){
         return mid
     }else if(target>nums[mid]){
         left = mid+1
         
     }else{
     right = mid-1 
     }
 }
 //for 循环要能出来就是right<left
 return 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 := i
        for j:=flag ;j<len(nums);j++{
        if(nums[j]!=target){
            flag2 := j-1
            return []int{flag,flag2}
            break
        }else if(len(nums)==j+1&&nums[j] == target){
           flag2:=j
           return []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 :=-2
    right := len(nums)-1
    left := 0
    for left<=right{
        mid := left+(right-left)>>1
        if(nums[mid]<=target){
         left = mid+1//其实最终这里的left就不符合if的条件了,我们的for循环终止条件是left>right
            //想一下,如果if条件一直成立,到最后nums[board]肯定会大于target
         board = left//最终会大于这个数
        }else {
            right=mid -1
        }
    }
    return board
}
func leftBoard(nums[]int,target int) int{
    //寻找最大的小于target的数
board :=-2
    right := len(nums)-1
    left := 0
    for left<=right{
        mid := left+(right-left)>>1
        if(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)>>1
    if 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,num
 for left<=right{
     mid := left+(right-left)>>1
     square := mid*mid
     if(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 := 0
    for 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 := 0
 temp := 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 := 0
 fast := 1
 for 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 := 0

for 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 :=0
    for i := range s{
        if s[i]!='#'{
            bt[index] = s[i]
        }else if(index >0){
          bt = bt[:index--]
        }
    }
    */
}

在Go语言中,i++j--是后缀操作符,它们返回的是操作前的值。但是在赋值语句中,我们需要使用操作后的值进行交换。因此,在这种情况下,i++j--不能替代i = i+1j = j-1

具体来说,在可变函数中,arr[i], arr[j] = arr[j], arr[i]的交换操作需要使用i+1j-1的值进行赋值。如果使用后缀操作符,那么i+1j-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的方式在这种具体情况下是错误的,因为它不会按预期更新ij的值。

在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/2
   count :=1
   x ,y :=0,0
   hinder := 1
    //圈圈的范围是由x,y和hinder构成的
   for loop>0{//等会试下loop--看行不行
    i,j := x,y
    for ;j<n-hinder;j++{
        res[i][j] = count
        count++
    }
    for ;i<n-hinder;i++{
        res[i][j] = count
        count++
    }
    for ;j>y;j--{
        res[i][j] = count
        count++
    }
    for ;i>x;i--{
        res[i][j] = count
        count++
    }
   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 = head
     cur := dummyHead
     for 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 int
    Next *SingleNode
}
type MyLinkedList struct {
 dummyHead *SingleNode
 Size 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 int
     Next *SingleNode
}
type MyLinkedList struct {
    //我需要虚拟头节点,所以要设置一个额外的struct,这个struct包含val 和 next
    //Size在这个里面
    dummyNode *SingleNode
    Size 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 := head
  for cur !=nil{//我们不能指定它的下一个node不是nil,原因就是最后一个节点就是nil,也需要反转
      temp := cur.Next
      cur.Next = pre
      pre = cur
      cur = 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.Next
       pre.Next = head.Next
       head.Next.Next = head
       head.Next = temp
       pre = head
       head = 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 := head
     prev :=dummyHead//
     count :=1//因为本身差了一个
     //就相当于前面有个牛,你和牛的距离一定要是n+1位置,这样你才能到倒数第n+1个位置
    //count 是我与牛之间的距离
    //最终牛会到达nil->len+1
     for cur!=nil{
           cur = cur.Next
         if count>n{//此时count == n+1,满足条件,我们被牛牵动
             prev = prev.Next
         }
         count++
     }
     prev.Next = prev.Next.Next
     return dummyHead.Next
}
面试题 02.07. 链表相交
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
//这道题还考察了审题
//我们需要发现在交点之后,剩下的一段链表是相同的
//也就是说两端链表是包含或真包含的一个关系
//所以从距离相同的起点开始检索
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    //我们可以发现从后往前面数,更容易得到我们想要的答案
    //但是太浪费空间了
    //换个思路就是把两者的长度计算出来,计算出差值,让长度长的
    //从表头自动增加差值,这样两个链表就可以对齐
    curA := headA
    curB := headB
    countA :=0
    countB := 0
    for headA!=nil{
        countA++
        headA = headA.Next
    }
    for headB!=nil{
        countB++
        headB = headB.Next
    } 
    var gap int
    var fast,slow *ListNode
    if countA>countB{
       gap = countA - countB
       fast = curA
       slow = curB
    }else{
        gap = countB - countA
        fast = curB
        slow = curA
    }
    for i:=0;i<gap;i++{
        fast = fast.Next
    }
   for fast!=slow{
       fast = fast.Next
       slow = slow.Next
   }
    return fast
}
/*
for slow!=nil{
        if fast==slow{
            return fast
        }
        fast = fast.Next
        slow=slow.Next
    */


142.环形链表II
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func detectCycle(head *ListNode) *ListNode {
 slow,fast:=head,head
 for fast!=nil && fast.Next!=nil{
 //走两步要保证自己本身那一步没问题,然后第一部没问题,第二步有问题没关系,不违法
     slow = slow.Next
     fast = fast.Next.Next
     if slow == fast{
         for slow!=head{
             slow = slow.Next
             head = 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提取出来,不断提取sum

count := 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 := 0
    for 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中没有对应项,返回false
        return []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)-1
    for 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 [][]int

for 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 + d

if 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和结构体之间有疑问

  1. 结构体类型的变量:

    • 如果你声明了一个结构体类型的变量,该变量在默认情况下会被初始化为零值。零值取决于结构体字段的类型,对于数值类型,零值通常是 0,而对于引用类型,零值通常是 nil

      goCopy codetype MyStruct struct {
          Field1 int
          Field2 string
      }
      
      var myVar MyStruct  // 结构体变量,会被初始化为 MyStruct 的零值
      fmt.Println(myVar)  // 输出: {0 ""}
      
  2. 结构体类型的指针:

    • 当你使用 new& 操作符创建一个结构体类型的指针时,如果没有为结构体字段分配具体的值,那么指针的值会是 nil

      goCopy codetype MyStruct struct {
          Field1 int
          Field2 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)-1
    for l<r{
        b[l] ,b[r]= b[r],b[l]
        l++
        r--
    }
}
替换数字

在Go语言中,...(三个点)是一个扩展语法,用于将切片或数组的元素展开。在这种情况下,...用于将一个切片插入到另一个切片中。

package main

import "fmt"
func main(){
    var s []byte
    fmt.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 := 0
    for 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 := 0
    for 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 string
    var k int
    fmt.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 string
    var k int
    fmt.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)-1
    for l<r{
        s[l],s[r] = s[r],s[l]
        l++
        r--
    }
}
str()

前缀:不包括最后一个字符的

后缀:不包括第一个字符的

最长相同前后缀

模式串与前缀表对应位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。

使用前缀表的主要优势在于:

  1. 避免回溯: 在普通的字符串匹配算法中,一旦出现不匹配的情况,通常需要将模式串回溯到某个位置重新开始匹配。而KMP算法通过前缀表,可以跳过一些不可能匹配的位置,减少回溯的次数,提高效率。
  2. 不再匹配的信息利用: 前缀表中记录了之前已经匹配的部分信息,这些信息被充分利用,避免了重复的比较工作。

文本串里面,当我们匹配到第j个字符时,发现与模式串中的第i个字符不相同的时候,我们观察第i-1个字符,如果它的最长相等前后缀是q,我们要回到next[q]的位置,继续进行匹配

我们首先要把next数组给求出来

我们规定i是后缀末尾,也就是正在遍历的数

j是前缀末尾,也就是当前最长相等前后缀的值

//可以先在脑子里面想一下aabaafa
//前后缀相同肯定是要头和尾是相同的
func getNext(s string,next []int){
    j:=0
    next[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:=0
    next[0] = 0
    //初始化
    for i:=1;i<len(s);i++{
        for j>0 && s[j]!=s[i]{
            j = next[j-1]
        }//注意是for
        if 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:index
 for 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//着重点部分在于repeatCount
    for i:=1;i<repeatedCount;i++{//注意这里只能小于
        start:=i*length
        end := start+length
        if s[start:end]!=s[:length]{
            return false
        }
    }
    return true
}

在Go语言中,结构体字段和方法名的首字母是否大写决定了它们的可见性。如果字段或方法名的首字母是大写的,那么它们可以被其他包中的代码访问;如果首字母是小写的,那么它们只能在同一个包内被访问。

232. 用栈实现队列

232.用栈实现队列版本2

/*
首先,构建一个包含两个数组(栈)的结构体
然后,用make初始化
最后,其他方法都要使用到append
*/

type MyQueue struct{
   stackIn []int
   stackOut []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();
 */
用队列实现栈

225.用队列实现栈

一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时再去弹出元素就是栈的顺序了。

在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 []int
    queue2 []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 siftdown
	for 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]int

func (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:=*h
    n:=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 := root
     for 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)-1
    for i<j{
        res[i],res[j] = res[j],res[i]
        i++
        j--
    }
}

今天就做3道吧做不动了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/362408.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

6个AI写作工具,让你的文字变得生动有力

写作是一项需要耐心和技巧的任务&#xff0c;对于许多人来说&#xff0c;写作可能是一项困难的挑战。然而&#xff0c;随着人工智能的发展&#xff0c;AI智能写作软件逐渐崭露头角&#xff0c;为我们提供了更加便捷和高效的写作体验。在本文中&#xff0c;我们将介绍几款好用的…

算法练习01——哈希部分双指针

目录 1. 两数之和(*)242. 有效的字母异位词(easy)49. 字母异位词分组(*)349. 两个数组的交集202. 快乐数(1.使用Set存哈希&#xff0c;2.快慢指针)454. 四数相加 II383. 赎金信15. 三数之和*(双指针)18. 四数之和*(双指针)128. 最长连续序列 1. 两数之和(*) https://leetcode.…

边缘计算网关在智能制造中有哪些应用?-天拓四方

在智能制造和工业生产环境中&#xff0c;数据已经成为新的生产要素&#xff0c;工业生产对实时性、灵活性和智能化也提出了更高的要求。而在这个过程中&#xff0c;边缘计算网关发挥着不可或缺的作用。它作为设备层与网络层之间的关键桥梁&#xff0c;确保了数据的实时、高效处…

STM32F407移植OpenHarmony笔记6

继上一篇笔记&#xff0c;编译好STM32的裸机程序&#xff0c;能点亮LED灯了。 下一步就是启动liteos_m内核了。 不过为了更好的调试代码&#xff0c;需要先把printf重定向到串口&#xff0c;基于gcc的printf重定向和Keil不一样。 直接新建printf.c&#xff0c;在里面重写printf…

基于 NOVATEK NT98530 Multiview Stitching 应用解决方案

感测技术近来于影像监控系统应用有了进一步的发展&#xff0c;多镜头的应用也与日俱增&#xff0c;如 AI 视觉感测会议相机&#xff0c;能满足远端多人聚会、远距教育训练的多元需求等&#xff0c;相关应用层面广泛涵盖了在生活中所面对的各种场景&#xff0c;带动更加可观的潜…

【安装指南】nodejs下载、安装与配置详细教程

目录 &#x1f33c;一、概述 &#x1f340;二、下载node.js &#x1f337;三、安装node.js &#x1f341;四、配置node.js &#x1f33c;一、概述 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;用于构建可扩展的网络应用程序。Node.js 使用事件驱动、…

各品牌主板快速启动热键对照表及CMOS进入方法

各品牌主板快速启动热键对照表 主板品牌 启动按键 笔记本品牌 启动按键 主机品牌 启动按键 华硕主板 F8 联想笔记本 F12 联想台式机 F12 技嘉主板 F12 宏碁笔记本 F12 惠普台式机 F12 微星主板 F11 华硕笔记本 ESC 宏碁台式机 F12 梅捷主板 F9 惠普笔…

机器学习数学基础

机器学习基础 1、标量、向量、矩阵、张量2、概率函数、概率分布、概率密度、分布函数3、向量的线性相关性4、最大似然估计5、正态分布(高斯分布)6、向量的外积(叉积)7、向量的内积(点积)8、超平面(H)1、标量、向量、矩阵、张量 标量、向量、矩阵和张量是线性代数中不同…

Java基础学习:System类和Static方法的实际使用

一、System类 1.在程序开发中&#xff0c;我们需要对这个运行的结果进行检验跟我们预判的结果是否一致&#xff0c;就会用到打印结果在控制台中显示出来使用到了System类。System类定义了一些和系统相关的属性和方法&#xff0c;它的属性和方法都是属于静态的&#xff0c;想使用…

win11安装wsl作为linux子系统并当作服务器

wsl安装 打开控制面板&#xff0c;找到启用或关闭windows功能 开启windows虚拟机监控平台和适用于Linux的Windows子系统&#xff0c;重启电脑。 打开microsoft store搜索ubuntu&#xff0c;找到合适的版本下载安装 输入wsl -l如下所示&#xff0c;即为安装成功。 安装过程比较…

怎么进行视频压缩大小?常见的4种压缩方法

在当今数字化的时代&#xff0c;我们经常处理大量的视频文件&#xff0c;无论是用于社交媒体分享、视频制作还是存储在我们的设备中。然而&#xff0c;随着视频质量的提升和分辨率的增加&#xff0c;视频文件的大小也相应地变得更加庞大&#xff0c;给存储、分享和传输带来了一…

HTTPS之使用acme.sh申请免费ssl证书

1、步骤一&#xff1a;安装 acme.sh acme.sh 是一个集成了 ACME 客户端协议的 Bash 脚本 a、安装命令 curl https://get.acme.sh | sh -s emailusernameexample.com 或者 git clone --depth 1 https://github.com/acmesh-official/acme.sh.git cd acme.sh ./acme.sh --ins…

循环系统的血流方向 Circulatory System‘s Pathway of Blood Through the Heart

循环系统的血流方向 目录 循环系统的血流方向前置知识&#xff1a;心脏腔室和阀门&#xff1a;血液路线&#xff1a;心脏瓣膜病 循环系统是由心脏、血管和血液组成的复杂系统&#xff0c;负责输送氧气、营养和其他物质到身体的各个部位&#xff0c;并将代谢产物带回肺和肾脏等器…

【力扣经典面试题】189. 轮转数组

题目描述&#xff1a; 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 示例 1: 输入: nums [1,2,3,4,5,6,7], k 3 输出: [5,6,7,1,2,3,4] 解释: 向右轮转 1 步: [7,1,2,3,4,5,6] 向右轮转 2 步: [6,7,1,2,3,4,5] 向右轮转 …

SIT1145AQ带选择性唤醒及故障保护的低功耗 CAN FD 总线收发器

特点 符合 ISO 11898-2:2016 和 SAE J2284-1 至 SAE J2284-5 标准 ➢ AEC-Q100 认证 ➢ 拥有低功耗休眠模式以及待机模式 ➢ 支持标准 CAN 唤醒帧的远程唤醒&#xff0c;兼容 ISO 11898- 2:2016 标准的选择性唤醒帧远程唤醒 ➢ 唤醒源诊断识别功能 ➢ 总…

二月外贸新规,外贸人请查收

政策导读&#xff1a; 1.新版《稳外贸稳外资税收政策指引》发布&#xff1b; 2.商务部公布2024年进出口许可证发证机构名录&#xff1b; 3.2月9日起中国和新加坡互免签证&#xff1b; 4.进出口低含量三乙醇胺混合物产品无需办理两用物项许可证&#xff1b; 5.USB-C成为欧盟…

通过与chatGPT交流实现零样本事件抽取

1、写作动机&#xff1a; 近来的大规模语言模型&#xff08;例如Chat GPT&#xff09;在零样本设置下取得了很好的表现&#xff0c;这启发作者探索基于提示的方法来解决零样本IE任务。 2、主要贡献&#xff1a; 提出了基于chatgpt的多阶段的信息抽取方法&#xff1a;在第一阶…

企业网络基础架构监控工具

IT 基础架构已成为提供基本业务服务的基石&#xff0c;无论是内部管理操作还是为客户托管的应用程序服务&#xff0c;监控 IT 基础设施至关重要&#xff0c;并且已经建立起来&#xff0c;SMB IT 基础架构需要简单的网络监控工具来监控性能和报告问题。通常&#xff0c;几个 IT …

写个Android事件分发实际用例(持续更新)

一&#xff0c;概述 感兴趣的读者&#xff0c;如果对Android事件分发还有不了解的地方&#xff0c;可以阅读笔者写的文章再谈android事件分发机制。 本文的主要目的&#xff0c;是结合前文所分享事件分发相关原理&#xff0c;在实际案例中使用。 二&#xff0c;Recycler嵌套…

SourceTree 不显示新添加文件

最近遇到了在项目中新添加了文件&#xff0c;但是在提交的时候SourceTree 中“未暂存区域”却不显示文件。如果你也有类似的问题&#xff0c;不防来看看吧。 我可能不知道什么时候动了下面的配置&#xff1a; 配置选择为“待定”&#xff0c;新增的未提交文件就显示出来了&…
最新文章