常见十大排序算法代码总览(C++实现)

十大排序算法的C++代码,一个源文件全部搞定,并给出了详细的注释便于理解。

#include <iostream>
#include <bits/stdc++.h>
#include <vector>

using namespace std;

class Sort {
    public:

    // *****************三个基础排序O(n^2)*********************************************

    // ---------------------------------------------------------------
    // 1.冒泡排序
    // (1)从数组的第一个元素开始,相邻元素两两比较。
    // (2)如果前一个元素大于后一个元素,就交换它们的位置。
    // (3)对每一轮的未排序部分重复上述比较和交换过程。
    // (4)当某一轮比较中没有发生任何交换时,说明数组已完全排序,结束算法。
    // ---------------------------------------------------------------
    void bubbleSort(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++) {
            bool sw = false;    // 记录某一轮是否发生了交换
            for (int j = nums.size() - 1; j > i; j--) {
                if (nums[j] < nums[j - 1]) {
                    swap(nums[j], nums[j - 1]);
                    sw = true;
                }
            }
            if (sw == false) break;   // 没有发生任何交换时,说明数组已完全排序,提前结束
        }
    }

    // ---------------------------------------------------------------
    // 2.选择排序
    // (1)在未排序部分中找到最小(或最大)元素及其索引。
    // (2)将找到的最小(或最大)元素与未排序部分的第一个元素交换位置。
    // (3)缩小未排序部分,继续在新的未排序部分中重复上述步骤,直到整个数组排序完毕。
    // ---------------------------------------------------------------
    void selectionSort(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++) {
            int minNum = i;
            for (int j = nums.size() - 1; j > i; j--) {
                if (nums[j] < nums[minNum]) {
                    minNum = j;
                }
            }
            if (minNum != i) swap(nums[i], nums[minNum]);
        }
    }

    // ---------------------------------------------------------------
    // 3.插入排序
    // (1)将待排序数组分为已排序序列(初始时只有一个元素)和未排序序列。
    // (2)取未排序序列的第一个元素,与已排序序列中的元素逐个比较,找到插入位置。
    // (3)将当前待插入元素插入到已排序序列的相应位置。
    // (4)更新已排序序列范围,继续处理未排序序列中的下一个元素,直到未排序序列为空。
    // ---------------------------------------------------------------
    void insertSort(vector<int>& nums) {
        for (int i = 1; i < nums.size(); i++) {
            int tmp = nums[i];
            int j = i;
            while (j > 0 && tmp < nums[j - 1]) {
                nums[j] = nums[j - 1];
                j--;
            }
            nums[j] = tmp;
        }
    }

    // ***********************四个O(nlogn)排序********************************************

    // ---------------------------------------------------------------
    // 4.希尔排序:插入排序的改进版,结合插入排序记忆

    // (1)设定初始增量step,并按当前增量将数组划分为多个子序列。
    // (2)对每个子序列独立执行插入排序。
    // (3)减小增量,进入下一组子序列的排序。
    // (4)重复上述过程,直到增量减至1。
    // (5)使用增量为1进行最后一次插入排序,完成整个排序过程。
    // ---------------------------------------------------------------
    void shellSort(vector<int>& nums) {
        for (int step = nums.size() / 2; step > 0; step /= 2) {     // 增量划定操作
            for (int i = step; i < nums.size(); i++) {              // 剩下的就是插入排序的思路
                int tmp = nums[i];
                int j = i;
                while (j - step >= 0 && tmp < nums[j - step]) {
                    nums[j] = nums[j - step];
                    j -= step;
                }
                nums[j] = tmp;
            }
        }
    }

    // ---------------------------------------------------------------
    // 5.快速排序
    // (1)选取一个基准元素。
    // (2)对数组进行分区操作,使得基准元素左侧所有元素都小于它,右侧所有元素都大于它。
    // (3)递归地对基准元素左右两侧的子数组进行快速排序。
    // (4)当待排序数组长度小于等于1时,递归结束。
    // ---------------------------------------------------------------
    void quickSort(vector<int>& nums) {
        int n = nums.size() - 1;
        partition(nums, 0, n);
    }
    // 递归部分
    void partition(vector<int>& nums, int low, int high) {
        // 递归终结条件,左右指针指向同一个元素(排序数组的长度小于等于1)
        if (low >= high) return;
        // 快速排序内容-----------------
        int i = low, j = high;
        int pivot = nums[low];
        while (i < j) {
            while (i < j && nums[j] > pivot) j--;
            nums[i] = nums[j];
            while (i < j && nums[i] < pivot) i++;
            nums[j] = nums[i];
        }
        nums[i] = pivot;
        // ----------------------------
        // 继续分解再执行快排
        partition(nums, low, i - 1);
        partition(nums, i + 1, high);
    }

    // ---------------------------------------------------------------
    // 6.并归排序,分为分解和合并两步,在递归的写法中,先分解再合并
    // (1)递归地将待排序数组分解成长度为1的子数组。
    // (2)从最底层开始,两两合并已排序的子数组,生成新的已排序子数组。
    // (3)递归地合并上一层子数组,直到合并成一个完整的有序数组。
    // ---------------------------------------------------------------
    void mergeSort(vector<int>& nums) {
        int n = nums.size() - 1;
        divide(nums, 0, n);
    }
    // 分解
    void divide(vector<int>& nums, int low, int high) {
        // 当分解到只有一个单位,则结束
        if (low >= high) return;
        // 继续递归分解
        int mid = low + (high - low) / 2;
        divide(nums, low, mid);
        divide(nums, mid + 1, high);
        // 合并
        merge(nums, low, mid, high);
    }
    // 合并
    void merge(vector<int>& nums, int low, int mid, int high) {
        int i = low, j = mid + 1, k = 0;
        vector<int> tmp(high - low + 1);
        // 依次选择小的进入结果数组
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                tmp[k++] = nums[i++];
            } else {
                tmp[k++] = nums[j++];
            }
        }
        // 对不满的一半填满
        while (i <= mid) tmp[k++] = nums[i++];
        while (j <= high) tmp[k++] = nums[j++];
        // 将结果填回原数组
        for (i = low, k = 0; i <= high; i++, k++) {
            nums[i] = tmp[k];
        }
    }

    // ---------------------------------------------------------------
    // 7.堆排序
    // (1)构建初始堆:从数组中间位置开始自底向上进行下沉操作,确保整个数组满足堆序性质。
    // (2)交换堆顶元素与末尾元素:将最大元素放到正确的位置(数组末尾)。
    // (3)调整堆:将新堆重新调整为大顶堆。
    // (4)重复交换与调整过程,直到数组完全有序。
    // ---------------------------------------------------------------
    // API与初始化
    void heapSort(vector<int>& nums) {
        // 首先整体范围建堆
        for (int i = nums.size() / 2 - 1; i >= 0; i--) {
            adjustHeap(nums, i, nums.size());
        }
        // 交换根结点(最大值)和最后的结点,放在数组末尾,对剩下的序列从上到下调整为大顶堆
        for (int i = nums.size() - 1; i >= 0; i--) {
            swap(nums[0], nums[i]);
            adjustHeap(nums, 0, i);
        }
    }
    // 建堆过程
    void adjustHeap(vector<int>& nums, int father, int len) {
        int leftChild = father * 2 + 1;   // 使用数组存树,按层序遍历的方式放置元素,则某节点的左孩子位于该节点位置的2倍加1
        int rightChild = father * 2 + 2;
        // 堆的调整过程:如果某父节点存在比它大的孩子,则交换值
        int maxVal = father;
        if (leftChild < len && nums[leftChild] > nums[maxVal]) {
            maxVal = leftChild;
        }
        if (rightChild < len && nums[rightChild] > nums[maxVal]) {
            maxVal = rightChild;
        }
        if (maxVal != father) {
            swap(nums[maxVal], nums[father]);
            // 对改变过的子树调整为大顶堆,此时maxVal不再指向最大值,而是指向了与父节点交换了值的孩子
            adjustHeap(nums, maxVal, len);
        }
        
    }

    // *************时间复杂度为O(n)的三个排序******************************************
    // ---------------------------------------------------------------
    // 8.计数排序,本质上是哈希的思路
    // (1)确定待排序数组的值域范围。
    // (2)初始化计数数组,用于统计每个值的出现次数。
    // (3)遍历待排序数组,统计每个值在计数数组中的频率。
    // (4)累计计数,使计数数组中的每个值表示小于或等于该值的元素在排序后的位置。
    // (5)根据计数数组输出排序结果。
    // ---------------------------------------------------------------
    void countSort(vector<int>& nums) {
        // 获取数组最大值以确定计数范围,建立计数数组
        int maxVal = *max_element(nums.begin(), nums.end());
        vector<int> tmp(maxVal + 1);
        // 统计每个值的出现频率
        for (int i : nums) tmp[i]++;
        int index = 0;
        for (int i = 0; i < tmp.size(); i++) {
            // 累计计数
            while (tmp[i] > 0) {
                nums[index++] = i;
                tmp[i]--;
            }
        }
    }

    // ---------------------------------------------------------------
    // 9.桶排序,本质上是对待排数据划分区间,再在小区间里使用其它排序算法
    // (1)确定桶的数量和范围。
    // (2)分配元素到对应的桶。
    // (3)对每个桶内部进行排序。
    // (4)合并桶中的元素,得到最终的有序序列。
    // ---------------------------------------------------------------
    void bucketSort(vector<int>& nums) {
        // 设定桶大小
        int bucketSize = 10;   // 可自定
        // 获取数据范围与计算桶数量
        int minVal = nums[0], maxVal = nums[0];  // 获取最大最小值也可以直接用min_element和max_element,见下一个算法
        for (int i = 0; i < nums.size(); i++) {
            minVal = min(nums[i], minVal);
            maxVal = max(nums[i], maxVal);
        }
        int bucketNum = int((maxVal - minVal) / bucketSize) + 1;
        // 桶生成,数据入桶:划区间的过程
        vector<vector<int>> bucket(bucketNum, vector<int>());
        for (int i = 0; i < nums.size(); i++) {
            bucket[int((nums[i] - minVal) / bucketSize)].push_back(nums[i]);
        }
        // 桶内数据排序与返回原数组
        int index = 0;
        for (int i = 0; i < bucket.size(); i++) {
            // 这里使用了计数排序,使用其它排序算法也可以
            countSort(bucket[i]);
            // 填充回原数组
            for (int j = 0; j < bucket[i].size(); j++) {
                nums[index++] = bucket[i][j];
            }
        }
    }

    // ---------------------------------------------------------------
    // 10.基数排序,与桶排序的思路有相同之处
    // (1)确定最大位数,以确定循环次数。
    // (2)创建辅助数组。
    // (3)从最低位开始,进行多轮分配和收集操作。
    // (4)每轮收集后,将辅助数组的内容复制回原数组。
    // (5)重复上述过程,直到最高位也完成排序。
    // ---------------------------------------------------------------
    void radixSort(vector<int>& nums) {
        // 获取最大最小值,最大值用于确定循环次数,最小值用于确定是否对负数进行补偿
        int minVal = *min_element(nums.begin(), nums.end());
        int maxVal = *max_element(nums.begin(), nums.end());
        // 负数补偿(负数直接进行基数排序不得行,得保证整个序列是非负的)
        if (minVal < 0) {
            for (int i = 0; i < nums.size(); i++) {
                nums[i] -= minVal;
            }
            maxVal -= minVal;
        }
        // 确定循环次数,即最大值的位数
        int digit = 0;
        while (maxVal > 0) {
            maxVal /= 10;
            digit++;
        }
        // 与桶排序思路类似,基数排序的实现过程
        int radix = 10; // 每个位有 0~9 十种情况,故有10个桶
        vector<vector<int>> bucket(radix, vector<int>());
        for (int i = 0, mod = 10, dev = 1; i < digit; i++, mod *= 10, dev *= 10) {
            // 按位入桶
            for (int j = 0; j < nums.size(); j++) {
                bucket[(nums[j] % mod) / dev].push_back(nums[j]);  // 位数有低到高,个十百千...
            }
            // 填充回原数组
            int index = 0;
            for (int j = 0; j < bucket.size(); j++) {
                for (int k = 0; k < bucket[j].size(); k++) {
                    nums[index++] = bucket[j][k];
                }
                bucket[j].clear();   // 记得清空,下次循环还要用
            }
        }
        // 负数补偿的还原
        if (minVal < 0) {
            for (int i = 0; i < nums.size(); i++) {
                nums[i] += minVal;
            }
        }
    }
};
// ********************************************************************************

int main(){
    // 测试用例
    // vector<int> nums = {6, 8, 3, 4, 1, 7, 9, 2, 3, 5, 0};
    vector<int> nums = {15, 19, 46, 26, 3, 5, 27, 2, 36, 4, 38, 47, 50, 44, 48};

    Sort sort;

    // ---------------------------------
    // 10种排序算法调用如下:

    // sort.bubbleSort(nums);
    // sort.selectionSort(nums);
    // sort.insertSort(nums);
    // ------
    // sort.shellSort(nums);
    // sort.quickSort(nums);
    // sort.mergeSort(nums);
    // sort.heapSort(nums);
    // ------
    // sort.countSort(nums);
    // sort.bucketSort(nums);
    // sort.radixSort(nums);
    // ---------------------------------

    for (int i : nums) cout << i << ' ';

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏虫_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值