G
N
I
D
A
O
L

代码随想录算法训练营第1天 | lc704、lc35、lc34、lc27

(本合集全部为Go语言实现)

相关文章链接:数组理论基础 704题解 27题解
相关视频链接:704视频 27视频

Leetcode704

状态:秒了
实现过程中的难点:着重注意左右指针是否包含在当前未扫描的区间内。(建议开区间也转换为闭区间进行考虑,因为闭区间不会很绕)

对撞指针的情况分析

left/right/mid分别对应nums[left]/nums[right]/nums[mid]

对于两指针间隔的所有情况,可以挨个进行分析:

  • left - 1 = right,直接不符合循环准入条件
  • left = right时,mid = left = right
    • mid < target,则left - 1 = right
    • mid > target,则left - 1 = right
  • left + 1 = right时,mid = left
    • mid < target,则left = right
    • mid > target,则left - 1 = right
  • left + 2 = right时,mid = left + 1
    • mid < target,则left = right
    • mid > target,则left = right
  • 其他间隔更大的情况,均会像第三种情况一样,最终退化成前两种情况

在上边的三种结果为left - 1 = right的情况时,可以推出:此时right < target < left(但注意这是在数组元素不重复的情况下)

左闭右闭写法

func search(nums []int, target int) int {
    left, right := 0, len(nums) - 1
    for left <= right {
        mid := left + ((right - left) >> 1);
        if nums[mid] < target {
            left = mid + 1;
        } else if nums[mid] > target {
            right = mid - 1;
        } else {
            return mid
        }
    }
    return -1
}

Leetcode35

状态:改了几次
实现过程中的难点:本题是聚焦退出循环时对撞指针和target值之间的关系

问题分析

left/right/mid分别对应nums[left]/nums[right]/nums[mid]

  • 如果可以在序列中找到target,那么会被直接返回
  • 如果没有找到,那么最终状态一定是left - 1 = right,此时一定是right < target < left

左闭右闭写法

func searchInsert(nums []int, target int) int {
    left, right := 0, len(nums) - 1
    for left <= right {
        mid := left + ((right - left) >> 1);
        fmt.Println(mid, nums[mid])
        if nums[mid] < target {
            left = mid + 1;
        } else if nums[mid] > target {
            right = mid - 1;
        } else {
            return mid
        }
    }
    return left
}

Leetcode35

状态:暴力写法秒的。题解中的巧妙写法看了挺久才看懂
实现过程中的难点:巧妙写法中通过将命中target的分支归到非等值分支中,实现了向非等值分支方向的查找,从而定位到该方向的首个target

题解写法分析

题解写法中,将命中target的情况归到扫描边界向左收敛(即right = mid - 1)的情况当中,这使得出循环时一定处于right < target = left的状态

  • 会不会向左收敛时,right跨过了target
    • 会。但一定是恰好跨过,也就是跨过时的循环内mid = target,这样才会因为right = mid - 1而跨过target,否则如果当时mid < target,那么就是向右收敛
    • 此后,循环就一定是一直向右收敛,因为mid < target,最终出循环时right < target = left
  • 如果数组中没有target - 1,那么结果对么?
    • 如果数组中有target - 1,那么就转换为找target - 1的左边界
    • 如果数组中没有target - 1,那么就退化为在无重复值的非递减数组中找到target - 1的插入位置(即上边的Leetcode35)的问题

左闭右闭题解写法

暴力写法略(在命中target时进行左右遍历确定左右边界)

func searchRange(nums []int, target int) []int {
  Search := func(a []int, t int) int {
        left := 0
        right := len(a) - 1
        
        for left <= right {
            mid := (right - left) / 2 + left
            if a[mid] < t {
                left = mid + 1
            } else {
                right = mid - 1
            }
        }
        return left
    }
    leftIdx := Search(nums, target)
    if leftIdx == len(nums) || nums[leftIdx] != target {
        return []int {-1, -1}
    }
    rightIdx := Search(nums, target + 1) - 1
    
    return []int {leftIdx, rightIdx}
}

Leetcode27

状态:秒了
实现过程中的难点:注意赋值slow后自增fast

个人写法

func removeElement(nums []int, val int) int {
  slow, fast := 0, 0
  for fast < len(nums) {
    for fast < len(nums) && nums[fast] == val {
      fast++
    }
    if fast >= len(nums) {
      return slow
    }
    nums[slow] = nums[fast]
    slow++
    fast++
  }
  return slow
}

今日收获

  • 复习了二分搜索的写法
  • 学会了可以基于将命中target的分支归到其他两分支之一,来实现获取target边界下标

学习时长:2小时左右,时间比较分散

posted @ 2023-12-02 17:39  geJoyo  阅读(9)  评论(0)    收藏  举报