【数据结构】(C++数据结构)查找算法与排序算法详解

(C++数据结构)查找算法与排序算法详解

目录

  1. 有序向量的查找算法
  2. 排序算法
  3. 算法复杂度分析

1. 有序向量的查找算法

1.1 二分查找(Binary Search)

二分查找是最基本的有序向量查找算法,时间复杂度为O(log n)。

基本实现
c 复制代码
int binarySearch(int A[], int lo, int hi, int e) {
    while (lo < hi) {
        mi = (lo + hi) >> 1;  // 中间位置,右移1位相当于除以2
        if (e < A[mi]) hi = mi;           // 目标在左半区间
        else if (e > A[mi]) lo = mi + 1;  // 目标在右半区间
        else return mi;                   // 找到目标
    }
    return -1;  // 未找到
}
改进版本
c 复制代码
int binarySearchImproved(int A[], int lo, int hi, int e) {
    while (lo < hi) {
        mi = (lo + hi) >> 1;
        if (e < A[mi]) hi = mi;
        else lo = mi + 1;  // 右子区间不包含mi
    }
    return --lo;  // 返回不大于e的最大元素位置
}

关键点

  • 循环条件 lo < hi 确保区间有效
  • mi = (lo + hi) >> 1 避免溢出
  • 平均查找长度为 O(1.5 log n)

1.2 插值查找(Interpolation Search)

插值查找适用于均匀分布的有序向量,通过线性插值预测目标位置。

核心公式

m i − l o h i − l o = e − A [ l o ] A [ h i ] − A [ l o ] \frac{mi - lo}{hi - lo} = \frac{e - A[lo]}{A[hi] - A[lo]} hi−lomi−lo=A[hi]−A[lo]e−A[lo]

解得:
m i = l o + ( h i − l o ) ⋅ e − A [ l o ] A [ h i ] − A [ l o ] mi = lo + (hi - lo) \cdot \frac{e - A[lo]}{A[hi] - A[lo]} mi=lo+(hi−lo)⋅A[hi]−A[lo]e−A[lo]

实现代码
c 复制代码
int interpolationSearch(int A[], int lo, int hi, int e) {
    while (lo <= hi && e >= A[lo] && e <= A[hi]) {
        if (lo == hi) {
            if (A[lo] == e) return lo;
            return -1;
        }
        
        // 插值公式计算预测位置
        mi = lo + ((double)(hi - lo) / (A[hi] - A[lo])) * (e - A[lo]);
        
        if (A[mi] == e) return mi;
        if (A[mi] < e) lo = mi + 1;
        else hi = mi - 1;
    }
    return -1;
}
示例分析

对于数组 V = {2,3,5,7,11,13,17,19,23},查找元素 e=7
m i − 0 8 − 0 = 7 − 2 23 − 2 = 5 21 \frac{mi - 0}{8 - 0} = \frac{7 - 2}{23 - 2} = \frac{5}{21} 8−0mi−0=23−27−2=215
m i = 8 × 5 21 ≈ 1.9 → m i = 1 mi = 8 \times \frac{5}{21} \approx 1.9 \rightarrow mi = 1 mi=8×215≈1.9→mi=1
复杂度分析

  • 平均时间复杂度:O(log log n)
  • 最坏时间复杂度:O(n)

1.3 斐波那契查找(Fibonacci Search)

斐波那契查找利用斐波那契数列的特性进行分割,接近黄金比例分割。

斐波那契数列
复制代码
k:  0  1  2  3  4  5  6   7
fib:0  1  1  2  3  5  8  13
核心思想
  • 数组长度满足:n = fib(k) - 1
  • 中间位置:mi = fib(k-1) - 1
  • 左子区间长度:fib(k-2) - 1
  • 右子区间长度:fib(k-1) - 1
实现代码
c 复制代码
int fibonacciSearch(int A[], int n, int e) {
    // 初始化斐波那契数列
    int fib[20];
    fib[0] = 0; fib[1] = 1;
    for (int i = 2; i < 20; i++) {
        fib[i] = fib[i-1] + fib[i-2];
    }
    
    // 找到最小的k使得fib(k) - 1 >= n
    int k = 0;
    while (fib[k] - 1 < n) k++;
    
    int lo = 0, hi = n - 1;
    
    while (lo <= hi) {
        mi = lo + fib[k-1] - 1;
        
        if (mi > hi) {  // 处理边界情况
            mi = hi;
        }
        
        if (A[mi] == e) return mi;
        if (A[mi] < e) {
            lo = mi + 1;
            k = k - 2;  // 右子区间
        } else {
            hi = mi - 1;
            k = k - 1;  // 左子区间
        }
    }
    return -1;
}
示例分析

对于数组 V = {1,2,3,4,5,6,7},查找元素 e=1

  1. n=7, fib(6)-1=7, k=6
  2. mi = fib(5)-1 = 4, V[4]=5 > 1, hi=3, k=5
  3. mi = fib(4)-1 = 2, V[2]=3 > 1, hi=1, k=4
  4. mi = fib(3)-1 = 1, V[1]=2 > 1, hi=0, k=3
  5. mi = fib(2)-1 = 0, V[0]=1, 找到目标
    访问序列:[5,3,2,1]

2. 排序算法

2.1 冒泡排序及改进

基本冒泡排序
c 复制代码
void bubbleSort(int A[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (A[j] > A[j+1]) {
                swap(A[j], A[j+1]);
            }
        }
    }
}
改进版本(记录最后交换位置)
c 复制代码
int bubble(int A[], int lo, int hi) {
    Rank last = lo;
    while (++lo < hi) {
        if (A[lo-1] > A[lo]) {
            swap(A[lo-1], A[lo]);
            last = lo;  // 记录最后交换位置
        }
    }
    return last;
}
void improvedBubbleSort(int A[], int n) {
    int lo = 0, hi = n;
    while (lo < (hi = bubble(A, lo, hi)));
}

改进原理:记录每次循环中最后一次交换的位置,下一轮只需比较到该位置即可。

2.2 归并排序(Merge Sort)

归并排序采用分治策略,时间复杂度O(n log n),是稳定排序。

核心合并算法
c 复制代码
void merge(int A[], int lo, int mid, int hi) {
    int* B = new int[hi-lo+1];  // 临时数组
    int i = lo, j = mid+1, k = 0;
    
    // 合并两个有序子数组
    while (i <= mid && j <= hi) {
        if (A[i] <= A[j]) B[k++] = A[i++];
        else B[k++] = A[j++];
    }
    
    // 复制剩余元素
    while (i <= mid) B[k++] = A[i++];
    while (j <= hi) B[k++] = A[j++];
    
    // 复制回原数组
    for (int t = 0; t < k; t++) {
        A[lo+t] = B[t];
    }
    
    delete[] B;
}
void mergeSort(int A[], int lo, int hi) {
    if (hi - lo < 2) return;  // 递归基
    
    int mid = (lo + hi) >> 1;
    mergeSort(A, lo, mid);    // 排序左半部分
    mergeSort(A, mid, hi);    // 排序右半部分
    merge(A, lo, mid, hi);    // 合并
}

2.3 基数排序(Radix Sort)

基数排序按位数从低到高进行排序,要求每位排序时保持稳定性。

实现代码
c 复制代码
// 获取数字的第d位数字
int getDigit(int num, int d) {
    while (d-- > 1) {
        num /= 10;
    }
    return num % 10;
}
// 对数组的第d位进行计数排序
void countingSortByDigit(int A[], int n, int d) {
    const int RADIX = 10;
    int* B = new int[n];
    int count[RADIX] = {0};
    
    // 统计各数字出现次数
    for (int i = 0; i < n; i++) {
        count[getDigit(A[i], d)]++;
    }
    
    // 计算累积位置
    for (int i = 1; i < RADIX; i++) {
        count[i] += count[i-1];
    }
    
    // 从右向左填充,保证稳定性
    for (int i = n-1; i >= 0; i--) {
        int digit = getDigit(A[i], d);
        B[count[digit]-1] = A[i];
        count[digit]--;
    }
    
    // 复制回原数组
    for (int i = 0; i < n; i++) {
        A[i] = B[i];
    }
    
    delete[] B;
}
void radixSort(int A[], int n) {
    if (n <= 1) return;
    
    // 找到最大值,确定位数
    int maxVal = A[0];
    for (int i = 1; i < n; i++) {
        if (A[i] > maxVal) maxVal = A[i];
    }
    
    // 按位数从低到高排序
    for (int d = 1; maxVal / d > 0; d *= 10) {
        countingSortByDigit(A, n, d);
    }
}
示例演示

对数组 [170, 45, 75, 90, 802, 24, 2, 66] 进行基数排序:

  1. 个位排序[170, 90, 802, 2, 24, 45, 75, 66]
  2. 十位排序[802, 2, 24, 45, 66, 170, 75, 90]
  3. 百位排序[2, 24, 45, 66, 75, 90, 170, 802]

3. 算法复杂度分析

3.1 查找算法复杂度对比

算法 平均时间复杂度 最坏时间复杂度 适用场景
二分查找 O(log n) O(log n) 通用有序向量
插值查找 O(log log n) O(n) 均匀分布数据
斐波那契查找 O(log n) O(log n) 需要减少除法运算

3.2 排序算法复杂度对比

算法 时间复杂度 空间复杂度 稳定性
冒泡排序 O(n²) O(1) 稳定
归并排序 O(n log n) O(n) 稳定
基数排序 O(d·n) O(n) 稳定

3.3 黄金比例与算法优化

斐波那契数列相邻项的比值趋近于黄金比例 φ = (1+√5)/2 ≈ 1.618,这种特性使得斐波那契查找在分割区间时能够达到接近最优的效果。

总结

本文详细介绍了三种重要的查找算法(二分查找、插值查找、斐波那契查找)和三种排序算法(冒泡排序、归并排序、基数排序)的原理、实现和复杂度分析。每种算法都有其适用的场景和优缺点:

  • 查找算法:根据数据分布特性选择合适的算法
  • 排序算法 :根据稳定性要求和数据规模选择合适的方法
    理解这些基础算法的原理和实现,对于编写高效的程序和解决实际问题具有重要意义。
相关推荐
_OP_CHEN2 分钟前
【算法基础篇】(四十三)数论之费马小定理深度解析:从同余性质到乘法逆元
c++·算法·蓝桥杯·数论·acm/icpc
水月wwww5 分钟前
【算法设计】分支限界法
算法·分支限界法
茶猫_11 分钟前
C++学习记录-旧题新做-链表求和
数据结构·c++·学习·算法·leetcode·链表
龘龍龙17 分钟前
Python基础学习(十一)
python·学习·mysql
yuniko-n18 分钟前
【牛客面试 TOP 101】链表篇(一)
数据结构·算法·链表·面试·职场和发展
王老师青少年编程19 分钟前
信奥赛C++提高组csp-s之并查集(案例实践)1
数据结构·c++·并查集·csp·信奥赛·csp-s·提高组
谢娘蓝桥19 分钟前
adi sharc c/C++ 语言指令优化
开发语言·c++
郑泰科技21 分钟前
fmm(快速地图匹配)实践:Unknown toolset: vcunk的解决方案
c++·windows·交通物流
2501_9418053125 分钟前
从微服务网关到统一安全治理的互联网工程语法实践与多语言探索
前端·python·算法
源代码•宸26 分钟前
Leetcode—1161. 最大层内元素和【中等】
经验分享·算法·leetcode·golang