二分查找

一、寻找一个数

1.左闭右闭

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

2.左闭右开

func search(nums []int, target int) int {
    lf, rt := 0, len(nums) // 左闭右开
    // 不用等于为了避免lf=rt=len(nums)越界
    for lf < rt{ 
        mid := lf + (rt-lf)/2
        if nums[mid] < target{
            lf = mid + 1 
        }else if nums[mid] > target{
            rt = mid // 区别
        }else if nums[mid] == target{
            return mid 
        }
    }
    // lf < len(nums)避免越界 如1,2,3,4中找8,此时lf=4越界
    // nums[lf] == target lf==right这种情况漏了
    if lf < len(nums) && nums[lf] == target{ 
        return lf
    }
    return -1
}

二、寻找边界

常用左闭右开方法,该方法更好理解,不容易出错!

1.左闭右开

// 左边界 左闭右开
func leftBound(nums []int, target int) int {
	if len(nums) == 0 {
		return -1
	}
	lf, rt := 0, len(nums) // 左闭右开
	for lf < rt {
		mid := lf + (rt-lf)/2
		if nums[mid] < target {
			lf = mid + 1
		} else if nums[mid] > target {
			rt = mid
		} else if nums[mid] == target {
			rt = mid
		}
	}
	// target比nums中的所有数都要大的时候 lf >= len(nums)
	// 如果nums中不存在与target相等的数 但有小于target的数时会出现lf < len(nums) && nums[lf] != target
	if lf < len(nums) && nums[lf] == target {
		return lf
	}
	return -1
}
// 右边界 左闭右开
func rightBound(nums []int, target int) int {
	if len(nums) == 0 {
		return -1
	}
	lf, rt := 0, len(nums) // 左闭右开
	for lf < rt {
		mid := lf + (rt-lf)/2
		if nums[mid] < target {
			lf = mid + 1
		} else if nums[mid] > target {
			rt = mid
		} else if nums[mid] == target {
			lf = mid + 1
		}
	}
	// target比nums中的左右数都要小的时候 lf-1 < 0
	// 如果nums中不存在与target相等的数 但有小于target的数时会出现lf-1 >= 0 && nums[lf-1] != target
	// 因为nums[mid] == target时lf=mid+1,所以结束循环时lf=右边界+1
	if lf-1 >= 0 && nums[lf-1] == target {
		return lf - 1 
	}
	return -1
}

2.左闭右闭

越界现象

// 寻找左边界 左闭右闭
func leftBound(nums []int, target int) int {
	lf, rt, mid := 0, len(nums)-1, 0
	for lf <= rt {
		mid = (lf + rt) >> 1
		if target < nums[mid] {
			rt = mid - 1
		} else if target > nums[mid] {
			lf = mid + 1
		} else {
			rt = mid - 1 // 右边界等于中间值--
		}
	}
	if lf >= len(nums) || nums[lf] != target{
	    return -1
	}
	return lf
}
// 寻找右边界 左闭右闭
func rightBound(nums []int, target int) int {
	lf, rt, mid := 0, len(nums)-1, 0
	for lf <= rt {
		mid = (lf + rt) >> 1
		if target < nums[mid] {
			rt = mid - 1
		} else if target > nums[mid] {
			lf = mid + 1
		} else {
			lf = mid + 1 // 左边界等于中间值++
		}
	}
	if rt >= 0 && nums[rt] == target {
		return rt
	}
	return -1
}

参考链接

原文地址:https://www.cnblogs.com/lsyy2020/p/14826584.html