labuladong算法小抄-二分搜索-leetcode34、leetcode35、leetcode354、leetcode392、leetcode704、leetcode793、875、1011

⼆分搜索

思想

二分搜索分为:基本二分搜索,最左边的二分搜索,最右边的二分搜索。

防止溢出:计算 mid 时需要防⽌溢出,代码中 left + (right - left) / 2 就和 (left +

right) / 2 的结果相同,但是有效防⽌了 left 和 right 太⼤,直接相加导致溢出的情况。

1.基本的二分搜索(左闭右闭的情况)

//基本的二分搜索
func binarySearch(nums []int,target int)int{
	left := 0
	right := len(nums) - 1
	for left <= right{
		mid := left + (right - left) / 2
		if nums[mid] == target{
			return mid
		}else if nums[mid] > target{
			right = mid -1
		}else if nums[mid] <target{
			left = mid +1
		}
	}
	return -1
}

2.基本的二分搜索(左闭右开)

func binarySearch1(nums []int,target int)int{
	// 左闭右开
	left := 0 // left表示可取
	right := len(nums) // right表示末尾,且不能取
	for left < right{
		mid := left + (right - left) / 2
		if nums[mid] == target{
			return mid
		}else if nums[mid] > target{ // right表示不可取
			right = mid
		}else if nums[mid] <target{
			left = mid +1
		}
	}
	return -1
}

3.左侧搜索

func left_bound(nums []int,target int) int{
	left := 0
	right := len(nums) -1
	for left <= right{
		mid := left + (right - left) / 2
		if nums[mid] > target{
			right = mid -1
		}else if nums[mid] < target{
			left = mid + 1
		}else if target == nums[mid]{
			right = mid - 1 // 等于的话必须左移动
		}
	}
	if left >= len(nums) || nums[left] != target{
		return -1
	}
	return left
}

4.右侧搜索

func right_bound(nums []int,target int) int{
	left := 0
	right := len(nums) -1 // right表示末尾,不可取
	for left <= right{
		mid := left + (right - left) / 2
		if nums[mid] > target{
			right = mid -1
		}else if nums[mid] < target{
			left = mid + 1
		}else if nums[mid] == target{
			left = mid + 1
		}
	}
	if right < 0 || nums[right] != target{
		return -1
	}
	return right
}

例题

34. 在排序数组中查找元素的第⼀个和最后⼀个位置

func searchRange(nums []int, target int) []int {
    res := []int{left_bound(nums,target),right_bound(nums,target)}
    return res
}
func left_bound(nums []int,target int) int{
	left := 0
	right := len(nums) -1
	for left <= right{
		mid := left + (right - left) / 2
		if nums[mid] > target{
			right = mid -1
		}else if nums[mid] < target{
			left = mid + 1
		}else if target == nums[mid]{
			right = mid - 1 // 等于的话必须左移动
		}
	}
	if left >= len(nums) || nums[left] != target{
		return -1
	}
	return left
}

func right_bound(nums []int,target int) int{
	left := 0
	right := len(nums) -1 // right表示末尾,不可取
	for left <= right{
		mid := left + (right - left) / 2
		if nums[mid] > target{
			right = mid -1
		}else if nums[mid] < target{
			left = mid + 1
		}else if nums[mid] == target{
			left = mid + 1
		}
	}
	if right < 0 || nums[right] != target{
		return -1
	}
	return right
}

35. 搜索插⼊位置

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

354. 俄罗斯套娃信封问题

 // 时间超时-先排序在按照dp去做

func maxEnvelopes(envelopes [][]int) int {
	sort.Slice(envelopes, func(i, j int) bool {
		if envelopes[i][0] > envelopes[j][0]{
			return false
		}else if envelopes[i][0] < envelopes[j][0]{
			return true
		}else {
			if envelopes[i][1] > envelopes[j][1]{
				return false
			}else if envelopes[i][1] <= envelopes[j][1] {
				return true
			}
		}
		return false
	})

	//for _,i := range envelopes{
	//	fmt.Println()
	//	for _,j := range i{
	//		fmt.Print(j," ")
	//	}
	//}
	max := 0
	clen := len(envelopes)
	dp := make([]int,clen)
	for i:=0;i<clen;i++{
		dp[i] = 1
	}
	for i:=0;i<clen;i++{
        cv := 1
		for j := i - 1 ;j>=0;j--{
			if envelopes[i][0] > envelopes[j][0] && envelopes[i][1] > envelopes[j][1]{
				// dp[i] = dp[j] + 1
				// break
                if cv < dp[j] + 1{
                    cv = dp[j] + 1
                }
			}
		}
        dp[i] = cv
		if dp[i] > max{
			max = dp[i]
		}
	}
	return max
}

392. 判断⼦序列

func isSubsequence(s string, t string) bool {
    if len(s) == 0{
        return true
    }
	index := 0
	for i :=0; i<len(t);i++ {
		if t[i] == s[index]{
			index ++
		}
		if index == len(s){
			return true
		}
	}
	return false
}

704. ⼆分查找

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

793. 阶乘函数后 K 个零

// 思想是考虑0的得出,计算2和5的个数,因为5的个数比2少,所以计算5的个数就能得出结果,又因为从0到无从大,末尾零的个数是线性增加的,取得了两边的边界就能得出结果,所以采用二分去做。

func preimageSizeFZF(k int) int {

    return rightBound(0,math.MaxInt64,k) - leftBound(0,math.MaxInt64,k) + 1
}
func leftBound(left int ,right int, target int)int{
    for left <= right{
        mid := left + (right - left) / 2
        if f(mid) > target{
            right = mid - 1
        }else if f(mid) < target{
            left = mid + 1
        }else if f(mid) == target{
            right = mid - 1
        }
    }
    return left
}
func rightBound(left int ,right int, target int)int{
    for left <= right{
        mid := left + (right - left) / 2
        if f(mid) > target{
            right = mid - 1
        }else if f(mid) < target{
            left = mid + 1
        }else if f(mid) == target{
            left = mid + 1
        }
    }
    return right
}
func f(n int)int{
    if n ==0{
        return 0
    }
    return n / 5 + f(n/5)
}

 

875. 爱吃⾹蕉的珂珂(从答案的角度去进行二分查找-二分查找答案)

func minEatingSpeed(piles []int, h int) int {
	left := 1
	right := 1000000001
	for left < right{
		mid := left + (right - left ) / 2
		flag := speedToHour(piles,mid)
		if flag <= h{
			right = mid
		}else if flag > h{
			left = mid + 1
		}
	}
	return left
}
func speedToHour(piles []int, speed int) (int) {
	res := 0
	for _,val := range piles{
		res += (val / speed)
		if val % speed != 0{
			res ++
		}
	}
	return res
}

1011. 在 D 天内送达包裹的能⼒

func shipWithinDays(weights []int, days int) int {
    
    left := 1
    for _,val := range weights{
        if val > left{
            left = val
        }
    }
    right := 500 * 50000
    for left < right{
        mid := left + (right - left) / 2
        flag := transportDay(weights,mid)
        if flag > days{
            left = mid + 1
        }else if flag <= days{
            right = mid
        }
    }
    return left
}

func transportDay(weights []int,transport int)int{
    day := 0
    cv := 0
    for _,val := range weights{
        if cv + val > transport{
          if cv !=0{
              day ++
          }
          cv = val

        }else if cv + val == transport{
            cv = 0
            day ++
        }else if cv + val < transport{
            cv += val
        }
    }
    if cv > 0{
        day ++
    }
    return day
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿联爱学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值