【数据结构】(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,这种特性使得斐波那契查找在分割区间时能够达到接近最优的效果。

总结

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

  • 查找算法:根据数据分布特性选择合适的算法
  • 排序算法 :根据稳定性要求和数据规模选择合适的方法
    理解这些基础算法的原理和实现,对于编写高效的程序和解决实际问题具有重要意义。
相关推荐
---学无止境---3 小时前
Linux中初始化根文件系统populate_rootfs的实现
1024程序员节
紫荆鱼3 小时前
设计模式-适配器模式(Adapter)
c++·设计模式·适配器模式
成长痕迹3 小时前
【Electron桌面应用完整方案】
1024程序员节
前进的李工3 小时前
LeetCode hot100:560 和为k的子数组:快速统计法
python·算法·leetcode·前缀和·哈希表
陌上明苏3 小时前
使用ssrs矩阵
1024程序员节
学编程就要猛3 小时前
数据结构初阶:Java中的ArrayList
java·开发语言·数据结构
墨利昂3 小时前
深度学习常用优化器解析
人工智能·深度学习·机器学习·1024程序员节
asdfsdgss3 小时前
PyTorch 生成式 AI(1):模型训练过拟合处理,神经网络正则化方法详解
1024程序员节
PyHaVolask3 小时前
Metasploit网络嗅探实战:从数据包捕获到协议分析的完整指南
数据包分析·metasploit·1024程序员节·流量分析·网络嗅探