学习数组的第一天

本文详细介绍了二分查找算法,包括查找有序数组中元素下标的不同写法以及搜索插入位置的方法,展示了JavaScript和Go语言的实现,并分析了它们的时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、二分查找

1.查找有序数组的元素下标

  • 示例1
    输入: nums = [-1,0,3,5,9,12], target = 9
    输出: 4
    解释: 9 出现在 nums 中并且下标为 4
  • 示例2
    输入: nums = [-1,0,3,5,9,12], target = 2
    输出: -1
    解释: 2 不存在 nums 中因此返回 -1

1.1 左闭右闭写法

区间的定义这就决定了二分法的代码应该如何写,因为定义target在 [left, right] 区间,所以有如下三点:

  1. right的初始化值应该取数组长度减一,因为右区间可以取等。
  2. while (left <= right)要是使用 <= ,因为left == right是有意义的,举例[1,1]。
  3. 当nums[middle] > target时,right赋值应该为middle - 1,因为当前这个nums[middle]一定不是target。

JS 代码

var BinaryLookup = function(nums,target) {
	let left = 0,right = nums.length - 1;
	while (left <= right){
		let middle = left + ((right - left) >> 1); //(right - left) >> 1 相当于(left + right) / 2
		if (nums[middle] > target){
			right = middle - 1;
		}else if (nums[middle] < target){
			left = middle + 1
		}else {
			return middle;
		}
	}
	return -1;
}
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

1.2 左闭右开写法

如果定义target在 [left, right) 区间,所以有如下三点:

  1. right的初始化值应该取数组长度,为了让右区间取到数组最后一个值。
  2. while (left < right)要是使用 < ,因为left == right是有无意义的,举例[1,1]。
  3. 当nums[middle] > target时,right赋值应该为middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle](比如数组1,2,4,5,6,target = 3,mid = 2,满足判断条件如果right = mid - 1会完美错开下标2这个位置)

GO 代码


func BinaryLookup(nums []int, target int) int {
	left, right := 0, len(nums)
	for left < right {
		middle := left + (right-left)>>1
		if nums[middle] > target {
			right = middle
		} else if nums[middle] < target {
			left = middle + 1
		} else {
			return middle
		}
	}
	return -1
}
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)

2.搜索插入位置

  • 描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
  • 示例 1:
    输入: [1,3,5,6], 5
    输出: 2
  • 示例 2:
    输入: [1,3,5,6], 2
    输出: 1

2.1 左闭右闭写法

基于上个题目的理论,分别处理如下四种情况:

  • 目标值在数组所有元素之前 nums = [2,3,4], target = 1;
    left的值为0不会改变,right的值最后参与循环会到-1,返回left或者right+1均可。
  • 目标值等于数组中某一个元素 return middle;
    这种情况符合第一个题目。
  • 目标值插入数组中的位置 [left, right]之间
    当最后一次满足nums[left] < target < nums[right], 且right - left = 1;target的下标应该是right才对哦。
    此时不管代码执行left=middle+1还是right=middle-1,返回left或者right+1均可。
  • 目标值在数组所有元素之后的情况 [left, right]
    left的值不断增大到right,最后left-right=1,返回left或者right+1均可。

JS 代码

var searchInsert = function(nums, target) {
	let left = 0;
	let right = nums.length - 1;
	while (left <= right) {
		let middle = left + parseInt((right - left)  / 2);
		if (target > nums[middle]) {
			left = middle + 1;
		}else if (target < nums[middle]){
			right = middle - 1;
		}else{
			return middle;
		}
	}
	return right + 1;
	//OR return left
};

2.2 左闭右开写法

基于上个题目的理论,分别处理如下四种情况:

  • 目标值在数组所有元素之前 nums = [2,3,4], target = 1;
    left的值为0不会改变,right的值最后参与循环会到0,返回left或者right均可。
  • 目标值等于数组中某一个元素 return middle;
    这种情况符合第一个题目。
  • 目标值插入数组中的位置 [left, right]之间
    当最后一次满足nums[left] < target < nums[right], 且right - left = 1;target的下标应该是right才对哦。
    此时不管代码执行left = middle+1还是right = middle,返回left或者right均可。
  • 目标值在数组所有元素之后的情况 [left, right]
    left的值不断增大到right,最后left==right,返回left或者right均可。

GO 代码

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值