【数据结构与算法-Day 37】超越二分查找:探索插值、斐波那契与分块查找的奥秘

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的"USB-C",模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

Docker系列文章目录

数据结构与算法系列文章目录

01-【数据结构与算法-Day 1】程序世界的基石:到底什么是数据结构与算法?
02-【数据结构与算法-Day 2】衡量代码的标尺:时间复杂度与大O表示法入门
03-【数据结构与算法-Day 3】揭秘算法效率的真相:全面解析O(n^2), O(2^n)及最好/最坏/平均复杂度
04-【数据结构与算法-Day 4】从O(1)到O(n²),全面掌握空间复杂度分析
05-【数据结构与算法-Day 5】实战演练:轻松看懂代码的时间与空间复杂度
06-【数据结构与算法-Day 6】最朴素的容器 - 数组(Array)深度解析
07-【数据结构与算法-Day 7】告别数组束缚,初识灵活的链表 (Linked List)
08-【数据结构与算法-Day 8】手把手带你拿捏单向链表:增、删、改核心操作详解
09-【数据结构与算法-Day 9】图解单向链表:从基础遍历到面试必考的链表反转
10-【数据结构与算法-Day 10】双向奔赴:深入解析双向链表(含图解与代码)
11-【数据结构与算法-Day 11】从循环链表到约瑟夫环,一文搞定链表的终极形态
12-【数据结构与算法-Day 12】深入浅出栈:从"后进先出"原理到数组与链表双实现
13-【数据结构与算法-Day 13】栈的应用:从括号匹配到逆波兰表达式求值,面试高频考点全解析
14-【数据结构与算法-Day 14】先进先出的公平:深入解析队列(Queue)的核心原理与数组实现
15-【数据结构与算法-Day 15】告别"假溢出":深入解析循环队列与双端队列
16-【数据结构与算法-Day 16】队列的应用:广度优先搜索(BFS)的基石与迷宫寻路实战
17-【数据结构与算法-Day 17】揭秘哈希表:O(1)查找速度背后的魔法
18-【数据结构与算法-Day 18】面试必考!一文彻底搞懂哈希冲突四大解决方案:开放寻址、拉链法、再哈希
19-【数据结构与算法-Day 19】告别线性世界,一文掌握树(Tree)的核心概念与表示法
20-【数据结构与算法-Day 20】从零到一掌握二叉树:定义、性质、特殊形态与存储结构全解析
21-【数据结构与算法-Day 21】精通二叉树遍历(上):前序、中序、后序的递归与迭代实现
22-【数据结构与算法-Day 22】玩转二叉树遍历(下):广度优先搜索(BFS)与层序遍历的奥秘
23-【数据结构与算法-Day 23】为搜索而生:一文彻底搞懂二叉搜索树 (BST) 的奥秘
24-【数据结构与算法-Day 24】平衡的艺术:图解AVL树,彻底告别"瘸腿"二叉搜索树
25-【数据结构与算法-Day 25】工程中的王者:深入解析红黑树 (Red-Black Tree)
26-【数据结构与算法-Day 26】堆:揭秘优先队列背后的"特殊"完全二叉树
27-【数据结构与算法-Day 27】堆的应用:从堆排序到 Top K 问题,一文彻底搞定!
28-【数据结构与算法-Day 28】字符串查找的终极利器:深入解析字典树 (Trie / 前缀树)
29-【数据结构与算法-Day 29】从社交网络到地图导航,一文带你入门终极数据结构:图
30-【数据结构与算法-Day 30】图的存储:邻接矩阵 vs 邻接表,哪种才是最优选?
31-【数据结构与算法-Day 31】图的遍历:深度优先搜索 (DFS) 详解,一条路走到黑的智慧
32-【数据结构与算法-Day 32】掌握广度优先搜索 (BFS),轻松解决无权图最短路径问题
33-【数据结构与算法-Day 33】最小生成树之 Prim 算法:从零构建通信网络
34-【数据结构与算法-Day 34】最小生成树之 Kruskal 算法:从边的视角构建最小网络
35-【数据结构与算法-Day 35】拓扑排序:从依赖关系到关键路径的完整解析
36-【数据结构与算法-Day 36】查找算法入门:从顺序查找的朴素到二分查找的惊艳

37-【数据结构与算法-Day 37】超越二分查找:探索插值、斐波那契与分块查找的奥秘


文章目录


摘要

在上一篇文章中,我们深入探讨了二分查找,它是在有序数组中进行高效查找的基石。然而,算法的世界永无止境。当数据分布呈现特定规律,或者我们面临更复杂的查找需求时,二分查找的固定分割策略可能并非最优解。本文将作为查找算法的进阶篇,带你探索三种二分查找的"变种"与优化:插值查找、斐波那契查找和分块查找。我们将详细解析它们的核心思想、实现原理、适用场景及性能优劣,并通过代码实战与图文对比,助你彻底掌握这些更精妙的查找技术,为你的算法工具箱再添利器。

一、温故知新:二分查找的局限性

在深入学习新的查找算法之前,我们先快速回顾一下二分查找(Binary Search)。它的核心思想是每次都从数组的正中间 位置 mid = (low + high) / 2 进行分割,将查找范围缩小一半。这种策略简单、稳定且高效,平均时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)。

但请思考一个场景:在一本按姓氏首字母排序的电话簿中查找姓 "Z" 的人。你会从中间(比如 "L" 或 "M" 姓)开始翻吗?显然不会。你更可能直接翻到电话簿的末尾部分。

这个简单的例子揭示了二分查找的一个"固执"之处:它没有利用数组中数据分布的任何信息。无论你要查找的值是靠近数组开头还是结尾,它都雷打不动地从正中间开始。这种"一视同仁"的策略在数据分布极不均匀时,效率就有提升的空间。正是为了解决这类问题,插值查找等更智能的算法应运而生。

二、插值查找 (Interpolation Search):自适应的二分

插值查找是二分查找的一种优化,它试图通过一个更"聪明"的方式来预测目标值可能出现的位置。

2.1.1 核心思想:按比例查找

插值查找的核心思想源于一个简单的线性插值公式。它不再是固执地取中点,而是根据要查找的关键字 key 与数组首尾元素的差值关系,来动态地计算 mid 的位置。

  • 类比:就像我们查字典或电话簿一样。如果要找的单词以 "A" 开头,我们会在字典的前几页查找;如果要找的以 "Z" 开头,我们则会翻到最后。插值查找就是这种思路的数学化表达。

2.1.2 原理与公式解析

插值查找的关键在于其 mid 值的计算公式。对于一个升序数组 arr,其 mid 的计算方式如下:

m i d = l o w + k e y − a r r [ l o w ] a r r [ h i g h ] − a r r [ l o w ] × ( h i g h − l o w ) mid = low + \frac{key - arr[low]}{arr[high] - arr[low]} \times (high - low) mid=low+arr[high]−arr[low]key−arr[low]×(high−low)

让我们来解析这个公式:

  • key - arr[low]:目标值与当前查找范围最小值的差。
  • arr[high] - arr[low]:当前查找范围最大值与最小值的差。
  • k e y − a r r [ l o w ] a r r [ h i g h ] − a r r [ l o w ] \frac{key - arr[low]}{arr[high] - arr[low]} arr[high]−arr[low]key−arr[low]:这个比率代表了 keyarr[low]arr[high] 这个值域范围内的相对位置。例如,如果 key 非常接近 arr[low],这个比率就接近 0;如果 key 接近 arr[high],比率就接近 1。
  • ... \times (high - low):将这个值域上的比例,映射到索引范围 lowhigh 上。
  • low + ...:最后,从 low 索引开始,加上这个偏移量,就得到了预测的 mid 索引。

2.1.3 适用场景与优劣势

(1) 适用场景

插值查找最适用于表长较长,且关键字分布比较均匀 的有序数组。在数据均匀分布的情况下,其平均时间复杂度可以达到惊人的 O ( log ⁡ ( log ⁡ n ) ) O(\log(\log n)) O(log(logn)),比二分查找的 O ( log ⁡ n ) O(\log n) O(logn) 还要快。

(2) 优劣势
  • 优势:在数据均匀分布时,查找效率极高。
  • 劣势 :对于分布极不均匀的数据,性能会急剧下降。在最坏情况下(例如,数据集中在某一端),mid 的计算可能持续偏向一侧,导致算法退化为顺序查找,时间复杂度变为 O ( n ) O(n) O(n)。此外,公式中包含乘法和除法,计算开销比二分查找的位移或加法要大。

2.1.4 代码实现与注释

以下是插值查找的 Java 实现:

java 复制代码
public class InterpolationSearch {

    /**
     * 插值查找
     * @param arr 有序数组
     * @param key 要查找的关键字
     * @return 找到则返回索引,否则返回 -1
     */
    public static int interpolationSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;

        // 核心循环条件
        // 1. low <= high 保证查找范围有效
        // 2. key >= arr[low] && key <= arr[high] 是必须的,
        //    因为 key 如果超出这个范围,(key - arr[low]) 可能会导致计算出的 mid 越界
        while (low <= high && key >= arr[low] && key <= arr[high]) {
            // 如果 low 和 high 相等,说明只剩一个元素
            if (low == high) {
                return (arr[low] == key) ? low : -1;
            }
            
            // 防止 arr[high] == arr[low] 导致除以零的错误
            if (arr[high] == arr[low]) {
                if (arr[low] == key) return low;
                else return -1;
            }

            // 计算 mid,这是与二分查找唯一的不同之处
            // 注意:为了防止溢出,可以写成 low + (int)(((double)(key - arr[low]) / (arr[high] - arr[low])) * (high - low));
            int mid = low + ((key - arr[low]) * (high - low)) / (arr[high] - arr[low]);

            if (arr[mid] == key) {
                return mid; // 找到目标
            } else if (arr[mid] < key) {
                low = mid + 1; // 目标在右侧
            } else {
                high = mid - 1; // 目标在左侧
            }
        }
        return -1; // 未找到
    }

    public static void main(String[] args) {
        // 一个分布相对均匀的数组
        int[] arr = new int[100];
        for (int i = 0; i < 100; i++) {
            arr[i] = i + 1;
        }
        int key = 88;
        int index = interpolationSearch(arr, key);
        System.out.println("在均匀数组中查找 " + key + ",索引为: " + index); // 输出 87

        // 一个分布不均匀的数组
        int[] unevenArr = {1, 2, 3, 4, 5, 1000, 1001, 1002};
        key = 1000;
        index = interpolationSearch(unevenArr, key);
        System.out.println("在不均匀数组中查找 " + key + ",索引为: " + index); // 输出 5
    }
}

三、斐波那契查找 (Fibonacci Search):黄金分割的艺术

斐波那契查找同样是一种基于二分思想的查找算法,但它分割数组的方式非常独特,利用了斐波那契数列和黄金分割原理。

3.1.1 核心思想:黄金分割点查找

斐波那契查找的核心是使用斐波那契数来确定分割点 。我们知道,斐波那契数列中相邻两项的比值( F [ k − 1 ] / F [ k ] F[k-1]/F[k] F[k−1]/F[k])随着 k k k 的增大,会无限趋近于黄金分割比例(约 0.618)。斐波那契查找就是利用这个特性来分割查找区间的。

它的一个主要优点是只涉及加法和减法运算,不使用乘除法,这在某些硬件环境下(如早期的计算机或某些嵌入式系统)可以带来性能优势。

3.1.2 原理与步骤

斐波那契查找的步骤稍显复杂:

  1. 构建斐波那契数列 :首先需要一个斐波那契数列,至少到其某一项 F[k] 的值大于等于原数组的长度 n

  2. 扩展数组 :为了让数组长度符合斐波那契分割的要求,需要将原数组的长度扩展到 m = F[k] - 1。扩展的部分通常用原数组的最后一个元素来填充。

  3. 迭代查找

    • 计算分割点 mid = low + F[k-1] - 1
    • 比较 keyarr[mid] 的值:
      • 如果 key < arr[mid],说明目标在左半部分。新的查找范围是 lowmid-1,长度为 F[k-1] - 1。此时,我们下一步需要在这个 F[k-1] - 1 长度的区间内查找,对应的是斐波那契数列的第 k-1 项,所以我们令 k = k-1
      • 如果 key > arr[mid],说明目标在右半部分。新的查找范围是 mid+1high,长度为 F[k-2] - 1。我们下一步需要在这个 F[k-2] - 1 长度的区间内查找,对应的是斐波那契数列的第 k-2 项,所以我们令 k = k-2
      • 如果 key == arr[mid],查找成功。需要注意的是,如果 mid 的索引大于原数组长度,说明命中的是填充值,应返回原数组的最后一个元素的索引。

3.1.3 适用场景与优劣势

(1) 适用场景

适用于有序数组,尤其是当乘除法运算成本较高时。虽然其平均时间复杂度也是 O ( log ⁡ n ) O(\log n) O(logn),与二分查找处于同一量级,但其理论性能常数更优。

(2) 优劣势
  • 优势:只用加减法,避免了乘除法,在特定环境下更快。
  • 劣势:实现比二分查找复杂,需要预先构建斐波那契数列,并且需要扩展原数组,增加了额外的空间开销和操作。

3.1.4 代码实现与注释

java 复制代码
import java.util.Arrays;

public class FibonacciSearch {
    
    private static final int MAX_SIZE = 20; // 斐波那契数列的最大长度

    // 生成斐波那契数列
    private static int[] fib() {
        int[] f = new int[MAX_SIZE];
        f[0] = 1;
        f[1] = 1;
        for (int i = 2; i < MAX_SIZE; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }

    /**
     * 斐波那契查找
     * @param arr 有序数组
     * @param key 要查找的关键字
     * @return 找到则返回索引,否则返回 -1
     */
    public static int fibonacciSearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        int k = 0; // 斐波那契数列的索引
        int[] f = fib();

        // 找到第一个大于等于数组长度的斐波那契数 F[k]
        while (arr.length > f[k] - 1) {
            k++;
        }

        // 创建一个临时数组,长度为 F[k] - 1
        int[] temp = Arrays.copyOf(arr, f[k] - 1);
        // 将超出原数组的部分用原数组最后一个元素填充
        for (int i = high + 1; i < temp.length; i++) {
            temp[i] = arr[high];
        }

        while (low <= high) {
            // 计算黄金分割点 mid
            int mid = low + f[k - 1] - 1;

            if (key < temp[mid]) { // 目标在左侧
                high = mid - 1;
                // 全部元素 = F[k-1] - 1,下次在 F[k-1] 中查找
                k--; 
            } else if (key > temp[mid]) { // 目标在右侧
                low = mid + 1;
                // 右侧元素 = F[k-2] - 1,下次在 F[k-2] 中查找
                k -= 2;
            } else { // 找到目标
                // 需要确认返回的索引是否在原数组范围内
                if (mid <= high) {
                    return mid;
                } else {
                    return high; // 命中填充值,返回原数组最后一个有效索引
                }
            }
        }
        return -1; // 未找到
    }

    public static void main(String[] args) {
        int[] arr = {10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100};
        int key = 100;
        int index = fibonacciSearch(arr, key);
        System.out.println("查找 " + key + ",索引为: " + index); // 输出 10
    }
}

四、分块查找 (Block Search):索引与顺序的结合

分块查找,又称索引顺序查找,是一种介于顺序查找和二分查找之间的算法。它将查找表分成若干个"块",然后对这些块建立一个"索引",是一种空间换时间的策略。

4.1.1 核心思想:"目录 + 内容"的查找模式

分块查找的思想非常直观,就像查书一样:

  1. 先查目录:找到目标内容所在的章节(块)。
  2. 再翻内容:进入该章节,一页一页地(顺序)查找具体内容。

在数据结构中,"目录"就是索引表 ,"内容"就是数据块

4.1.2 数据结构设计

分块查找需要对原始数据进行预处理,构建两个部分:

  1. 索引表(Index Table) :存储每个数据块的关键信息。通常,索引表中的每一项包含:
    • 该块中所有元素的最大(或最小)值。
    • 该块在原数组中的起始(或结束)地址。
  2. 数据块(Data Blocks):原始数据被划分成的若干子数组。

要求

  • 块间有序 :索引表本身是按其关键字(块内最大值)有序的。例如,第 i 块的最大值必须小于第 i+1 块的最大值。
  • 块内无序:每个数据块内部的元素可以是无序的。

下面是分块查找数据结构的示意图:

graph TD subgraph 索引表 (Index Table) direction LR I1["块1
Max: 22
Start: 0"] --> I2["块2
Max: 82
Start: 4"] --> I3["块3
Max: 100
Start: 8"] end subgraph 数据块 (Data Blocks) direction TB B1["块1: [10, 22, 15, 8]"] B2["块2: [50, 45, 82, 60]"] B3["块3: [90, 85, 100, 95]"] end subgraph 原始数据存储 (Array) A[10, 22, 15, 8, 50, 45, 82, 60, 90, 85, 100, 95] end I1 -- 指向 --> B1 I2 -- 指向 --> B2 I3 -- 指向 --> B3 B1 -- 组成 --> A B2 -- 组成 --> A B3 -- 组成 --> A style I1 fill:#cde,stroke:#333,stroke-width:2px style I2 fill:#cde,stroke:#333,stroke-width:2px style I3 fill:#cde,stroke:#333,stroke-width:2px

4.1.3 查找过程

  1. 确定块 :在索引表上进行查找(由于索引表有序,可以使用二分查找或顺序查找),找到目标 key 应该在哪个块中。
    • 例如,要查找 key = 60。在索引表中查找:60 > 22 (块1最大值),60 <= 82 (块2最大值)。因此,60 如果存在,必定在块2中。
  2. 块内查找 :定位到目标块后,在该块内部进行顺序查找。
    • 在块2 [50, 45, 82, 60] 中顺序查找 60,最终找到。

4.1.4 性能分析与适用场景

(1) 性能分析

分块查找的平均查找长度 = 查找索引表的平均长度 + 查找块内元素的平均长度。

假设有 n 个元素,分为 b 块,每块有 s 个元素( n = b × s n = b \times s n=b×s)。

  • 在索引表上用二分查找,时间复杂度为 O ( log ⁡ b ) O(\log b) O(logb)。
  • 在块内顺序查找,平均时间复杂度为 O ( s ) O(s) O(s)。
    总时间复杂度约为 O ( log ⁡ b + s ) O(\log b + s) O(logb+s)。通过调整块大小 s,可以平衡这两部分的时间。当 s ≈ n s \approx \sqrt{n} s≈n 时,总复杂度约为 O ( n ) O(\sqrt{n}) O(n )。
(2) 适用场景

分块查找适用于数据量大,但插入、删除等操作不频繁的场景。由于它允许块内无序,当有新数据插入时,只要不破坏块间的有序性(即新数据小于等于所在块的最大值),就不需要大规模移动数据,这比在整个有序数组中插入要高效。

4.1.5 代码实现与注释

java 复制代码
import java.util.ArrayList;
import java.util.List;

// 索引项结构
class IndexEntry {
    int maxValue;
    int startIndex;

    public IndexEntry(int maxValue, int startIndex) {
        this.maxValue = maxValue;
        this.startIndex = startIndex;
    }
}

public class BlockSearch {

    private int[] data; // 原始数据
    private List<IndexEntry> indexTable; // 索引表
    private int blockSize; // 块大小

    public BlockSearch(int[] data, int blockSize) {
        this.data = data;
        this.blockSize = blockSize;
        this.indexTable = new ArrayList<>();
        buildIndex();
    }

    // 构建索引表
    private void buildIndex() {
        int numBlocks = (int) Math.ceil((double) data.length / blockSize);
        for (int i = 0; i < numBlocks; i++) {
            int startIndex = i * blockSize;
            int endIndex = Math.min((i + 1) * blockSize - 1, data.length - 1);
            
            // 找到块内的最大值
            int maxVal = data[startIndex];
            for (int j = startIndex + 1; j <= endIndex; j++) {
                if (data[j] > maxVal) {
                    maxVal = data[j];
                }
            }
            indexTable.add(new IndexEntry(maxVal, startIndex));
        }
    }

    public int search(int key) {
        // 1. 在索引表中查找确定块
        int blockIndex = -1;
        for (int i = 0; i < indexTable.size(); i++) {
            if (key <= indexTable.get(i).maxValue) {
                blockIndex = i;
                break;
            }
        }

        if (blockIndex == -1) {
            return -1; // 目标值比所有块的最大值都大,不存在
        }

        // 2. 在块内进行顺序查找
        int startIndex = indexTable.get(blockIndex).startIndex;
        int endIndex = Math.min(startIndex + blockSize, data.length);
        
        for (int i = startIndex; i < endIndex; i++) {
            if (data[i] == key) {
                return i; // 找到
            }
        }

        return -1; // 未找到
    }

    public static void main(String[] args) {
        int[] data = {22, 12, 13, 8, 9, 20, 33, 42, 44, 38, 24, 48, 60, 58, 74, 49, 86, 53};
        int blockSize = 6;
        BlockSearch bs = new BlockSearch(data, blockSize);

        // 索引表将会是:
        // Block 0 (idx 0-5): max=22
        // Block 1 (idx 6-11): max=48
        // Block 2 (idx 12-17): max=86
        
        int key = 44;
        int index = bs.search(key);
        System.out.println("查找 " + key + ",索引为: " + index); // 输出 8

        key = 10;
        index = bs.search(key);
        System.out.println("查找 " + key + ",索引为: " + index); // 输出 -1
    }
}

五、三大查找算法横向对比

为了更直观地理解这几种算法的特点,我们通过一个表格进行总结:

特性维度 二分查找 (Binary Search) 插值查找 (Interpolation Search) 斐波那契查找 (Fibonacci Search) 分块查找 (Block Search)
核心思想 固定中点分割 按比例自适应分割 黄金分割点分割 索引 + 顺序查找
前提条件 数组有序 数组有序,且数据分布均匀 数组有序 块间有序,块内可无序
平均时间复杂度 O ( log ⁡ n ) O(\log n) O(logn) O ( log ⁡ ( log ⁡ n ) ) O(\log(\log n)) O(log(logn)) (数据均匀时) O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(\sqrt{n}) O(n ) (块大小为 n \sqrt{n} n 时)
最坏时间复杂度 O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(n) O(n) (数据分布极不均时) O ( log ⁡ n ) O(\log n) O(logn) O ( n ) O(\sqrt{n}) O(n )
空间复杂度 O ( 1 ) O(1) O(1) (迭代) O ( 1 ) O(1) O(1) (迭代) O ( 1 ) O(1) O(1) (不计斐波那契数列和临时数组) O ( b ) O(b) O(b) 或 O ( n ) O(\sqrt{n}) O(n ) (索引表开销)
主要优点 稳定、简单、普适性强 数据均匀时效率极高 仅用加减法,避免乘除运算 对插入删除友好,允许块内无序
主要缺点 未利用数据分布信息 依赖数据分布,最坏情况性能差 实现复杂,需要额外空间 需要额外空间存索引,性能不如二分查找

六、总结

本文在二分查找的基础上,深入探讨了三种各具特色的查找算法,现在对它们的核心价值进行总结:

  1. 插值查找 :它是二分查找的"智能版",通过数据分布的预测来加速查找。当面对数据量巨大且分布均匀的有序数据集时,它能展现出超越二分查找的卓越性能。

  2. 斐波那契查找 :它是一种利用黄金分割原理进行查找的"艺术品"。其核心优势在于避免了乘除法 ,使其在某些对算术运算敏感的硬件或环境中成为更优的选择,同时保持了 O ( log ⁡ n ) O(\log n) O(logn) 的稳定效率。

  3. 分块查找 :它是一种"空间换时间"的折中方案,通过建立索引来平衡全局查找和局部查找的效率。它最大的特点是降低了数据维护的成本,在需要频繁进行小范围增删但又不希望完全重排数据的场景下,展现出独特的实用价值。

最终,我们需要理解,算法的选择没有绝对的"最好",只有"最合适"。掌握这些二分查找的变体,能让你在面对不同数据特征和业务需求时,做出更精准、更高效的技术决策,这正是从"会用"算法到"精通"算法的关键一步。


相关推荐
Chiandra_Leong1 分钟前
Python-Pandas、Numpy
python·pandas
BoBoZz192 分钟前
ParametricObjectsDemo多种参数曲面展示及面上部分点法线展示
python·vtk·图形渲染·图形处理
ariesjzj5 分钟前
DeepSeek时代的Large-scale LLM推理
大模型·llm·deepseek·推理优化·大规模ep
苍何8 分钟前
越来越对 AI 做的 PPT 敬佩了!(附7大用法)
人工智能
苍何13 分钟前
超全Nano Banana Pro 提示词案例库来啦,小白也能轻松上手
人工智能
quikai198139 分钟前
python练习第三组
开发语言·python
阿杰学AI1 小时前
AI核心知识39——大语言模型之World Model(简洁且通俗易懂版)
人工智能·ai·语言模型·aigc·世界模型·world model·sara
智慧地球(AI·Earth)1 小时前
Vibe Coding:你被取代了吗?
人工智能
ULTRA??1 小时前
初学protobuf,C++应用例子(AI辅助)
c++·python
CHANG_THE_WORLD2 小时前
Python 字符串全面解析
开发语言·python