算法 —— 十大排序算法

文章目录

    • 1. 冒泡排序
    • 2. 选择排序
    • 3. 插入排序
    • 4. 希尔排序
    • 5. 快速排序
    • 6. 归并排序
    • 7. 堆排序
    • 8. 桶排序
    • 9. 基数排序
    • 10. 计数排序

简要总结十大排序算法

排序算法 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性 适用场景
冒泡排序 O(n²) O(n²) O(1) 稳定 小数据量
选择排序 O(n²) O(n²) O(1) 不稳定 简单实现
插入排序 O(n²) O(n²) O(1) 稳定 部分有序
希尔排序 O(n log n) O(n²) O(1) 不稳定 中等规模
快速排序 O(n log n) O(n²) O(log n) 不稳定 通用排序
归并排序 O(n log n) O(n log n) O(n) 稳定 大数据量
堆排序 O(n log n) O(n log n) O(1) 不稳定 内存受限
计数排序 O(n + k) O(n + k) O(k) 稳定 整数小范围
桶排序 O(n + k) O(n²) O(n + k) 稳定 均匀分布
基数排序 O(d(n + k)) O(d(n + k)) O(n + k) 稳定 多关键字

1. 冒泡排序

原理:通过相邻元素比较交换,使最大元素逐渐上浮到右侧
步骤

  1. 比较相邻元素,若顺序错误则交换
  2. 对每一对相邻元素重复步骤1
  3. 重复上述过程直到没有交换发生

特点

  • 稳定排序
  • 时间复杂度:最好O(n) 最坏O(n²) 平均O(n²)
  • 空间复杂度:O(1)
void bubbleSort(int arr[], int n) {
    for(int i = 0; i < n-1; ++i) {
        bool swapped = false;
        for(int j = 0; j < n-i-1; ++j) {
            if(arr[j] > arr[j+1]) {
                swap(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        if(!swapped) break; // 提前终止优化
    }
}

2. 选择排序

原理:每次从未排序序列中选择最小元素放到已排序序列末尾
步骤

  1. 在未排序序列中找到最小元素
  2. 与未排序序列首元素交换
  3. 重复上述过程直到排序完成

特点

  • 不稳定排序
  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
void selectionSort(int arr[], int n) {
    for(int i = 0; i < n-1; ++i) {
        int minIndex = i;
        for(int j = i+1; j < n; ++j)
            if(arr[j] < arr[minIndex])
                minIndex = j;
        swap(arr[i], arr[minIndex]);
    }
}

3. 插入排序

原理:将未排序元素逐个插入已排序序列的合适位置
步骤

  1. 从第一个元素开始视为已排序
  2. 取出下一个元素,在已排序序列中从后向前扫描
  3. 找到合适位置插入
  4. 重复直到所有元素插入完毕

特点

  • 稳定排序
  • 时间复杂度:最好O(n) 最坏O(n²)
  • 空间复杂度:O(1)
void insertionSort(int arr[], int n) {
    for(int i = 1; i < n; ++i) {
        int key = arr[i];
        int j = i-1;
        while(j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j];
            --j;
        }
        arr[j+1] = key;
    }
}

4. 希尔排序

原理:改进的插入排序,通过分组进行插入排序
步骤

  1. 按增量序列(如n/2, n/4,…1)分组
  2. 对每组进行插入排序
  3. 逐渐缩小增量直至1

特点

  • 不稳定排序
  • 时间复杂度:O(n log n) ~ O(n²)
  • 空间复杂度:O(1)
void shellSort(int arr[], int n) {
    for(int gap = n/2; gap > 0; gap /= 2) {
        for(int i = gap; i < n; ++i) {
            int temp = arr[i];
            int j;
            for(j = i; j >= gap && arr[j-gap] > temp; j -= gap)
                arr[j] = arr[j-gap];
            arr[j] = temp;
        }
    }
}

5. 快速排序

原理:分治法选取基准元素进行分区排序
步骤

  1. 选取基准元素(pivot)
  2. 将数组分为小于基准和大于基准的两部分
  3. 递归地对两个子数组排序

特点

  • 不稳定排序
  • 时间复杂度:平均O(n log n) 最坏O(n²)
  • 空间复杂度:O(log n)
void quickSort(int array[], int l, int r) {
    if(l >= r) return;
    
    int p = array[l + r >> 1], i = l - 1, j = r + 1;
    while(i < j) {
        while(array[++i] < p);
        while(array[--j] > p);
        if(i < j) swap(array[i], array[j]);
    }
    
    quickSort(array, l, j);
    quiclSort(array, j + 1, r);
}

6. 归并排序

原理:分治法将数组分为两半分别排序后合并
步骤

  1. 递归地将数组分成两半
  2. 对每半进行排序
  3. 合并两个已排序的数组

特点

  • 稳定排序
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(n)
void mergeSort(int array[], int l, int r) {
    if(l >= r) return;
    
    int mid = l + r >> 1;
    mergeSort(array, l, mid);
    mergeSort(array, mid + 1, r);
    
    int k = 0, i = l, j = mid + 1;
    int temp[r - l + 1];
    while(i <= mid && j <= r) {
        if(array[i] <= array[j]) temp[k++] = array[i++];
        else temp[k++] = array[j++];
    }
    while(i <= mid) temp[k++] = array[i++];
    while(j <= r) temp[k++] = array[j++];
    
    for(i = l, j = 0; i <= r; i++, j++) array[i] = temp[j];
}

7. 堆排序

原理:利用堆数据结构进行排序
步骤

  1. 构建最大堆
  2. 将堆顶元素与末尾元素交换
  3. 调整堆结构
  4. 重复步骤2-3直到排序完成

特点

  • 不稳定排序
  • 时间复杂度:O(n log n)
  • 空间复杂度:O(1)
void down(int array[], int si, int u) {
    int t = u;
    if(u * 2 + 1 < si && array[u * 2 + 1] > h[t]) t = u * 2 + 1;
    if(u * 2 + 2 < si && array[u * 2 + 2] > h[t]) t = u * 2 + 2;
    if(u != t) {
        swap(array[t], array[u]);
        down(array, si, t);
    }
}

void heapSort(int array[], int n) {
    for(int i = n / 2 - 1; ~i; i--) down(i);
    
    for(int i = n - 1; i; i--) {
        swap(array[0], array[i]);
        down(array, i, 0);
    }
}

8. 桶排序

原理:将数据分到有限数量的桶中,各桶分别排序
适用条件:均匀分布的数据

步骤

  1. 创建若干空桶
  2. 将元素分配到对应桶中
  3. 对每个非空桶排序
  4. 合并所有桶中元素
void bucketSort(vector<int> arr[], int bucketSize) {
    // 1. 确定数据范围
    int minVal = *min_element(arr.begin(), arr.end());
    int maxVal = *max_element(arr.begin(), arr.end());
    
    // 2. 计算桶的数量
    int bucketCount = (maxVal - minVal) / bucketSize + 1;
    vector<vector<int>> buckets(bucketCount);
    
    // 3. 元素分桶
    for (int num : arr) {
        int bucketIdx = (num - minVal) / bucketSize;
        buckets[bucketIdx].push_back(num);
    }
    
    // 4. 排序每个桶并合并
    int index = 0;
    for (auto& bucket : buckets) {
        sort(bucket.begin(), bucket.end()); // 使用快速排序
        for (int num : bucket) {
            arr[index++] = num;
        }
    }
}

9. 基数排序

【算法系列】基数排序-CSDN博客

10. 计数排序

十大排序算法——计数排序-CSDN博客

原理:使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。
适用条件:整数排序,数据范围较小

步骤

  1. 统计每个元素出现次数
  2. 计算前缀和确定元素位置
  3. 反向填充目标数组

特点

  • 稳定排序
  • 时间复杂度:O(n + k)
  • 空间复杂度:O(k)

参考:DeepSeek

你可能感兴趣的:(排序算法,算法,数据结构)