算法通关村第九关-二分查找:逢试必考

二分查找:逢试必考

二分查找和二叉树的中序遍历的底层原理是一致的,只不过考察侧重点不一样

查找概述
查找可以很简单,也可以很复杂,散列、动态规划等高难度算法都可以视为查找问题;

常见的查找算法有顺序查找、二分查找、插值查找、斐波那契查找、树表查找、分块查找、哈希查找等;

二分查找、插值查找、斐波那契查找可以归为一类——插值查找。插值查找和斐波那契查找是在二分查找的基础上的优化查找算法;

重要:哈希查找,二分查找

凡是涉及到在排好序的地方的查找,都可以考虑二分来优化查找效率。不一定全局都排好才行,只要某个部分是排好的,就可以针对该部分进行二分查找,这是优化查找的重要途径。

二分查找的进一步拓展,二分每次取一半,但在某些场景下,大致知道数据的位置了,不必折半,取1/3、1/4这样也可以。

插值查找的通用公式:
mid = low + (key-a[low])/(a[high]-a[low])*(high-low)
注:公式没看懂

分块查找
分块查找是折半(二分)查找和顺序查找的一种改进方法;
只要求索引表是有序的,对块内节点没有排序要求,特别适合于节点动态变化的情况;

分块查找要求把一个数据分为若干块,每一块里面的元素可以是无序的,但是块与块之间的元素需要是有序的。
举例,即第1块中任一元素的关键字必须小于第2块中任一元素的关键字,第2块中任一元素的关键字必须小于第3块中任一元素的关键字,依次类推。

基本查找

最简单,有序无序都可以,不需要排序,一个个对比,效率低下。

def search(arr, key):
    for i in range(len(arr)):
        if arr[i] == key:
            return i
    return -1

注:二分查找、插值查找、斐波那契查找都是基于已经排序过的数据

二分查找与分治法

分治法/分治思想
即分而治之,把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题…直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
注:分治是很多高效算法的基础,如二分查找、排序算法(快速排序、归并排序)等

二分查找

二分查找就是将中间结果与目标进行比较,一次去掉一半。二分查找可以用递归或者循环的方式来做。
注:最简单、最典型的分治

循环方式二分查找

代码实现


def binary_search(array, low, high, target):
    while low <= high:
        mid = (low + high) // 2
        if array[mid] == target:
            return mid
        elif array[mid] > target:
            high = mid - 1
        else:
            low = mid + 1
    return -1


if __name__ == '__main__':
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 8))
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 20))

注:
如果用Java实现,存在一个细节改进(python语言里没有该问题)

原始:int mid = (low+high)/2
改进1:int mid = (low+high)>>1 说明:除的效率低,移位代替
改进2:int mid = low+((high-low)>>1) 说明:low+high可能会溢出

递归方式二分查找

代码实现


def binary_search(array, low, high, target):
    if low > high:
        return -1
    mid = (low + high) // 2
    if array[mid] == target:
        return mid
    elif array[mid] > target:
        return binary_search(array, low, mid - 1, target)
    else:
        return binary_search(array, mid + 1, high, target)


if __name__ == '__main__':
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 8))
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 20))

元素中有重复的二分查找

在上面的基础上,元素存在重复,如果重复则找左侧第一个

关键:找到目标结果之后不是返回而是继续向左侧移动。
方法1:找到相等位置向左使用线性查找,直到找到对应的位置
方法2:加入重复的数量特别大,考虑使用二分。找到目标元素之后根据要求继续递归寻找

# 方法1
def binary_search(array, low, high, target):
    if low > high:
        return -1
    mid = (low + high) // 2
    if array[mid] == target:
        # 若存在重复元素,左移,寻找最左边的元素
        if mid == 0:
            return mid
        else:
            while mid >= 0 and array[mid] == target:
                mid -= 1
            return mid + 1

    elif array[mid] > target:
        return binary_search(array, low, mid - 1, target)
    else:
        return binary_search(array, mid + 1, high, target)


if __name__ == '__main__':
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 7))
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 0, 9, 20))
    print(binary_search([0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 8, 9], 0, 11, 7))

# 方法1优化
def binary_search(array, low, high, target):
    if low > high:
        return -1
    mid = (low + high) // 2
    if array[mid] == target:
        # 若存在重复元素,左移,寻找最左边的元素
        while mid >= 0 and array[mid] == target:
            mid -= 1
        return mid + 1
    elif array[mid] > target:
        return binary_search(array, low, mid - 1, target)
    else:
        return binary_search(array, mid + 1, high, target)

方法2:二分查找

"""
题目:
有重复元素的二分查找,如果有重复元素则找左侧第一个
"""


def binary_search(nums, target):
    if not nums:
        return -1
    left = 0
    right = len(nums) - 1

    while left < right:
        mid = (left + right) // 2
        if nums[mid] < target:
            left = mid + 1
        elif nums[mid] == target:
            right = mid
        else:
            right = mid - 1
    return right if nums[right] == target else -1


if __name__ == '__main__':
    print(binary_search([], 4))  # -1
    print(binary_search([0, 1, 2, 3], 10))  # -1
    print(binary_search([0, 1, 2, 3], -10))  # -1

    print(binary_search([0, 1, 2, 3, 4, 5], 4))  # 4
    print(binary_search([0, 1, 2, 3, 3, 4, 5], 3))  # 3
    print(binary_search([0, 1, 2, 3, 3, 3, 4, 5], 3))  # 3
    print(binary_search([0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5], 3))  # 3

你可能感兴趣的:(算法)