十大排序算法

排序算法

十大排序算法视频教程:https://www.bilibili.com/video/BV1Ur4y1w7tv
排序算法时间、空间复杂度总结(n是数据规模,k是”桶“的个数,d是最大的位数)

名称平均最好最差空间复杂度稳定性
冒泡排序 (Bubble Sort)O(n2)O(n)O(n2)O(1)稳定
选择排序 (Selection Sort)O(n2)O(n2)O(n2)O(1)不稳定
插入排序 (Insertion Sort)O(n2)O(n)O(n2)O(1)稳定
希尔排序 (Shell Sort)O(n4/3)O(n)O(n4/3)~O(n2)O(1)不稳定
快速排序 (Quick Sort)O(nlogn)O(nlogn)O(n2)O(nlogn)不稳定
归并排序 (Merge Sort)O(nlogn)O(nlogn)O(nlogn)O(n)稳定
堆排序 (Heap Sort)O(nlogn)O(nlogn)O(nlogn)O(1)不稳定
计数排序 (Counting Sort)O(n+k)O(n+k)O(n+k)O(n+k)稳定
桶排序 (Bucket Sort)O(n+k)O(n+k)O(n+k)O(n+k)稳定
基数排序 (Radix Sort)O(d*(n+k))O(d*(n+k))O(d*(n+k))O(n+k)稳定

1 冒泡排序

如果前一个数比后一个数大,就交换两个数!

vector<int> bubbleSort(const vector<int> &nums) {
    auto n = nums.size();
    vector<int> result = nums;
    for (int i = n - 1; i > 0; --i) {
        for (int j = 0; j < i; ++j) {
            if (result[j] > result[j + 1])
                swap(result[j], result[j + 1]);
        }
    }
    return result;
}

2 选择排序

每次查找出后面未排序的数组中最小的那个和最前面的交换!

vector<int> selectSort(const vector<int> &nums) {
    auto n = nums.size();
    vector<int> result = nums;
    for (int i = 0; i < n - 1; ++i) {
        int minIdx = i, minNum = result[i];
        for (int j = i + 1; j < n; ++j) {
            if (result[j] < minNum) {
                minNum = result[j];
                minIdx = j;
            }
        }
        swap(result[i], result[minIdx]);
    }
    return result;
}

3 插入排序

将第n个数与前n-1个有序的数做比较,一直交换,直到将这个数插入到合适的位置!

vector<int> insertSort(const vector<int> &nums) {
    std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
    auto n = nums.size();
    vector<int> result = nums;
    for (int i = 1; i < n; ++i) {
        for (int j = i; j > 0; --j) {
            if (result[j] < result[j - 1])
                swap(result[j], result[j - 1]);
        }
    }
    return result;
}

4 希尔排序

将数据分组,每隔step作为一组,step每论除以2,直到step==1。分组后的数据使用插入排序!

vector<int> shellSort(const vector<int> &nums) {
    auto n = nums.size();
    vector<int> result = nums;
    for (int step = n / 2; step > 0; step /= 2) {
        for (int group = 0; group < step; ++group) { // 按照步长进行分组
            for (int i = group + step; i < n; i += step) { // 对每一组进行插入排序
                for (int j = i; j >= step; j -= step) {
                    if (result[j] < result[j - step])
                        swap(result[j], result[j - step]);
                }
            }
        }
    }
    return result;
}

5 快速排序

1)先从数列中取出一个元素作为基准数。2)扫描数列,将比基准数小的元素全部放到它的左边,大于或等于基准数的元素全部放到它的右边,得到左右两个区间。3)再对左右区间重复第二步.直到各区间少于两个元素。

// 递归方法1   https://blog.csdn.net/weixin_44915226/article/details/119535259
void quicksort1(vector<int> &nums, int start, int end) {
    int left = start, right = end - 1;
    if (left >= right)
        return;
    int base = nums[start];
    while (left < right) {
        while (nums[right] >= base && left < right) --right;
        nums[left] = nums[right];
        while (nums[left] <= base && left < right) ++left;
        nums[right] = nums[left];
    }
    nums[left] = base;
    quicksort1(nums, start, left);
    quicksort1(nums, left + 1, end);
}

// 递归方法2   https://blog.csdn.net/weixin_44915226/article/details/119535259
void quicksort2(vector<int> &nums, int start, int end) {
    int left = start, right = end - 1;
    if (left >= right)
        return;

    while (left < right) {
        while (nums[right] >= nums[start] && left < right) --right;
        while (nums[left] <= nums[start] && left < right) ++left;
        if (left < right) swap(nums[left], nums[right]);
    }
    swap(nums[left], nums[start]);
    quicksort2(nums, start, left);
    quicksort2(nums, left + 1, end);
}
// 快速排序
vector<int> quickSort(const vector<int> &nums) {
    vector<int> result = nums;
    quicksort1(result, 0, nums.size());
    return result;
}

6 归并排序

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。可以使用递归和循环实现。

// 递归函数
void _mergeSortR(vector<int> &nums, vector<int> &tempNums, int start, int end) {
    if (start >= end) return;
    // 分成左右两段然后分别排序
    int mid = start + (end - start) / 2;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    _mergeSortR(nums, tempNums, start1, end1);
    _mergeSortR(nums, tempNums, start2, end2);
    // 将排序后的两个数组合并
    int i = start;
    while (start1 <= end1 && start2 <= end2)
        tempNums[i++] = nums[start1] < nums[start2] ? nums[start1++] : nums[start2++];
    while (start1 <= end1) tempNums[i++] = nums[start1++];
    while (start2 <= end2) tempNums[i++] = nums[start2++];
    for (i = start; i <= end; ++i) // 赋值给原数组
        nums[i] = tempNums[i];
}
//归并排序,递归
vector<int> mergeSortR(const vector<int> &nums) {
    auto n = nums.size();
    vector<int> result = nums, tempNums = nums;
    _mergeSortR(result, tempNums, 0, n - 1);
    return result;
}
// 归并排序,循环
vector<int> mergeSortF(const vector<int> &nums) {
    int n = nums.size();
    vector<int> result = nums, tempNums = nums;
    for (int seg = 1; seg < n; seg *= 2) { // 排序的趟数的循环
        for (int start = 0; start < n; start += 2 * seg) { // 其中一段数组分成两段来排序
            // 分成左右两段
            int mid = min(start + seg, n);
            int right = min(start + 2 * seg, n);
            int start1 = start, end1 = mid;
            int start2 = mid, end2 = right;
            // 合并
            int i = start;
            while (start1 < end1 && start2 < end2)
           tempNums[i++]=result[start1]<result[start2]?result[start1++]:result[start2++];
            while (start1 < end1) tempNums[i++] = result[start1++];
            while (start2 < end2) tempNums[i++] = result[start2++];
            for (i = start; i < right; ++i)
                result[i] = tempNums[i];
        }
    }
    return result;
}

7 堆排序

堆排序的基本思想是:1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。最后,就得到一个有序的序列了。

//堆排序,递归方法
void heapifyR(vector<int> &nums,int start,int end){
    int father=start;
    int child=father*2+1;
    if(child>end) return;
    if((child+1)<=end && (nums[child]<nums[child+1])) ++child;
    if(nums[father]>nums[child]) return;
    swap(nums[father],nums[child]);
    heapifyR(nums,child,end);
}
//堆排序,循环方法
void heapifyF(vector<int> &nums,int start,int end){
    int father=start;
    int child=father*2+1;
    while(child<end){
        if((child+1)<=end && (nums[child]<nums[child+1])) ++child;
        if(nums[father]>nums[child]) return;
        swap(nums[father],nums[child]);
        father=child;
        child=father*2+1;
    }
}
//堆排序
vector<int> heapSort(const vector<int> &nums){
    std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
    int n = nums.size();
    vector<int> result = nums;
    // 第一次初始化大顶堆,从最后一个父节点开始调整
    for (int i = (n-2)/2; i >= 0 ; --i)
        heapifyR(result,i,n-1); // heapifyF(result,i,n-1);
    // 把第一个元素与堆最后一个元素做交换,然后重新调整,知道排序完毕
    for (int i = n-1; i > 0 ; --i){
        swap(result[0],result[i]);
        heapifyR(result,0,i-1); // heapifyF(result,0,i-1);
    }
    return result;
}

8 计数排序

计数排序是一个非基于比较的排序算法,它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(nlog(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)

vector<int> countSort(const vector<int> &nums){
    vector<int> result = nums;
    int maxNum= result[0];
    for (const auto& num:result) // 寻找数组中的最大值
        maxNum = max(num, maxNum);
    vector<int> countArr(maxNum+1,0); // 创建辅助数组
    for (const auto& num:result) // 计数
        ++countArr[num];
    int k=0;
    for (int i = 0; i <= maxNum; ++i) // 重新排序
        for (int j = 0; j < countArr[i]; ++j)
            result[k++]=i;
    return result;
}

9 桶排序

桶排序的基本思想是假设数据在[min,max]之间均匀分布,其中min、max分别指数据中的最小值和最大值。那么将区间[min,max]等分成n份,这n个区间便称为n个桶。将数据加入对应的桶中,然后每个桶内单独排序。由于桶之间有大小关系,因此可以从大到小(或从小到大)将桶中元素放入到数组中。

vector<int> bucketSort(const vector<int> &nums){
    int n = nums.size();
    vector<int> result = nums;
    int minNum=result[0],maxNum=minNum;
    for (const auto &num:result) {
        minNum = min(minNum,num);
        maxNum = max(maxNum,num);
    }
    double range = (double)(maxNum-minNum)/(n-1);
    vector<vector<int>> buckets(n);
    for (const auto &num:result) // 分桶装数据
        buckets[static_cast<int>( (num-minNum)/range)].push_back(num);
    for (auto &bucket:buckets) // 桶类排序
        sort(bucket.begin(),bucket.end());
    int i=0;
    for (const auto &bucket:buckets)
        for (const auto &num:bucket)
            result[i++] = num;
    return result;
}

10 基数排序

基数排序(Radix sort)是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。它是这样实现的: 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。

vector<int> radixSort(const vector<int> &nums) {
    int n = nums.size();
    vector<int> result = nums;
    int maxNum = *max_element(result.begin(), result.end());
    for (int digit = 1; maxNum / digit > 0; digit *= 10) {
        vector<int> buckets(10, 0),tempArr(n,0);
        for (const auto &num:result) ++buckets[num / digit % 10];
        for (int i = 1; i < 10; ++i) buckets[i] += buckets[i - 1];
        for (int i = n - 1; i >= 0; --i) {
            int id=result[i]/digit%10;
            tempArr[buckets[id]-1]=result[i];
            --buckets[id];
        }
        result = tempArr;
    }
    return result;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值