十大排序算法复杂度及稳定性分析和c++实现

排序

分类

在这里插入图片描述

按排序依据原则分类(普遍流行):

在这里插入图片描述

各排序算法比较

在这里插入图片描述

插入排序

  • 基本思想:边插入边排序,保证子序列中随时都是排好序的。
  • 按插入位置方法的不同分类:1)顺序法:直接插入排序 2)二分法:折半插入排序 3)缩小增量:希尔排序
  1. 直接插入排序

原理:从第二位数据开始, 当前数(第一趟是第二位数)与前面的数依次比较,如果前面的数大于当前数,则将这个数放在当前数的位置上,当前数的下标-1,直到当前数不大于前面的某一个数为止,直到遍历至最后一位元素。

代码实现

#include<iostream>
#include<vector>
using namespace std;

void InsertSort(vector<int>& nums){
    for (int i = 1; i < nums.size(); i++) {// 从第二位开始
        if (nums[i] < nums[i - 1]) {// 若第i个元素大于第i-1个元素,则直接插入。反之,需要移动有序表后插入
            int key = nums[i];// 设置哨兵
            int j;
            for (j = i - 1; key < nums[j]; j--) {//每次都和基准key比较,最小值往前插
                nums[j + 1] = nums[j];//后移
            }
            nums[j + 1] = key;// 插入到正确位置
        }
    }
}

int main() {
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    InsertSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}
  1. 折半插入排序

原理:采用二分法在直接插入排序基础上进行查找插入

代码实现

#include<iostream>
#include<vector>
using namespace std;

void BinaryInsertSort(vector<int>& nums) {
    for (int i = 1; i < nums.size(); i++) {// 从第二位开始
        int key = nums[i];// 设置哨兵
        int low = 0, high = i - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (key < nums[mid]) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        for (int j = i - 1; j >= high + 1; j--) {
            nums[j + 1] = nums[j];// 后移
        }
        nums[high + 1] = key;// 插入到正确位置 
    }
}

int main() {
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    BinaryInsertSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}
  1. 希尔排序

原理:希尔排序是插入排序改良的算法,是插入排序的一种更高效的改进版本,也称递减增量排序,但是非稳定排序算法。希尔排序步长从大到小调整,先将整个待排序记录序列分割成为若干子序列,分别进行直接插入排序,待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。所以步长是关键,最终步长为1,做最后的排序。

代码实现

#include<iostream>
#include<vector>
using namespace std;

void ShellInsert(vector<int>& nums, int dk) {
    for (int i = dk; i < nums.size(); i++) {// 从增量步长开始
        int key = nums[i];// 设置哨兵
        int j;
        for (j = i - dk; key < nums[j]; j = j - dk) {
            nums[j + dk] = nums[j];// 后移
        }
        nums[j + dk] = key;// 插入到正确位置 
    }
}

void ShellSort(vector<int>& nums) {
	int dk = nums.size() / 2;// 步长
    while (dk >= 1) {
        ShellInsert(nums, dk);
        dk /= 2;
    }
}

int main() {
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    ShellSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

交换排序

  • 基本思想:两两交换,如果发生逆序则交换,直到所有记录都排好序为止
  • 常见的交换排序方法:1) 冒泡排序 2)快速排序
  1. 冒泡排序

原理:每趟不断将记录两两比较,并按“前小后大”或者“前大后小”规则交换。具体的说就是重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。把最小的数浮上来,或者把最大的数据沉下去。

代码实现:

#include<iostream>
#include<vector>
using namespace std;

void bubbleSort(vector<int>& nums) {// 假设nums有n个元素
    for (int m = 1; m <= nums.size() - 1; m++) {// n个记录需要总共需要n - 1趟
        for (int j = 0; j < nums.size() - m; j++) {// 每趟需要比较n - m次
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
                /*
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
                */
            }
        }
    }
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    bubbleSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}
  1. 快速排序

原理:任取一个元素(第一个)为中心点,所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子数组,对各子数组重新选择中心元素并依此规则调整,直到每个子数组的元素只剩一个。

代码实现:

#include<iostream>
#include<vector>
using namespace std;

//partition为一趟排序 寻找中心点位置
int partition(vector<int>& nums, int low, int high) {
    int key = nums[low];// 先将下限第一个元素作为中心点设为哨兵
    while (low < high) {
        while (low < high && nums[high] >= key) high--;// 右边查找 比中心点大的元素一律后放
        nums[low] = nums[high];// 比中心点小的元素一律前放
        while (low < high && nums[low] <= key) low++;// 左边查找 比中心点小的元素一律前放
        nums[high] = nums[low];// 比中心点大的元素一律后放
    }
    nums[low] = key;// 中心点元素最后插入至中心点处
    return low;// 返回中心点位置
}

void quickSort(vector<int>& nums, int low, int high) {// 快速排序
    if (low >= high) return;
    int keyloc = partition(nums, low, high);//一次排序,找中心点位置,keyloc将数据分成左右两个子数组
    quickSort(nums, low, keyloc - 1);// 对低位左数组递归快排
    quickSort(nums, keyloc + 1, high);// 对高位右数组递归快排
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    quickSort(nums, 0, nums.size() - 1);// 分别指向两端的数据
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

选择排序

  1. 简单选择排序

原理:在待排序的数据中选出最小(大)的元素放在其最终的位置。这里以从小到大排序为例,具体操作是首先通过n-1次比较,从n个数中找出最小数,将它和第一个元素交换,然后通过n-2次比较,从剩余n-1个数中找最小数,将它和第二个数交换,重复上述过程,共进行n-1趟排序,排序结束。

代码实现:

#include<iostream>
#include<vector>
using namespace std;

void selectSort(vector<int>& nums) {
    for (int i = 0; i < nums.size() - 1; i++) {// 趟数 n-1次比较
        int minIndex = i;// 记录最小索引
        for (int j = i + 1; j < nums.size(); j++) {// 每趟
            if (nums[j] < nums[minIndex]) {// 寻找最小值
                minIndex = j;// 记录最小值位置
            }
        }
        if (minIndex != i) { // 每趟遍历完发现最小值位置不在一开始记录的位置
            swap(nums[minIndex], nums[i]);// 交换最小值与一开始设置最小值的元素
            /*
            int temp = nums[minIndex];
            nums[minIndex] = nums[i];
            nums[i] = temp;
            */
        }
    }
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    selectSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}
  1. 堆排序

原理:分为两点,第一是堆排序,若在输出堆顶的最小值(最大值)后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小(大)值,如此反复,便能得到一个有序序列,这个过程称之为堆排序。第二是堆调整,以小根堆为例,输出堆顶元素之后,以堆中最后一个元素替代之,然后将根节点值与左右子树的根节点值进行比较,并与其中小者进行交换,重复上述步骤,直到叶子节点,将得到新的堆,称这个从堆顶到叶子的调整过程为“筛选”。

代码实现:

#include<iostream>
#include<vector>
using namespace std;

// 堆调整:(最大堆,有序区)。从堆顶把根卸出来放在有序区之前,再恢复堆。
void heapAdjust(vector<int>& nums, int start, int end){
    int dad = start, son = dad * 2 + 1;// 建立父节点下标和子节点下标 父节点下标从0开始
	while (son <= end) {// 若子节点在范围内才做比较
        if (son + 1 <= end && nums[son] < nums[son + 1]) son++;// 先比较两个子节点值,选择最大的
        if (nums[dad] > nums[son]) {// 如果父节点大于子节点代表调整完成,直接跳出函数
            return;
        } else {// 否则交换父子內容再继续子节点与孙节点比較
            swap(nums[dad], nums[son]);
            /*
            int temp = nums[dad];
            nums[dad] = nums[son];
            nums[son] = temp;
            */
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

// 堆排序
void heapSort(vector<int>& nums){
    // 初始建堆,从最后一个非叶子节点(父节点)开始,左半边为非叶子节点,右半边为叶子节点。
    for (int i = nums.size() / 2 - 1; i >= 0; i--) {
        heapAdjust(nums, i, nums.size() - 1);
    }
    // 取堆顶,并且调整
    for (int i = nums.size() - 1; i > 0 ; i--){
        swap(nums[0], nums[i]);// 把堆顶元素交换至后面。
        heapAdjust(nums, 0, i - 1);// 对剩下的元素进行排序
    }
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    heapSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

归并排序

原理:把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

代码实现:

#include<iostream>
#include<vector>
using namespace std;

// 将两个已经排序好的序列[low, mid]和[mid + 1, high]归并到临时数组temp中
void merge(vector<int>& nums, int low, int mid, int high) {
    vector<int> temp(high - low + 1);// 开辟临时数组
    int m = high - low;// 临时数组的最后一个元素下标
    int i = mid, j = high;// 从2个序列的末尾开始遍历
    while (i >= low && j >= mid + 1) {
        if (nums[i] > nums[j]) temp[m--] = nums[i--];// nums[i]是剩余元素的最大值
        else temp[m--] = nums[j--];// nums[j]是剩余元素的最大值
    }
    while (i >= low) temp[m--] = nums[i--];// 剩余元素直接放入
    while (j >= mid + 1) temp[m--] = nums[j--];// 剩余元素直接放入
    for(int k = low; k <= high; k++) {// 将临时数组元素全部赋给相应的原数组
        nums[k] = temp[k - low];
    }
}

void mergeSort(vector<int>& nums, int low, int high){
    if (low >= high) return;
    int mid = (low + high) / 2;
    mergeSort(nums, low, mid);// 分割
    mergeSort(nums, mid + 1, high);// 分割
    merge(nums, low, mid, high);// 排序
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    mergeSort(nums, 0, nums.size() - 1);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

计数排序

原理:当待排序的数的值都是在一定的范围内的整数时,可以用待排序的数作为计数数组的下标,统计每个数的个数,然后依次输出即可。

代码实现:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

void countSort(vector<int>& nums){
    // 找出数组中的最大值
    int maxValue = INT_MIN;
    for (int i = 0; i < nums.size(); i++) {
        maxValue = max(maxValue, nums[i]);
    }

    vector<int> count(maxValue + 1); // 开辟最大值+1的数组空间。
    for (int i = 0; i < nums.size(); i++) {
        count[nums[i]]++;// 开始计数 对应位置存放数组中元素的个数
    }
    
    int id = 0;
    // 更新原数组
    for (int i = 0; i <= maxValue; i++) {
        for (int j = 0; j < count[i]; j++) {// 个数
            nums[id++] = i;
        }
    }
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    countSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

桶排序

原理:将值为i的元素放入i号桶,最后依次把桶里的元素倒出来。具体的说是每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。

代码实现:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

void bucketSort(vector<int>& nums) {
    // 计算最大值与最小值
    int maxValue = INT_MIN;
    int minValue = INT_MAX;
    for (int i = 0; i < nums.size(); i++) {
        maxValue = max(maxValue, nums[i]);
        minValue = min(minValue, nums[i]);
    }
    
    // 计算桶的数量
    int bucketNum = (maxValue - minValue) / nums.size() + 1;
    vector<vector<int>> bucketArr(bucketNum);// 分配桶
    
    // 将每个元素放入桶
    for (int i = 0; i < nums.size(); i++) {
        int num = (nums[i] - minValue) / (nums.size());
        bucketArr[num].push_back(nums[i]);
    }
    
    // 对每个桶进行排序
    for (int i = 0; i < bucketArr.size(); i++) {
        sort(bucketArr[i].begin(), bucketArr[i].end());
    }
    
    // 将桶中的元素赋给原数组
    nums.clear(); // 清空原数组
    for (int i = 0; i < bucketArr.size(); i++) {
        for (int j = 0; j < bucketArr[i].size(); j++) {
            nums.push_back(bucketArr[i][j]);
        }
    }  
}

int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    bucketSort(nums);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}

基数排序

原理:设置若干个箱子,将关键字为k的记录放入第k个箱子(按照个位、十位、百位等的数字为关键字),然后再按序号将非空箱子中的记录收集起来,循环往复直至排序完成。

代码实现:

#include<iostream>
#include<vector>
using namespace std;
 
// 求数据的最大位数,决定排序次数
int maxbit(vector<int>& nums, int n) {
    int t = 1; // 保存最大的位数
    int m = 10;
    for (int i = 0; i < n; i++) {
        while (nums[i] >= m) {
            m *= 10;
            ++t;
        }
    }
    return t;
}

//基数排序
void radixSort(vector<int>& nums, int n) {
    int t = maxbit(nums, n);// 确定最大位数即排序次数
    vector<int> temp(n);// 创建容纳这n个数的临时数组
    vector<int> count(10); // 计数器 统计每个记录数出现的次数
    int i, j, k;
    int radix = 1;// 位数的权重
    for (i = 1; i <= t; i++) {// 进行t次排序
        for (j = 0; j < 10; j++) {
            count[j] = 0; // 每次分配前清空计数器
        }
        for (j = 0; j < n; j++) {// 统计每个桶中的记录数
            k = (nums[j] / radix) % 10; 
            count[k]++;
        }
        for (j = 1; j < 10; j++) {
            count[j] = count[j - 1] + count[j]; // 将temp中的位置依次分配给每个桶
        }
        for (j = n - 1; j >= 0; j--) {// 将所有桶中记录依次收集到temp中
            k = (nums[j] / radix) % 10;
            temp[count[k] - 1] = nums[j];
            count[k]--;
        }
        for (j = 0; j < n; j++) {// 将临时数组的内容赋给原数组
            nums[j] = temp[j];
        }
        radix = radix * 10;
    }
}
 
int main()
{
    vector<int> nums = {3, 5, 10, 16, 32, 23, 7, 4, 6};
    cout << "Before Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    radixSort(nums, 9);
    cout << "After Sort: ";
    for (int num : nums) {
        cout << num << " ";
    }
    cout << endl;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值