6.2.1 python二分查找算法及LeetCode题目(1) —— Find First and Last Position & Find Peak Element

二分查找基本格式

算法要点:

(1)从中间位置开始,如果正好是要查找的元素或满足的条件,则搜素过程结束,这意味着你要有一个return或break的操作;

(2)如果不是,每一次比较都使搜索范围缩小一半,这意味着你要改变搜索的left和right。

废话少说,直接看一下二分查找的基本写法,以while 写法为例,需要注意(1)循环的条件带等号,(2)mid 计算的这个写法,是防止(lt +rt) 数值溢出,有的网上的题目发生过这种情况,其实一般来说 `mid = (lt + rt) // 2` 也可;(3)思考一下边界的情况,有助于理解 各处有没有等号,以及 lt = mid + 1, 而不是 lt = mid。总之, 下面的格式好好套用就ok。

nums = [1, 3, 4, 8, 12, 31, 34, 37, 45, 66, 77]
target = 66

# 非递归的方法
def binary_search(nums, target):
    lt, rt = 0, len(nums) - 1
    while lt <= rt:
        mid = lt + (rt - lt) // 2

        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            lt = mid + 1
        else:
            rt = mid - 1
    return False

print(binary_search(nums, target))

# 递归方法
def binary_search_rec(nums, target):
    def rec_binary_search(nums, lt, rt, target):
        if lt > rt:
            return False
        mid = lt + (rt - lt) // 2
        if nums[mid] == target:
            return mid
        elif nums[mid] < target:
            return rec_binary_search(nums, mid+1, rt, target)
        else:
            return rec_binary_search(nums, lt, mid - 1, target)

    lt, rt = 0, len(nums) - 1
    return rec_binary_search(nums, lt, rt, target)

print(binary_search_rec(nums, target))

对于二分查找, 查找的内容一般分为两类,一类是找某个确定的元素,所给数组可能是有所变化的,比如旋转过的或者二维的;另一类是找满足条件的位置, 此时注意 满足的条件 替换上面代码 `nums[mid] == target`, 满足的条件需要仔细琢磨边界条件,容易出错。

下面两道题是比较基础的题目,用来熟悉一下上面的基本套路。难题在后面呢。

34. Find First and Last Position of Element in Sorted Array

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

题目解析

该题目是虽然是要找一个数,其实是第二类,找位置的,满足的条件写的很长,要思考到边界条件的情况;解题为两步,第一步找起始索引,找不到的话,return [-1, -1];有的话再找结束索引;套用上面的格式即可,代码如下:

class Solution:
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        l = len(nums)
        
        if l == 0:
            return [-1, -1]
        elif l == 1:
            return [0, 0] if nums[0] == target else [-1, -1]
        
        lt, rt = 0, l- 1
        min_pos = -1
        while lt <= rt:
            mid = (lt + rt) // 2
            if mid > 0 and nums[mid] == target and nums[mid-1] < target or mid == 0 and nums[mid] == target:
                min_pos = mid  # 该数字起始位置
                break
            if nums[mid] >= target:
                rt = mid - 1
            else:
                lt = mid + 1
        if min_pos == -1:   # 没有该数字
            return [-1, -1]
        
        max_pos = min_pos
        lt, rt = min_pos, l-1
        while lt <= rt:
            mid = (lt + rt) // 2
            if mid + 1 < l and nums[mid] == target and nums[mid+1] > target or mid == l-1 and nums[mid] == target:
                max_pos = mid  # 该数字结束位置
                break
            if nums[mid] == target:
                lt = mid + 1
            else:
                rt = mid - 1
        return [min_pos, max_pos]
        
        

162. Find Peak Element

A peak element is an element that is greater than its neighbors.

Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index.

The array may contain multiple peaks, in that case return the index to any one of the peaks is fine.

You may imagine that nums[-1] = nums[n] = -∞.

Example 1:

Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is a peak element and your function should return the index number 2.

题目解析:

看似复杂的题目,实际上题目的背景限制很多,降低了难度。1. 也是找满足某个条件的一个位置,条件上考虑到边界情况,鄙人分三条写了三个条件;2. 这种问题用二分解决的思路是,数组是排序了的,或者是递增或者是递减或者就是我们要找的位置,进一步说,就是递增部分和递减部分的中间是我们找的peak element。代码如下:

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        if len(nums) == 0:
            return None
        if len(nums) == 1:
            return 0
        l = len(nums)
        lt, rt = 0, l-1
        
        while lt <= rt:
            mid = lt + (rt-lt)//2
            if mid == 0 and nums[mid] > nums[mid+1]:
                return mid
            elif mid == l-1 and nums[mid] > nums[mid-1]:
                return mid
            elif mid != 0 and mid != l-1 and nums[mid-1] < nums[mid] and nums[mid] > nums[mid+1]:
                return mid
            elif mid == 0 and nums[mid] < nums[mid+1]:
                lt = mid + 1
            elif mid == l-1 and nums[mid] < nums[mid-1]:
                rt = mid - 1
            elif mid != 0 and mid != l-1 and nums[mid-1] < nums[mid] < nums[mid+1]:
                lt = mid + 1
            else:
                rt = mid - 1

好啦, 二分查找的基础就是这些,包括套路和两道例题。更多精彩看下期~

猜你喜欢

转载自blog.csdn.net/xutiantian1412/article/details/88938242
今日推荐