基础算法之查找

来自图解LeetCode初级算法的笔记(分块查找没有研究)


为什么要查找
查找是搜索算法,可用在判断一个数是否在该数列种,或者找出一个无序数列中该数列的位置

顺序查找

就是将数列从头到尾的查找一遍

for i in rang(array):
    if  array[i]==key:
         return i
return -1

二分法查找

直接找数列中间的那个数字与被查找数(key)相比。如果这个数比key小,在就在这个有序数列的前面,否则就是这个然,要不然就在这个数的后面。相应的知道了范围,我们就可以在那个范围中去查找。
这里的关键在于如何确定,那一部分索引的中间元素,这里先引入索引0,和最后一个索引,先求出中间的索引,然后判断是在前的话,就把中间索引复制给right,然后继续求中间索引,这样不有可能会求不到么。不会,因为是有序的。


二分查找只能用于有顺序的数列(正序);
并不完全对,逆序必须对应得n[mid]于key值大小比较,然后改变right or left 需要更改方向

def binarysearch(arr,key): # 逆序就不可以了,很奇怪
    if arr: # 先判断,去大小,while True死循环,得到mid,判断arry[mid]与Key的大小
        left = 0
        right = len(arr) -1
        while True:
            mid = int((left+right)/2)
            # key在mid左侧
            if arr[mid] > key:
                right = mid -1
            # key在右侧
            elif arr[mid] < key:
                left = mid + 1
            elif arr[mid] == key:
                print('该key的索引在{}'.format(mid))
                return mid
    else:
        print('找不到')
        return -1

斐波那契查找

又称黄金分隔数列,指的是该数列中相邻两个数的壁纸越趋向于黄金比例值。相较于二分查找来说,它的分隔点是0.618,假如一个数列中有1000个数,二分查找法查找的位置在索引500这,而斐波那契查找则在618这。
斐波那契查找的索引是按照斐波那契数的排列,比如数列长10,则找一个比他大的斐波那契数13,找13之前的斐波那契数8,找这个索引。然后不断循环,直至找到那个书。这里就需要用到斐波那契数列的构建,也就是递归了。

斐波那契数的列表相加生成

def fibonacci(n):
    '''return the last element of the fibonacci sequence'''
    fList = [1, 1]
    while n > 1 :
        fList.append(fList[-1] + fList[-2])
        n -= 1
    print(fList)
    return fList[-1]

接着是fibonacci查找关键代码

    k = 1
    while fibonacci(k) -1 < iLen - 1: #fibonacci数列中的最后一个元素要比iList的长度稍微大一点
        k += 1

    while right - left > 1:
        mid = left + fibonacci(k - 1) # fibonacci数列中的最后一个元素前面的那个数
        if key < iList[mid] :
            right = mid - 1 # 在左边,则中间索引减少一
            k -= 1 # 斐波那契数是前面一个元素
        elif key == iList[mid]:
            return mid
        else:
            left = mid + 1 # 在右边的话,中间索引加1,并且赋值给左边
            k -= 2 # k-2没搞懂,为啥减少2呢

插值查找

fibonacci查找和二分查找的区别在于查找数列中选取比较点(参照点)。而插值查找则给出了一个大多数情况下的理论上最科学的比较点
mid = left +(key-iList[left])*(right-left)//(iList[right]-iList[left]) # 那为什么这个点是查找的最优点呢
插值查找关键代码

    while right - left > 1:
        mid = left + (key - iList[left]) * (right - left) // (iList[right] - iList[left])
        if mid == left:
            mid += 1 #当iList[right]和iList[left]相差太大时,有可能导致mid一直都等于left,从而陷入死循环
        if key < iList[mid]:
            right = mid
        elif key > iList[mid]:
            left = mid
        else:
            return mid
    if key == iList[left]:
        return left
    elif key == iList[right]:
        return right
    else:
        return -1

分块查找

分块查找与以上的查找方式不同,顺序查找的数列可以是无序的,二分查找和斐波那契查找数列必须是有序的,分块查找介于两者之间,需要块有序,元素可以无序。插值查找也是有序查找

原理:

按照一定的取值范围将数列分成数块,块内的元素可以是无序,单块必须有序
块有序:处于后面位置的块中的最小元素都要比前面位置块中的最大元素大

第一次查找是将被查找数key与块的分界数相比较,确定key属于哪个块

第二次查找

块内的查找就是顺序查找

分块操作(没研究)

iList = randomList(20)
indexList = [[250, 0], [500, 0], [750, 0], [1000, 0]]

def divideBlock():
    global iList, indexList 
    sortList = []
    for key in indexList: # 分块操作
        subList = [i for i in iList if i < key[0]] #列表推导, 小于key[0]的单独分块
        key[1] = len(subList)
        sortList += subList  # 加入到新建的列表中
        iList = list(set(iList) - set(subList)) #过滤掉已经加入到subList中的元素
    iList = sortList
    print()
    return indexList

在块中进行查找

def blockSearch(iList, key, indexList):
    print("iList = %s" %str(iList))
    print("indexList = %s" %str(indexList))
    print("Find The number : %d" %key)
    left = 0 #搜索数列的起始点索引
    right = 0 #搜索数列的终点索引
    for indexInfo in indexList:
        left += right
        right += indexInfo[1]
        if key < indexInfo[0]:
            break
    for i in range(left, right):
        if key == iList[i]:
            return i
    return -1
posted @ 2021-10-01 14:51  索匣  阅读(65)  评论(0编辑  收藏  举报