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

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


相关推荐
blank@l2 小时前
Python类和对象----实例属性,类属性(这是我理解类和对象最透彻的一次!!)
开发语言·python·python接口自动化基础·python类和对象·python实例属性·python类属性·类属性和实例属性的区别
超奇电子2 小时前
高斯包络调制正弦波的Python代码
开发语言·python
数智顾问2 小时前
Transformer模型:深度解析自然语言处理的革命性架构——从注意力机制到基础架构拆解
人工智能·rnn·深度学习
IT_陈寒2 小时前
React Hooks 实战:这5个自定义Hook让我开发效率提升了40%
前端·人工智能·后端
KKKlucifer2 小时前
Gartner 2025 中国网络安全成熟度曲线深度解读:AI 安全如何重构防御逻辑
人工智能·安全·web安全
合作小小程序员小小店2 小时前
桌面预测类开发,桌面%雷达,信号预测%系统开发,基于python,tk,scikit-learn机器学习算法实现,桌面预支持向量机分类算法,CSV无数据库
python·算法·机器学习·支持向量机·scikit-learn
Etherious_Young2 小时前
用u2写一个实况足球图像识别自动化脚本(2)
python·自动化
java1234_小锋2 小时前
Scikit-learn Python机器学习 - 聚类分析算法 - Agglomerative Clustering(凝聚层次聚类)
python·算法·机器学习
扑克中的黑桃A2 小时前
Python快速入门专业版(九):字符串进阶:常用方法(查找、替换、分割、大小写转换)
python