【查找算法】二分查找/插值查找/斐波那契查找(PHP)

除了排序,查找指定值也是常见的功能,所以非常有必要掌握一下相关算法。经典查找算法有顺序查找二分查找差值查找斐波那契查找。顺序查找比较简单就不介绍了,下面主要介绍剩余三种查询算法的实现原理PHP代码示例,以及性能分析
<输入的最好方式就是输出>,本着学习的态度表达一下自己浅显的理解。

1.二分查找
二分查找算法的实现前提是待排数组必须是有序的(以下算法均以升序为例),其主要思想是通过将每次取数组中间位置作为比较对象,小于该位置则到左区间寻找,大于该位置则到右区间寻找,左右区间按照同样的方式,循环取当前区间中间值比较,直到找到最终结果。

代码如下:

function binarySearch(array $arr, int $val)
{
    $len = count($arr);
    $left = 0;//初始区间左下标
    $right = $len - 1;//初始区间右下标
    $result = -1;
    //越界处理
    if ($val > $arr[$right] || $val < $arr[$left]){
        return $result;
    }
    //边界处理
    if ($arr[$left] == $val){
        return $left;
    }elseif ($arr[$right] == $val){
        return $right;
    }else{
        //开始二分查找
        while ($left <= $right) {
            $mid = $left + intval(($right - $left) / 2);//取中间值
            if ($arr[$mid] == $val) {
                return $mid;//返回待查值下标位置
            } elseif ($arr[$mid] > $val) {
                //值在左边
                $right = $mid - 1;
            } else {
                //值在右边
                $left = $mid + 1;
            }
        }
    }
    return $result;//未找到
}

记忆关键词:区间二分
性能分析:时间复杂度O(logn)

2.插值查找
插值查找是二分查找的优化,区别在于:二分查找每次与中间位置进行比较,而插值查找每次与最靠近待查值的位置进行比较,比盲目的二分策略性更强。

代码如下:

function interpolationSearch(array $arr, int $val)
{
    $len = count($arr);
    $left = 0;
    $right = $len - 1;
    $result = -1;
    //边界处理
    if ($val < $arr[$left] || $val > $arr[$right]) {
        return $result;
    }
    while ($left < $right) {
        $mid = $left + abs(($val - $arr[$left]) * ($right - $left) / ($arr[$right] - $arr[$left]));//注意绝对值
        $mid = intval($mid);
        if ($arr[$mid] == $val) {
            return $mid;//返回元素下标
        } elseif ($arr[$mid] > $val) {
            //左侧
            $right = $mid - 1;
        } else {
            //右侧
            $left = $mid + 1;
        }
    }
    return -1;//未找到
}

插值查询的关键在于mid的确定,根据下图等式可得到mid的值

1-mid公式

记忆关键词: 差值比例
性能分析:时间复杂度O(logn)

3.斐波那契查找
斐波那契查找算法是斐波那契数列的应用,斐波那契数列又称黄金分割数列,其核心公式为F(n)=F(n-1)+F(n-2),即从第三项开始,每一项可以分割为前一项和前两项之和,而前一项与当前项的比值接近0.618,该比例称为黄金分割比例,斐波那契查找算法的核心思想就是将数组按照黄金分割比例进行查找划分。代码实现思路:

  • 构造斐波那契数列
  • 找到大于等于数组长度的最小值k,即F(k)>=len
  • 最大值填充数组多余长度
  • 按照黄金分割比例进行查找
  • 若未找到,根据差值进入左右分区,左分区长度为F(k-1)右分区为F(k-2)
  • 在新的分区重复黄金分割比例查找,直到找到结果或未找到结果。

代码如下:

/**
 * 获取斐波那契数列值
 * F(1)=F(2)=1,F(k)=F(k-1)+F(k-2),k>=3
 * @param int $k
 * @return int|void
 * @throws Exception
 */
function fioNum(int $k = FIBONACCI_NUM)
{
    if ($k < 1) {
        throw new Exception('k必须为大于0的整数');
    }
    if ($k == 1 || $k == 2) {
        return 1;//数列初始值
    }
    return fioNum($k - 1) + fioNum($k - 2);
}
/**
 * 斐波那契查找
 * @param array $arr
 * @param int $val
 * @return bool|int|mixed
 * @throws Exception
 */
function fibSearch(array $arr, int $val)
{
    $len = count($arr);
    //数组长度小于3,直接用顺序查找
    if ($len < 3) {
        foreach ($arr as $k => $v) {
            if ($v == $val) {
                return $k;
            }
        }
        return -1;
    }
    $left = 0;
    $right = $len - 1;
    $fibArr = [];//斐波那契数列
    $k = 0;//斐波那契数列最小值初始化
    try {
        //1.构造斐波那契数列
        $fibNum = 0;
        while ($len > $fibNum) {
            //2.寻找大于等于数组长度的最小值k
            $k++;
            $fibNum = fioNum($k);
            $fibArr[] = $fibNum;
        }
    } catch (Exception $e) {
        throw new Exception($e->getMessage());
    }
    //3.对多余部分进行最大值填充
    for ($i = $len - 1; $i < $fibNum; $i++) {
        $arr[$i] = $arr[$right];
    }
    $k--;//数组下标从0开始,所以要减1
    //4.进行黄金分割查找
    while ($left <= $right) {
        $mid = $left + $fibArr[$k - 1] - 1;//确定黄金分割位置,长度-1表示间隔距离
        if ($arr[$mid] == $val) {
            //判断是否是填充数据
            if ($mid > $right) {
                return $right;
            } else {
                return $mid;
            }
        } elseif ($arr[$mid] > $val) {
            //在左边
            $right = $mid - 1;
            $k -= 1;//左边区域长度F(k-1)
        } else {
            //在右边
            $left = $mid + 1;
            $k -= 2;//右边区域长度F(k-2)
        }
    }
    return -1;//没找到
}

记忆关键词: 斐波那契数列、黄金分割比例
性能分析:时间复杂度O(logn)

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

推荐阅读更多精彩内容