[Go版]算法通关村第九关青铜——透彻理解二分查找

二分查找

题目链接:LeetCode-704. 二分查找
在这里插入图片描述

原理:每次将查找范围缩小一半

二分查找(Binary Search)是一种高效的搜索算法,用于在有序数组中查找特定元素的位置。
其基本原理是将查找范围分成两半,然后判断目标元素与中间元素的大小关系,从而确定目标元素可能存在的区间,然后重复这个过程缩小查找范围直到找到目标元素或确定目标元素不存在

前提:有序数组

二分查找的前提是数组必须是有序的,如果数组无序,需要先进行排序操作。

复杂度:时间复杂度 O ( l o g n ) O(log n) O(logn)

二分查找的关键在于每次将查找范围缩小一半,因此其时间复杂度为 O(log n),其中 n 是数组的大小。

二分查找的Go代码实现

源码地址: GitHub-golang版本(有对应的单元测试代码)

思路分析

  1. 首先,确定要查找的范围。将查找范围的起始索引(通常记为 left)和结束索引(通常记为 right)初始化为数组的两个端点。
  2. 计算中间索引 mid,即 mid = (left + right) / 2。这将把查找范围分成两半。
    • 优化1:由于在计算机中,除法的效率非常低,所以这里用位运算代替除法:mid = (left + right) >> 1
    • 优化2:假如left和hign很大的话,left+right可能会溢出,所以可以改成:mid = left + ((right - left) >> 1)
  3. 比较中间元素 arr[mid] 与目标元素 target 的大小关系:
    • 如果 arr[mid] == target,则找到了目标元素,返回 mid。
    • 如果 arr[mid] < target,则目标元素可能在右半部分,将 left 更新为 mid + 1。
    • 如果 arr[mid] > target,则目标元素可能在左半部分,将 right 更新为 mid - 1。
  4. 重复步骤 2 和 3,不断缩小查找范围,直到 left 大于 right,表示查找范围为空,此时目标元素不存在,返回 -1。

循环的方式

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

递归的方式

func SearchByRecursion(nums []int, target int) int {
	length := len(nums)
	if length == 0 {
		return -1
	}
	return findTarget(nums, 0, length-1, target)
}

func findTarget(arr []int, left int, right int, target int) int {
	ret := -1
	if left > right {
		return ret
	}
	mid := left + ((right - left) >> 1)
	if arr[mid] == target {
		return mid
	} else if arr[mid] < target {
		return findTarget(arr, mid+1, right, target)
	} else {
		return findTarget(arr, left, mid-1, target)
	}
}

进阶考虑:元素中有重复的二分查找

找到后再线性找

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

找到后再二分查找

下面代码在找到目标值时会继续向左搜索,直到找到第一个不等于目标值的元素。通过这种方式,可以有效地找到目标值的最左位置,时间复杂度仍然是 O(log n)。

func Search2ByForLeftInRepeatArr(nums []int, target int) int {
	ret := -1
	length := len(nums)
	if length == 0 {
		return ret
	}
	left, right := 0, length-1
	for left <= right {
		mid := left + ((right - left) >> 1)
		if nums[mid] == target {
			right = mid - 1
		} else if nums[mid] < target {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	if left < length && nums[left] == target {
		return left
	}
	return ret
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值