【leetcode】在排序数组中查找元素的第一个和最后一个位置

154 阅读1分钟

题目

原文链接:leetcode-cn.com/problems/fi…

读题

  1. 给一个升序排列的整数数组
  2. 找出目标值的开始位置和结束位置
  3. 不存在则返回[-1,-1]
  4. 尽可能在时间内解决

解题

  1. 很明显,这道题是二分法的变形。
  2. 最简单的是先使用二分法找出目前值的一个位置,然后向两边扩展寻找出边界值。
  3. 这种方法情况下,如果数组为[1,1,1,1,...,1,1,1],目标值为1的情况下,会遍历整个数组,效率为
  4. 一种解决方案是通过两次二分查找来解决,分别找到开始、结束位置。二分法的本质是减少查找范围,尝试能否通过改变判断条件来查找边界值。

go语言+二分法+暴力搜索

package main

import (
	"fmt"
)

func searchRange(nums []int, target int) []int {

	ans := []int{-1, -1}
	targetPos := -1
	left, right := 0, len(nums)-1
	// 二分法找到target出现的位置
	for left <= right {
		mid := left + (right-left)/2
		if nums[mid] == target {
			targetPos = mid
			break
		} else if nums[mid] < target {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	// 没有找到
	if targetPos == -1 {
		return ans
	}
	// 找到位置后,向两边扩展找边界,这里极限情况下会有性能问题,比如数组为[8,8,...,8,8,8],target=8的情况
	for i := targetPos; i >= 0; i-- {
		if nums[i] == target {
			ans[0] = i
		}
	}
	for i := targetPos; i < len(nums); i++ {
		if nums[i] == target {
			ans[1] = i
		}
	}
	return ans
}

func main() {
	arr := []int{1, 2, 3, 4, 5, 5, 5, 5, 6, 7, 8, 9, 10}
	fmt.Println(searchRange(arr, 5))
}

go+两次二分法解决问题

package main

import (
	"fmt"
)

func searchRange(nums []int, target int) []int {
	length := len(nums)
	start, end := -1, -1
	left, right := 0, length-1
	// 二分法找到target开始出现的位置
	for left <= right {
		mid := left + (right-left)/2
		if nums[mid] == target {
			if mid > 0 && nums[mid-1] == target {
				right = mid - 1
			} else {
				start = mid
				break
			}
		} else if nums[mid] < target {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	left, right = 0, length-1
	// 二分法找到target最后出现的位置
	for left <= right {
		mid := left + (right-left)/2
		if nums[mid] == target {
			if mid < length-1 && nums[mid+1] == target {
				left = mid + 1
			} else {
				end = mid
				break
			}
		} else if nums[mid] < target {
			left = mid + 1
		} else {
			right = mid - 1
		}
	}
	return []int{start, end}
}

func main() {
	arr := []int{1, 2, 3, 4, 5, 5, 5, 5, 6, 7, 8, 9, 10}
	fmt.Println(searchRange(arr, 5))
}

反思与总结

  1. 通过本道题对二分法有了进一步的认识。维基百科对二分法的解释如下:

二分法(dichotomy)指的是将一个整体事物分割成两部分。也即是说,这两部分必须是互补事件,即所有事物必须属于双方中的一方,且互斥,即没有事物可以同时属于双方。

之后如果遇到二分法的变形题,我想关键是每次查找将搜索范围减半,要么确定搜索值在哪个半区,要么反其道确定搜索值不在某个半区从而排除掉。