C++排序算法总结(附 完整cpp程序)

本文详细介绍了C++实现的多种排序算法,包括冒泡、选择、插入、希尔、快速、归并、堆、计数和基数排序。每种算法的特点、时间复杂度和稳定性都有所概述,适合数据结构基础扎实的程序员复习使用。同时提供了完整的可运行代码,便于测试和理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

排序算法 (C++)

写在前面

  1. 本文为自己实现的排序算法的简单总结(只有简单的思想 / 思路概括,没有详细的原理分析),主要用于复习,适合于有一定数据结构基础的coder。
  2. 持续更新中…(目前只完成了冒泡,选择,插入,希尔,快速,归并, 桶,计数;还需要完成的有) —— 目前堆也已经完成。
  3. 最后给出了一个可以运行的.cpp代码,包含所有的排序函数,可以复制后测试。

一、排序总结

算法名称特点(思想)时间复杂度空间复杂度稳定性适用范围
[冒泡](#1. 冒泡排序)O(n^2)O(1)稳定
[选择](#2. 选择排序)每轮选择一个最小的O(n^2)O(1)不稳定
[插入](#3. 插入排序)将最后一个元素插入到有序序列中O(n^2)O(1)稳定
[希尔](#4. 希尔排序)多轮插入排序,以gap为间隔,gap初始化为n / 2,每轮gap /= 2平均O(n^1.3)O(1)不稳定
[快速](#5. 快速排序)选择一个privot,让它左边比它小,右边比它大;递归O(nlog(n))O(1)不稳定
[归并](#6. 归并排序)参照数组归并的思想,递归O(nlog(n))O(n)稳定
参考完全二叉树,建立大根堆O(nlog(n))O(1)不稳定
[计数](#8. 计数排序)构建一个max - min大小的count数组(保存每个数字的个数)O(n + k)O(k)稳定适合范围小,但是数据量大的场景。
[桶(基数)](#9. 基数排序(桶))按照个位,十位,百位…排序O (nlog®m)稳定

0. 公共函数

一个swap函数,一个print(打印数组)函数

// 公共函数,交换+打印数组
void swap(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}
void print_arr(vector<int>& arr) {
    for (auto e : arr) {
        cout << e << " ";
    }
    cout << endl;
}

1. 冒泡排序

void BubbleSort() {
    for (int i = 0; i < n; ++i) {
		for (int j = 0; j < n - i - 1; ++j) {
			if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]);
		}
	}
}

2. 选择排序

每轮选择最小的

// 选择排序 (每次选择最小值)
int FindMinPos(vector<int>& arr, int start_pos) {
    int min_pos = start_pos, min_value = arr[start_pos];
    int n = arr.size();
    for (int i = start_pos + 1; i < n; ++i) {
        if (arr[i] < min_value) {
            min_pos = i;
            min_value = arr[i];
        }
    }
    return min_pos;
}
void SelectSort(vector<int> arr) {
    int n = arr.size();
    for (int i = 0; i < n; ++i) {
        int min_pos = FindMinPos(arr, i);
        if (min_pos != i) {
            swap(arr[min_pos], arr[i]);
        }
    }
    cout << "select sort: ";
    print_arr(arr);
}

3. 插入排序

每轮将最后一个元素插入到有序序列中

// 插入排序(将无序序列插入到有序序列中)
// 每次循环中,最后一个是无序的,前面的都是有序的,只需要移动这个无序元素就行
void InsertSort(vector<int> arr) {
    int n = arr.size();
    for (int i = 1; i < n; ++i) {
        for (int j = i; j > 0; --j) {
            if (arr[j] < arr[j - 1]) swap(arr[j], arr[j - 1]);
        }
    }
    cout << "insert sort: ";
    print_arr(arr);
}

4. 希尔排序

多次选择排序

// 希尔排序
// 先分组,组内排序,然后分组的gap /= 2
// 其实是多轮插入排序,只不过交换的步长变成gap
void ShellSort(vector<int> arr) {
    int n = arr.size();
    // gap为步长InsertSort()几乎完全一样,只不过用gap替换了原来的1 (0, 即1 - 1,替换为gap - 1)
    for (int gap = n / 2; gap >= 1; gap /= 2) {
        // 这里的代码和
        for (int i = gap; i < n; ++i) {
            for (int j = i; j > gap - 1; j -= gap) {
                if (arr[j] < arr[j - gap]) swap(arr[j], arr[j - gap]);
            }
        }
    }
    cout << "shell sort:  ";
    print_arr(arr);
}

5. 快速排序

每次选择一个数字,让它左边比它小,右边比它大(递归)

// 快速排序
// 每次找到一个基准,分成两份,让左边的都比它小,右边都比它大
int Partition(vector<int> &arr, int low, int high) {
    int pivot_value = arr[low];
    while (low < high) {
        while (low < high && arr[high] >= pivot_value) --high;
        arr[low] = arr[high];
        while(low < high && arr[low] <= pivot_value) ++low;
        arr[high] = arr[low];
    }
    arr[low] = pivot_value;
    return low;
}

void QuickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pivot_pos = Partition(arr, low, high);
        QuickSort(arr, low, pivot_pos - 1);
        QuickSort(arr, pivot_pos + 1, high);
    }
    // cout << "quick sort:  ";
    // print_arr(arr);
}

// 为了不改变原来nums数组,这里我建立一个函数调用QuickSort
void call_QuickSort(vector<int> arr) {
    QuickSort(arr, 0, arr.size() - 1);
    cout << "quick sort:  ";
    print_arr(arr);
}

6. 归并排序

数组归并的思想,递归

注意这里的vec赋值操作vector<int> aux_arr(arr.begin(), arr.end());,并不是引用,而是新建后赋值,详情见C++ vector的赋初值是引用了原来的vector还是新建vector?

// 归并排序
// 2路归并:先22排列有序,然后44,然后88,知道全局有序
void Merge(vector<int>& arr, int low, int mid, int high) {
    // 临时数组(注意这里的赋值操作,并不是引用,而是新建后赋值)
    vector<int> aux_arr(arr.begin(), arr.end());

    int i = low;
    int j = mid + 1;

    // 经典的归并数组(两个需要归并的有序数组分别为aux_arr的low ~ mid和aux_arr的mid+1 ~ high,归并后的数组为arr的low ~ high)
    for (int i = low, k = low, j = mid + 1; i <= mid && j <= high; ++k) {
        if (aux_arr[i] <= aux_arr[j]) {
            arr[k] = aux_arr[i++];
        } else {
            arr[k] = aux_arr[j++];
        }

        // 一个走完,接下来就是直接复制
        while (i <= mid) arr[k] = aux_arr[i++];
        while (j <= high) arr[k] = aux_arr[j++];
    }

}
void MergeSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int mid = (low + high) / 2;
        MergeSort(arr, low, mid);
        MergeSort(arr, mid + 1, high);
        Merge(arr, low, mid, high);
    }
}
void call_MergeSort(vector<int> arr) {
    MergeSort(arr, 0, arr.size() - 1);
}

7. 堆排序

堆排序的思想是将排序后的数组中,看作是完全二叉树——根据完全二叉树的性质,arr[i *2]和arr[i * 2 + 1]是arr[i]的字节点。

大根堆:arr[i] >= arr[i *2] && arr[i] >= arr[i * 2 + 1]. (1 <= i <= n /2 )

小根堆:arr[i] <= arr[i *2] && arr[i] <= arr[i * 2 + 1]. (1 <= i <= n /2 )

注意,这里i是从1开始的,所以我们在建堆的时候,i应该从1开始,当然了,arr[0]也不是被浪费了,它可以被用作哨兵元素(或者简单来说就是一个tmp值),用来暂存元素。

建立大根堆的过程如下(来自王道数据结构):

image-20220921173608866

**建立大根堆的过程:其实就是对每个根节点,比对它和字节点的大小,把更大的放在root。**但是修改了底层的根节点,可能会影响上层的根节点,因此要对根节点自底向上遍历。

建立大根堆代码实现如下⬇️(自底向上遍历所有的root,对于每个root自上而下进行调整),建立大根堆的时间复杂度和树高有关,但是可以证明其(平均 / 最坏)时间复杂度为O(n)

//函数adjust_down将元素 k向下进行调整
void adjust_down(vector<int> &arr, int k, int len) {
    arr[0] = arr[k];                                    //arr[0] 暂存  

    for (int i = 2 * k; i <= len; i *= 2) {             //沿 key 较大的子结点向下筛选
        if (i < len && arr[i] < arr[i + 1]) ++i;        //取 key较大的子结点的下标

        if (arr[0] >= arr[i]) break;                    //筛选结束
        else {
            arr[k] = arr[i];                            //将 arr[i)调整到双亲结点上
            k = i;                                      //修改 k值,以便继续向下筛选
        }
    }                                                   // end for
    arr[k] = arr[0];                                    //被筛选结点的值放入最终位置
}

void build_max_heap(vector<int> &arr, int len) {
    for (int i = len / 2; i > 0; --i)                   //从i = n/2 ~ 1,反复调整堆
        adjust_down(arr, i, len);
}

堆排序的思路:建立堆,然后每次删除一个元素,调整堆。

堆排序的代码⬇️(最好 / 最坏 / 平均)时间复杂度 = O(nlog2n)

void print_heap(vector<int> &arr) {
    int n = arr.size();
    for(int i = 1; i < n; ++i) {                        //第一个元素是哨兵,这里就不输出了
        if (i != n - 1) cout << arr[i] << ", ";
        else cout << arr[i] << endl;
    }
}

//函数adjust_down将元素 k向下进行调整
void adjust_down(vector<int> &arr, int k, int len) {
    arr[0] = arr[k];                                    //arr[0] 暂存  

    for (int i = 2 * k; i <= len; i *= 2) {             //沿 key 较大的子结点向下筛选
        if (i < len && arr[i] < arr[i + 1]) ++i;        //取 key较大的子结点的下标

        if (arr[0] >= arr[i]) break;                    //筛选结束
        else {
            arr[k] = arr[i];                            //将 arr[i)调整到双亲结点上
            k = i;                                      //修改 k值,以便继续向下筛选
        }
    }                                                   // end for
    arr[k] = arr[0];                                    //被筛选结点的值放入最终位置
}

void build_max_heap(vector<int> &arr, int len) {
    for (int i = len / 2; i > 0; --i)                   //从i = n/2 ~ 1,反复调整堆
        adjust_down(arr, i, len);
}

void heap_sort(vector<int> &arr) {
    build_max_heap(arr, arr.size());
    cout << "build heap: ";
    print_heap(arr);
    int n = arr.size();
    cout << "heap sort result: ";
    for (int i = n; i > 1; --i) {                    //n-1 趟的交换和建堆过程 
        cout << arr[1] << ", ";                      //每伦输出一个堆顶元素
        swap(arr[i], arr[1]);                        //输出堆顶元素后,将堆顶和堆底元素交换
        adjust_down(arr, 1, i - 1);                  //整理,把剩余的 i-1 个元素整理成堆(认为堆中不再包含上一轮换到底部的最大值)
    }
    cout << endl;
}

对堆进行删除 / 插入操作,可以用向上调整来解决⬇️

// 向上调整(删除/插入元素)
void adjust_up(vector<int> &arr, int k) {               //k为向上调整的节点,同时也是堆的元素的个数
    arr[0] = arr[k];
    int i = k / 2;                                      //若节点值大于双亲节点,则将双亲节点向下调,并继续向上比较
    while (i > 0 && arr[i] < arr[0]) {                  //循环跳出节点
        arr[k] = arr[i];                                //双亲节点下调
        k = i;              
        i = k / 2;                                      //继续向上比较
    }                                                   //end while
    arr[k] = arr[0];                                    //复制到最终位置
}

堆排序的完整代码⬇️

#include <bits/stdc++.h>

using namespace std;


void print_heap(vector<int> &arr) {
    int n = arr.size();
    for(int i = 1; i < n; ++i) {                        //第一个元素是哨兵,这里就不输出了
        if (i != n - 1) cout << arr[i] << ", ";
        else cout << arr[i] << endl;
    }
}

//函数adjust_down将元素 k向下进行调整
void adjust_down(vector<int> &arr, int k, int len) {
    arr[0] = arr[k];                                    //arr[0] 暂存  

    for (int i = 2 * k; i <= len; i *= 2) {             //沿 key 较大的子结点向下筛选
        if (i < len && arr[i] < arr[i + 1]) ++i;        //取 key较大的子结点的下标

        if (arr[0] >= arr[i]) break;                    //筛选结束
        else {
            arr[k] = arr[i];                            //将 arr[i)调整到双亲结点上
            k = i;                                      //修改 k值,以便继续向下筛选
        }
    }                                                   // end for
    arr[k] = arr[0];                                    //被筛选结点的值放入最终位置
}

void build_max_heap(vector<int> &arr, int len) {
    for (int i = len / 2; i > 0; --i)                   //从i = n/2 ~ 1,反复调整堆
        adjust_down(arr, i, len);
}

void heap_sort(vector<int> &arr) {
    build_max_heap(arr, arr.size());
    cout << "build heap: ";
    print_heap(arr);
    int n = arr.size();
    cout << "heap sort result: ";
    for (int i = n; i > 1; --i) {                    //n-1 趟的交换和建堆过程 
        cout << arr[1] << ", ";                      //每伦输出一个堆顶元素
        swap(arr[i], arr[1]);                        //输出堆顶元素后,将堆顶和堆底元素交换
        adjust_down(arr, 1, i - 1);                  //整理,把剩余的 i-1 个元素整理成堆(认为堆中不再包含上一轮换到底部的最大值)
    }
    cout << endl;
}


void call_heap_sort() {
    vector<int> A = {0, 53, 17, 78, 9, 45, 65, 87, 32}; // 注意0是哨兵元素,并不参与排序和输出
    heap_sort(A);
}

// 向上调整(删除/插入元素)
void adjust_up(vector<int> &arr, int k) {               //k为向上调整的节点,同时也是堆的元素的个数
    arr[0] = arr[k];
    int i = k / 2;                                      //若节点值大于双亲节点,则将双亲节点向下调,并继续向上比较
    while (i > 0 && arr[i] < arr[0]) {                  //循环跳出节点
        arr[k] = arr[i];                                //双亲节点下调
        k = i;              
        i = k / 2;                                      //继续向上比较
    }                                                   //end while
    arr[k] = arr[0];                                    //复制到最终位置
}

int main(){
    call_heap_sort();
    return 0;
}

8. 记数排序

经典例子:100万个学生,满分500分,输入一个分数,输出对应排名 —— 适合范围小,但是数据量大的场景。
创建一个max - min 大小的count数组,存储每个数字出现的个数。

时间复杂度O(n + k), 空间复杂度O(k),(其中k = max - min) 稳定。

——缺点

  • 只能做整数
  • max - min差距大的时候,空间复杂度太高
// 计数排序,创建一个max - min 大小的count数组,存储每个数字出现的个数
// find_min_and_max_for_count_sort:找到最大最小值
void find_min_and_max_for_count_sort(vector<int> &arr, int &min, int &max) {
    int n = arr.size();

    for (int i = 0; i < n; ++i) {
        if (arr[i] > max) max = arr[i];
        if (arr[i] < min) min = arr[i];
    }
}
void CountSort(vector<int> arr) {
    int min_num = INT32_MAX, max_num = INT32_MIN;
    find_min_and_max_for_count_sort(arr, min_num, max_num);
    
    vector<int> cnt(max_num - min_num + 1, 0);
    int n = arr.size();

    // 填充cnt数组
    for (int i = 0; i < n; ++i) {
        ++cnt[arr[i] - min_num];
    }

    // 遍历cnt数组,重新赋值给arr
    int arr_index = 0;
    for (int i = 0; i < max_num - min_num + 1; ++i) {
        while (cnt[i] > 0) {
            arr[arr_index++] = i + min_num;
            --cnt[i];
        }
    }

    cout << "count sort: ";
    print_arr(arr);
} 

9. 基数排序(桶)

思路很简单,就是创建10个桶,先排个位数(个位数相同的放到一个桶中),然后收集起来,再排十位数,再排百位数…

时间复杂度:;空间复杂度;稳定性:稳定。

——当元素取值范围较大,但元素个数较少时可以利用基数排序

// 基数(桶)排序 radix sort / bucket sort
// bucket sort的辅助函数:求bucket sort的最大轮数(即最高位有多少位)
int bucket_sort_max_round(vector<int> &arr) {
    int max_r = 0;
    int n = arr.size();
    int tmp = arr[0];
    for (int i = 0; i < n; ++i) {
        if (arr[i] > tmp) tmp = arr[i];
    }

    while (tmp) {
        tmp /= 10;
        ++max_r;
    }

    return max_r;
}
void BucketSort(vector<int> arr) {
    int r = bucket_sort_max_round(arr); // 需要的轮数
    int n = arr.size();
    vector<vector<int>> bucket(10, vector<int>(n, 0)); // 10个桶子
    vector<int> bucket_size(10, 0); // 记录每个桶中有多少元素


    for (int i = 0; i < r; ++i) {
        // 每轮开始的时候每个桶的计数清零 (这里不必对桶进行清零,因为保存了桶中当前个数,并且每轮都会覆盖,所以不需要清零)
        for (int k = 0; k < 10; ++k) {
            bucket_size[k] = 0;
        }

        // 放入当前桶中
        for (int j = 0; j < n; ++j) {
            int cur_radix = (arr[j] % (int)pow(10, i + 1)) / (int)pow(10, i);
            bucket[cur_radix][bucket_size[cur_radix]] = arr[j];
            ++bucket_size[cur_radix];
        }

        // 一轮结束 —— 写回到arr中
        int arr_index = 0;
        for (int k = 0; k < 10; ++k) { 
            for (int l = 0; l < bucket_size[k]; ++l) {
                arr[arr_index++] = bucket[k][l];
            }
        }

    }

    cout << "radix (bucket) sort: ";
    print_arr(arr);
}

二、可运行的c++程序(包含以上所有排序的实现)

1. Linux下运行方法

复制以下命令到sort.cpp,保存。然后运行以下命令可以编译运行。

$ g++ -o sort sort.cpp 
$ ./sort

2. 代码如下

// g++ -o sort sort.cpp 
#include <bits/stdc++.h>

using namespace std;

void swap(int &a, int &b);
void print_arr(vector<int>& arr);

// 冒泡
void BubbleSort(vector<int> arr);

// 选择
int FindMinPos(vector<int>& arr, int start_pos);
void SelectSort(vector<int> arr);

// 插入
void InsertSort(vector<int> arr);

// 希尔
void ShellSort(vector<int> arr);

// 快速排序
int Partition(vector<int> &arr, int low, int high);
void QuickSort(vector<int>& arr, int low, int high);
void call_QuickSort(vector<int> arr);

// 归并排序
void Merge(vector<int>& arr, int low, int mid, int high);
void MergeSort(vector<int>& arr, int low, int high);
void call_MergeSort(vector<int> arr);

// 桶排序
int bucket_sort_max_round(vector<int> &arr);
void BucketSort(vector<int> arr);

// 计数排序
void find_min_and_max_for_count_sort(vector<int> &arr, int &min, int &max);
void CountSort(vector<int> arr);

// 堆排序
void print_heap(vector<int> &arr);
void adjust_down(vector<int> &arr, int k, int len);
void adjust_up(vector<int> &arr, int k);
void build_max_heap(vector<int> &arr, int len);
void call_heap_sort(vector<int> &arr);



int main() {
    vector<int> nums = {9, 6, 11, 3, 5, 4, 2, 1, 12, 0};
    cout << "original array: ";
    print_arr(nums);

    BubbleSort(nums);
    SelectSort(nums);
    InsertSort(nums);
    ShellSort(nums);
    call_QuickSort(nums);
    call_MergeSort(nums);
    BucketSort(nums);
    CountSort(nums);
    call_heap_sort(nums);
    exit(0);
}


// 公共函数,交换+打印数组
void swap(int &a, int &b) {
    int tmp = a;
    a = b;
    b = tmp;
}
void print_arr(vector<int>& arr) {
    for (auto e : arr) {
        cout << e << " ";
    }
    cout << endl;
}


// 冒泡排序
void BubbleSort(vector<int> arr) {
    int n = arr.size();
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n - i - 1; ++j) {
            if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]);
        }
    }
    cout << "bubble sort: ";
    print_arr(arr);
}

// 选择排序 (每次选择最小值)
int FindMinPos(vector<int>& arr, int start_pos) {
    int min_pos = start_pos, min_value = arr[start_pos];
    int n = arr.size();
    for (int i = start_pos + 1; i < n; ++i) {
        if (arr[i] < min_value) {
            min_pos = i;
            min_value = arr[i];
        }
    }
    return min_pos;
}
void SelectSort(vector<int> arr) {
    int n = arr.size();
    for (int i = 0; i < n; ++i) {
        int min_pos = FindMinPos(arr, i);
        if (min_pos != i) {
            swap(arr[min_pos], arr[i]);
        }
    }
    cout << "select sort: ";
    print_arr(arr);
}


// 插入排序(将无序序列插入到有序序列中)
// 每次循环中,最后一个是无序的,前面的都是有序的,只需要移动这个无序元素就行
void InsertSort(vector<int> arr) {
    int n = arr.size();
    for (int i = 1; i < n; ++i) {
        for (int j = i; j > 0; --j) {
            if (arr[j] < arr[j - 1]) swap(arr[j], arr[j - 1]);
        }
    }
    cout << "insert sort: ";
    print_arr(arr);
}

// 希尔排序
// 先分组,组内排序,然后分组的gap /= 2
// 其实是多轮插入排序,只不过交换的步长变成gap
void ShellSort(vector<int> arr) {
    int n = arr.size();
    // gap为步长InsertSort()几乎完全一样,只不过用gap替换了原来的1 (0, 即1 - 1,替换为gap - 1)
    for (int gap = n / 2; gap >= 1; gap /= 2) {
        for (int i = gap; i < n; ++i) {
            for (int j = i; j > gap - 1; j -= gap) {
                if (arr[j] < arr[j - gap]) swap(arr[j], arr[j - gap]);
            }
        }
    }
    cout << "shell sort:  ";
    print_arr(arr);
}


// 快速排序
// 每次找到一个基准,分成两份,让左边的都比它小,右边都比它大
int Partition(vector<int> &arr, int low, int high) {
    int pivot_value = arr[low];
    while (low < high) {
        while (low < high && arr[high] >= pivot_value) --high;
        arr[low] = arr[high];
        while(low < high && arr[low] <= pivot_value) ++low;
        arr[high] = arr[low];
    }
    arr[low] = pivot_value;
    return low;
}

void QuickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pivot_pos = Partition(arr, low, high);
        QuickSort(arr, low, pivot_pos - 1);
        QuickSort(arr, pivot_pos + 1, high);
    }
    // cout << "quick sort:  ";
    // print_arr(arr);
}

// 为了不改变原来nums数组,这里我建立一个函数调用QuickSort
void call_QuickSort(vector<int> arr) {
    QuickSort(arr, 0, arr.size() - 1);
    cout << "quick sort:  ";
    print_arr(arr);
}


// 归并排序
// 2路归并:先22排列有序,然后44,然后88,知道全局有序
void Merge(vector<int>& arr, int low, int mid, int high) {
    // 临时数组(注意这里的赋值操作,并不是引用,而是新建后赋值)
    // 详情见[C++ vector的赋初值是引用了原来的vector还是新建vector?](https://blog.youkuaiyun.com/ahundredmile/article/details/125380441?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165719768416781818789265%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=165719768416781818789265&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-125380441-null-null.185^v2^control&utm_term=vec&spm=1018.2226.3001.4450)
    vector<int> aux_arr(arr.begin(), arr.end());

    int i = low;
    int j = mid + 1;

    // 经典的归并数组(两个需要归并的有序数组分别为aux_arr的low ~ mid和aux_arr的mid+1 ~ high,归并后的数组为arr的low ~ high)
    for (int i = low, k = low, j = mid + 1; i <= mid && j <= high; ++k) {
        if (aux_arr[i] <= aux_arr[j]) {
            arr[k] = aux_arr[i++];
        } else {
            arr[k] = aux_arr[j++];
        }

        // 一个走完,接下来就是直接复制
        while (i <= mid) arr[k] = aux_arr[i++];
        while (j <= high) arr[k] = aux_arr[j++];
    }

}
void MergeSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int mid = (low + high) / 2;
        MergeSort(arr, low, mid);
        MergeSort(arr, mid + 1, high);
        Merge(arr, low, mid, high);
    }
}
void call_MergeSort(vector<int> arr) {
    MergeSort(arr, 0, arr.size() - 1);
}

// 堆排序
void HeapSort() {

}

// 计数

// 基数(桶)排序 radix sort / bucket sort
// bucket sort的辅助函数:求bucket sort的最大轮数(即最高位有多少位)
int bucket_sort_max_round(vector<int> &arr) {
    int max_r = 0;
    int n = arr.size();
    int tmp = arr[0];
    for (int i = 0; i < n; ++i) {
        if (arr[i] > tmp) tmp = arr[i];
    }

    while (tmp) {
        tmp /= 10;
        ++max_r;
    }

    return max_r;
}
void BucketSort(vector<int> arr) {
    int r = bucket_sort_max_round(arr); // 需要的轮数
    int n = arr.size();
    vector<vector<int>> bucket(10, vector<int>(n, 0)); // 10个桶子
    vector<int> bucket_size(10, 0); // 记录每个桶中有多少元素


    for (int i = 0; i < r; ++i) {
        // 每轮开始的时候每个桶的计数清零 (这里不必对桶进行清零,因为保存了桶中当前个数,并且每轮都会覆盖,所以不需要清零)
        for (int k = 0; k < 10; ++k) {
            bucket_size[k] = 0;
        }

        // 放入当前桶中
        for (int j = 0; j < n; ++j) {
            int cur_radix = (arr[j] % (int)pow(10, i + 1)) / (int)pow(10, i);
            bucket[cur_radix][bucket_size[cur_radix]] = arr[j];
            ++bucket_size[cur_radix];
        }

        // 一轮结束 —— 写回到arr中
        int arr_index = 0;
        for (int k = 0; k < 10; ++k) { 
            for (int l = 0; l < bucket_size[k]; ++l) {
                arr[arr_index++] = bucket[k][l];
            }
        }

    }

    cout << "radix (bucket) sort: ";
    print_arr(arr);
}


// 计数排序,创建一个max - min 大小的count数组,存储每个数字出现的个数
// find_min_and_max_for_count_sort:找到最大最小值
void find_min_and_max_for_count_sort(vector<int> &arr, int &min, int &max) {
    int n = arr.size();

    for (int i = 0; i < n; ++i) {
        if (arr[i] > max) max = arr[i];
        if (arr[i] < min) min = arr[i];
    }
}
void CountSort(vector<int> arr) {
    int min_num = INT32_MAX, max_num = INT32_MIN;
    find_min_and_max_for_count_sort(arr, min_num, max_num);
    
    vector<int> cnt(max_num - min_num + 1, 0);
    int n = arr.size();

    // 填充cnt数组
    for (int i = 0; i < n; ++i) {
        ++cnt[arr[i] - min_num];
    }

    // 遍历cnt数组,重新赋值给arr
    int arr_index = 0;
    for (int i = 0; i < max_num - min_num + 1; ++i) {
        while (cnt[i] > 0) {
            arr[arr_index++] = i + min_num;
            --cnt[i];
        }
    }

    cout << "count sort: ";
    print_arr(arr);
} 


// 堆排序
void print_heap(vector<int> &arr) {
    int n = arr.size();
    for(int i = 1; i < n; ++i) {                        //第一个元素是哨兵,这里就不输出了
        if (i != n - 1) cout << arr[i] << ", ";
        else cout << arr[i] << endl;
    }
}

//函数adjust_down将元素 k向下进行调整
void adjust_down(vector<int> &arr, int k, int len) {
    arr[0] = arr[k];                                    //arr[0] 暂存  

    for (int i = 2 * k; i <= len; i *= 2) {             //沿 key 较大的子结点向下筛选
        if (i < len && arr[i] < arr[i + 1]) ++i;        //取 key较大的子结点的下标

        if (arr[0] >= arr[i]) break;                    //筛选结束
        else {
            arr[k] = arr[i];                            //将 arr[i)调整到双亲结点上
            k = i;                                      //修改 k值,以便继续向下筛选
        }
    }                                                   // end for
    arr[k] = arr[0];                                    //被筛选结点的值放入最终位置
}

void build_max_heap(vector<int> &arr, int len) {
    for (int i = len / 2; i > 0; --i)                   //从i = n/2 ~ 1,反复调整堆
        adjust_down(arr, i, len);
}

void heap_sort(vector<int> &arr) {
    build_max_heap(arr, arr.size());
    cout << "build heap: ";
    print_heap(arr);
    int n = arr.size();
    cout << "heap sort result: ";
    for (int i = n; i > 1; --i) {                    //n-1 趟的交换和建堆过程 
        cout << arr[1] << ", ";                      //每伦输出一个堆顶元素
        swap(arr[i], arr[1]);                        //输出堆顶元素后,将堆顶和堆底元素交换
        adjust_down(arr, 1, i - 1);                  //整理,把剩余的 i-1 个元素整理成堆(认为堆中不再包含上一轮换到底部的最大值)
    }
    cout << endl;
}




// 向上调整(删除/插入元素)
void adjust_up(vector<int> &arr, int k) {               //k为向上调整的节点,同时也是堆的元素的个数
    arr[0] = arr[k];
    int i = k / 2;                                      //若节点值大于双亲节点,则将双亲节点向下调,并继续向上比较
    while (i > 0 && arr[i] < arr[0]) {                  //循环跳出节点
        arr[k] = arr[i];                                //双亲节点下调
        k = i;              
        i = k / 2;                                      //继续向上比较
    }                                                   //end while
    arr[k] = arr[0];                                    //复制到最终位置
}

void call_heap_sort(vector<int>& arr) {
    arr.insert(arr.begin(), 0);  // 注意0是哨兵元素,并不参与排序和输出
    heap_sort(arr);
}

运行结果

levi@LEVI1:~/code$ g++ -o sort sort.cpp 
levi@LEVI1:~/code$ ./sort 
original array: 9 6 11 3 5 4 2 1 12 0 
bubble sort: 0 1 2 3 4 5 6 9 11 12 
select sort: 0 1 2 3 4 5 6 9 11 12 
insert sort: 0 1 2 3 4 5 6 9 11 12 
shell sort:  0 1 2 3 4 5 6 9 11 12 
quick sort:  0 1 2 3 4 5 6 9 11 12 
radix (bucket) sort: 0 1 2 3 4 5 6 9 11 12 
count sort: 0 1 2 3 4 5 6 9 11 12 

3. 代码说明

以上代码可能有遗漏之处,欢迎指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值