17 基本查找算法:插值查找与斐波那契查找

一、插值查找

原理

在介绍插值查找之前,首先考虑一个新问题,为什么二分查找算法一定要是折半,而不是折四分之一或者折更多呢?

打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?
很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。

同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。

经过以上分析,折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的)。二分查找中查找点计算如下:
mid=(low+high)/2, 即mid=low+1/2*(high-low);

通过类比,我们可以将查找的点改进为如下:
  mid=low+(key-a[low])/(a[high]-a[low])*(high-low),

也就是将上述的比例参数1/2改进为自适应的,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。

基本思想:

基于二分查找算法改进,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。

复杂度分析:

查找成功或者失败的时间复杂度均为O(log2(log2n))。

注:对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。

Go语言描述
func InsertionSearch(arr []int, v, start, end int) int {
    if start <= end {
        mid := (v - arr[start]) / (arr[end] - arr[start]) * (end - start)
        // fmt.Println("mid:",mid)
        if arr[mid] == v {
            return mid
        } else if arr[mid] > v {
            // 左边
            InsertionSearch(arr, v, start, mid-1)
        } else {
            // 右边
            InsertionSearch(arr, v, mid+1, end)
        }
    }
    return -1
}

二、斐波那契查找

原理

上面我们讲了插值查找,它是基于折半查找改进,然后除了插值方式的切割外,还有基于斐波那契黄金分割点切割方式的改进。
我们先来了解一下斐波那契数列的特性:

|--------------- F(K)-1 ---------------|
|_______________________|_______________|
|------- F(K-1)-1 -----|--- F(K-2)-1 --|
斐波那契数列,又称黄金分割数列,之所以它又称为黄金分割数列,是因为它的前一项与后一项的比值随着数字数量的增多逐渐逼近黄金分割比值0.618。
所以斐波那契查找改变了二分查找中原有的中值 mid 的求解方式,其 mid 不再代表中值,而是代表了黄金分割点:
mid = left + F_{block - 1} - 1

基本思想:

假设表中有 n 个元素,查找过程为取区间中间元素的下标 mid ,对 mid 的关键字与给定值的关键字比较:
(1)如果与给定关键字相同,则查找成功,返回在表中的位置;
(2)如果给定关键字大,向右查找并减小2个斐波那契区间;
(3)如果给定关键字小,向左查找并减小1个斐波那契区间;
(4)重复过程,直到找到关键字(成功)或区间为空集(失败)。

复杂度分析:

最坏情况下,时间复杂度为O(lgN),且其期望复杂度也为O(lgN)。

Go语言描述
func FibonacciSearch(arr []int, v int) int {
    fibs := getFibonacciArray(10)
    fmt.Println("fibonacciArray:", fibs)

    // 根据斐波那契数列找适合的区间
    k := 0
    l := len(arr)
    for l > fibs[k] {
        k++
    }

    // 2、构建新序列,多出位补slice[n-1]
    tmpArr := make([]int, fibs[k]-1)
    copy(tmpArr, arr)
    for i := l; i < len(tmpArr); i++ {
        tmpArr[i] = arr[l-1]
    }

    // 开始斐波那契查找
    left, right := 0, l-1
    for left <= right {
        // 找黄金分割点
        mid := left + fibs[k-1] - 1
        // fmt.Println("mid:", mid)
        // fmt.Println("tmpArr:", tmpArr)

        if tmpArr[mid] == v {
            if mid < l {
                return mid
            } else {
                // 位于tempS的填补位
                return l - 1
            }
        } else if tmpArr[mid] > v {
            // 左边
            right = mid - 1
            // 查找值在前面的fibs(k-1)区间中
            k -= 1
        } else {
            // 右边
            left = mid + 1
            // 查找值在后面的fibs(k-2)区间中
            k -= 2
        }
    }
    return -1
}

// 获取斐波那契数列
func getFibonacciArray(n int) []int {
    fArr := make([]int, n+1, n+1) // 数列第一位从下标1开始

    fArr[1] = 1
    fArr[2] = 1

    for i := 3; i <= n; i++ {
        fArr[i] = fArr[i-1] + fArr[i-2]
    }

    return fArr[1:]
}

小结

  • 折半查找:以一半元素来分割数组;
  • 插值查找:根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key;
  • 斐波那契查找:以斐波那契数列元素之间的黄金分割点来分割数组。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335