十大常见排序算法

目录

算法分类

1、冒泡排序(稳定)

1.1、思路描述

1.2、代码实现

1.3、时间空间复杂度

2、快速排序(不稳定)

2.1、思路描述

2.2、代码实现

2.3、时间空间复杂度

3、插入排序(稳定)

3.1、思路描述

3.2、代码

3.3、时间空间复杂度

4、希尔排序

4.1、思路描述

4.2、代码

4.3、时间空间复杂度

5、选择排序(不稳定)

5.1、思路描述

5.2、代码

5.3、时间空间复杂度

6、堆排序

6.1、思路描述

6.2、代码

6.3、时间空间复杂度

7、归并排序(稳定)

7.1、思路描述

7.2、代码

7.3、时间空间复杂度

8、计数排序(稳定)

8.1、思路描述

8.2、代码

8.3、时间空间复杂度

9、桶排序(稳定)

9.1、思路描述

9.2、代码

9.3、时间空间复杂度

10、基数排序(稳定)

10.1、思路描述

10.2、代码

10.3、时间空间复杂度

排序算法总结


算法分类

常见排序算法可以分为两大类:

        1、比较类排序

                通过比较来决定元素间相对次序,由于时间复杂度不能突破O(n * log n),

                也称非线性时间比较类排序

        2、非比较类排序

                不通过比较来决定元素间的相对次序,可以突破基于比较类排序的时间下界,

                以线性时间运行,也称为线性时间非比较类排序。

1、冒泡排序(稳定)

1.1、思路描述

每一趟只能将一个数归位:每次循环比较前后两个元素大小,如果前者大于后者,则两者进行交换。结果是每次循环中最大的元素替换到末尾,经过 n - 1趟最终得到一个有序集合。

1.2、代码实现

//实现从小到大的排序
void bubble_sort(vector<int>& vec){
    for(int i = 0; i < vec.size() - 1; ++i){
        bool flag = false;
        for(int j = 0; j < n - 1 - i; ++j){
            if(vec[j] > vec[j + 1]){
                int t = vec[j];
                vec[j] = vec[j + 1];
                vec[j + 1] = t;
                flag = true;
            }
        }
        if(!flag)
            break;
    }
}

1.3、时间空间复杂度

最好情况,数组已经是排好序的状态,时间复杂度为O(n);

最坏情况,数组是从大到小的情况,时间复杂度为O(n^2);

平均情况,数组是无序的,时间复杂度为O(n^2);

空间复杂度为O(1)。

2、快速排序(不稳定)

2.1、思路描述

在一个无序数组中取一个数key,每一趟排序实现:key左边的数都小于它,右边的数都大于它;然后左右两个区间重复这个过程,直到各个区间只有一个数。

2.2、代码实现

void quick_sort(vector<int>& vec, int left, int right){
    if(left > right)
        return ;
    int i = left, j = right;
    int temp = vec[left];
    while(i != j){
        while(vec[j] >= temp && i < j){
            j--;
        }
        while(vec[i] <= temp && i < j){
            i++;
        }
        if(i < j){
            swap(vec[i], vec[j]);
        }
    }
    swap(vec[left], vec[i]);
    quick_sort(vec, left, i - 1);
    quick_sort(vec, i + 1, right);
}

2.3、时间空间复杂度

最好情况,每次把数组都平分为两个部分,时间复杂度为O(n*log n);

最坏情况,每次只有左半边或右半边,时间复杂度为O(n^2);

平均情况,时间复杂度为O(n*log n);

空间复杂度为O(n * log n)。

3、插入排序(稳定)

3.1、思路描述

从第一个元素开始,该元素可以认为已经被排序;取出下一个元素,在已经排序的元素序列中从后往前扫描;如果新元素小于扫描到的元素,则将该元素移动到前一个位置(重复该步骤直到找到正确位置)。重复前面的步骤。

3.2、代码

void insert_sort(vector<int>& vec){
    for(int i = 0; i < vec.size(); ++i){
        int temp = vec[i];
        int j = i;
        while(vec[j - 1] > vec[j] && j > 0){
            vec[j] = vec[j - 1];
            j--;
        }
        vec[j] = temp;
    }
}

3.3、时间空间复杂度

最好情况,数组是正序的,时间复杂度是O(n);

最坏情况,数组是倒序的,时间复杂度是O(n^2);

平均情况,时间复杂度为O(n ^ 2);

空间复杂度为O(1).

4、希尔排序(不稳定)

4.1、思路描述

根据间隔d,把间隔为d的元素都划分到一个组内,先让组内有序,刚开始 d = n / 2,接着d = n / 4,让d一直缩小,当d = 1时,此时数组中任意间隔为1的元素都有序,此时数组就是有序的。

4.2、代码

void shell_sort(vector<int>& vec) {
    int n = vec.size();
    for (int d = n / 2; d > 0; d /= 2) {   
        for (int i = d; i < n; ++i) {
            int temp = vec[i];
            int j = i - d;
            while (j >= 0 && temp < vec[j]) {
                vec[j + d] = vec[j];
                j -= d;
            }
            vec[j + d] = temp;
        }
    }
}

4.3、时间空间复杂度

最好情况,时间复杂度为O(n);

最坏情况,时间复杂度为O(n ^ 2);

平均情况,时间复杂度为O(n ^ 1.3)

空间复杂度为O(1).

5、选择排序(不稳定)

5.1、思路描述

扫描整个数组,找到最小元素,将这个元素与第一个元素进行交换;之后从第二个位置开始扫描整个数组,找到最小元素,将这个元素与第二个元素进行交换;持续找出最小元素直到n - 1 个元素。即每一趟在n - i + 1(i = 1, 2, 3, ... , n - 1)个元素中选择最小的元素,并将其作为有序序列中第i 个元素。

5.2、代码

void select_sort(vector<int>& vec){
    for(int i = 0; i < vec.size() - 1; ++i){
        int minIndex = i;
        for(int j = i + 1; j < vec.size(); ++j){
            minIndex = vec[minIndex] < vec[j] ? minIndex : j;
        }
        int t = vec[i];
        vec[i] = vec[minIndex];
        vec[minIndex] = t;
    }
}

5.3、时间空间复杂度

最好情况,最坏情况,每轮筛选出最小值都需要O(n),一共迭代 n - 1 次,

因此,时间复杂度都为O(n ^ 2);

空间复杂度为O(1).

6、堆排序(不稳定)

6.1、思路描述

堆的特点就是堆顶元素是一个最值,大顶堆堆顶是最大值,小顶堆堆顶是最小值。堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,我们再把堆中剩余的元素 构成一个新的大顶堆,然后把堆顶元素与倒数第二个元素交换,一直迭代下去,等元素只有一个 的时候,数组就是有序的。

6.2、代码

void heap_adjust(vector<int>& vec, int start, int end){
    int temp = vec[start];
    for(int i = start * 2 + 1; i <= end; i = i * 2 + 1){
        if(i < end && vec[i] < vec[i + 1]){
            i++;
        }
        if(vec[i] > temp){
            vec[start] = vec[i];
            start = i;
        }else{
            break;
        }
    }
    vec[start] = temp;
}
void heap_sort(vector<int>& vec){
    int n = vec.size();
    //建立大根堆,从后往前依次调整
    for(int i = (n - 1 - 1) / 2; i >= 0; i--){
        heap_adjust(vec, i, n - 1);
    }
    //每次将根和待排序最后一次交换,然后再调整
    int temp;
    for(int i = 0; i < n - 1; i++){
        temp = vec[0];
        vec[0] = vec[n - i - 1];
        vec[n - i - 1] = temp;
        heap_adjust(vec, 0, n - 1 - i - 1)
    }
}

6.3、时间空间复杂度

最好情况,最坏情况下,时间复杂度都是O(n log n);

空间复杂度为O(1).

7、归并排序(稳定)

7.1、思路描述

分治思想,将大问题拆解成小问题。将一个大的数组拆分成几个小的数组(排序),然后一点点的合并。

7.2、代码

void merge_sort(vector<int>& vec, int left, int right){
    if(left >= right)
        return ;
    int mid = (right - left) / 2 + left;
    merge_sort(vec, left, mid);
    merge_sort(vec, mid + 1, right);
    Merge(vec, left, mid, right);
}

7.3、时间空间复杂度

时间复杂度:O(n * log n)

空间复杂度:O(n)

8、计数排序(稳定)

8.1、思路描述

根据待排序数组中最大元素和最小元素的差值范围,申请额外空间;遍历待排序,将每个元素出现的次数记录到元素值对应的额外空间内;对额外空间内数据进行计算,得出每一个元素的正确位置;将待排序数组每一个元素移动到计算出得到的正确位置上。

8.2、代码

void count_sort(vector<int>& vec){
    int max = vec[0];
    for(auto &v : vec){
        if(v > max)
            max = v;
    }
    vector<int>arr(max + 1);
    for(int i = 0; i < vec.size(); ++i){
        arr[vec[i]]++;
    }
    int j = 0;
    for(int i = 0; i < arr.size(); ++i){
        for(int k = 0; k < arr[i]; ++i){
                vec[j++] = i;
        }
    }
}

8.3、时间空间复杂度

时间复杂度:O(n + k)

空间复杂度:O(k)

9、桶排序(稳定)

9.1、思路描述

设置一个定量的数组当作空桶,遍历输入数组数据,并把数据一个一个放到对应的桶里面;对每个不是空的桶进行排序;从不是空桶里把排好序的数据拼接起来。

9.2、代码

void BucketSort(vector<int> &vec) {   
    int n = vec.size();
    // 得到数列的最大最小值
    int max = vec[0], min = vec[0];  
    for(int i = 1; i < n; ++i) {        
        if(vec[i] > max)           
            max = vec[i];        
        if (vec[i] < min)
            min = vec[i];
    }
    // 计算桶的数量并初始化
    int bucketNum = (max - min) / n + 1;
    vector<int> temp;
    vector<vector<int>> bucket;
    for (int i = 0; i < bucketNum; ++i)
        bucket.push_back(temp);
    // 将每个元素放入桶
    for (int i = 0; i < n; ++i) {
        // 减去最小值,处理后均为非负数
        int pos = (vec[i] - min) / len;
        bucket[pos].push_back(vec[i]);
    }
    // 对每个桶进行排序,此处可选择不同排序方法
    for (int i = 0; i < bucket.size(); ++i) 
        sort(bucket[i].begin(), bucket[i].end());
    // 将桶中的元素赋值到原序列
	int index = 0;
	for (int i = 0; i < bucketNum; ++i)
		for(int j = 0; j < bucket[i].size(); ++j)
			vec[index++] = bucket[i][j];
}

9.3、时间空间复杂度

最坏情况,时间复杂度为O(n ^ 2);

最好情况,时间复杂度为O(n );

平均情况,时间复杂度为O(n + k);

空间复杂度:O(k)

10、基数排序(稳定)

10.1、思路描述

先以个位数的大小对数据进行排序,接着以十位数的大小来多数进行排序,排到最后,就是一组有序的元素了。在以某位数进行排序的时候,是用“桶”来排序的。

10.2、代码

void RadixSort(vector<int> &vec) {
    int n = vec.size();
    // 得到数列的最大值
    int max = vec[0];  
    for (int i = 1; i < n; ++i) {        
        if(vec[i] > max)           
            max = vec[i];
    }
    // 计算最大值是几位数
    int digits = 1;
    while (max / 10 > 0) {
        digits++;
        max /= 10;
    }
    // 创建10个桶
    vector<int> temp;
    vector<vector<int>> bucket;
    for (int i = 0; i < 10; ++i) {
        bucket.push_back(temp);
    }
    // 进行每一趟的排序,从个位数开始排
    for (int i = 1; i <= digits; i++) {
        for (int j = 0; j < n; j++) {
            // 获取每个数最后第 i 位对应桶的位置
            int radio = (num[j] / (int)pow(10,i-1)) % 10;
            // 放进对应的桶里
            bucket[radio].push_back(num[j]);
        }
        // 合并放回原数组
        int k = 0;
        for (int j = 0; j < 10; j++) {
            for (int& t : bucket[j]) {
                vec[k++] = t;
            }
            //合并之后清空桶
            bucket[j].clear();
        }  
    }
}

10.3、时间空间复杂度

时间复杂度:O(k * n)

空间复杂度:O(k + n)

排序算法总结

排序算法

最好情况

最坏情况

平均时间复杂度

空间复杂度

稳定性

冒泡排序

O(n )

O(n ^ 2)

O(n ^ 2)

O(1)

稳定

快速排序

O(n log n)

O(n ^ 2)

O(n log n)

O(n log n)

不稳定

插入排序

O(n )

O(n ^ 2)

O(n ^ 2)

O(1)

稳定

希尔排序

O(n)

O(n ^ 2)

O(n ^ 1.3)

O(1)

不稳定

选择排序

O(n ^ 2)

O(n ^ 2)

O(n ^ 2)

O(1)

不稳定

堆排序

O(n log n)

O(n log n)

O(n log n)

O(1)

不稳定

归并排序

O(n log n)

O(n log n)

O(n log n)

O(n)

稳定

计数排序

O(n + k)

O(n + k)

O(n + k)

O(k)

稳定

桶排序

O(n )

O(n ^ 2)

O(n + k)

O(k)

稳定

基数排序

O(n * k)

O(n * k)

O(n * k)

O(n + k)

稳定

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值