C++的排序

@TOC
在这里插入图片描述


稳定排序(排序前后两个相等的数的相对位置不变):归并排序、冒泡排序、插入排序、基数排序;
非稳定排序:希尔排序、堆排序、选择排序、快速排序。

1.快速排序

快速排序采用分而治之的思想,选取基准值,第一次排序之后将小于等于基准值的值都放在该值前面,将大于等于基准值的值都放在该值后面,接下来对前面的和后面的再次进行快速排序,分而治之直到无法再“分”为止。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>

using namespace std;

int Paritition(vector<int> &array, int left, int right) {
    int tmp = array[left];//选取一个基准值

    while (left < right) {
        while (left < right && tmp <= array[right]) {
            right--;//基准值小于等于后面的值 右指针左移
        }
        if (left < right) array[left++] = array[right]; //将后边小于基准值的值放在前面,左指针右移

        while (left < right && tmp > array[left]) {
            left++;//基准值大于后面的值 左指针右移
        }
        if (left < right) array[right--] = array[left];//将前面大于等于基准值的值放在后面,右指针左移
    }

    array[left] = tmp;//左右指针重合 存入基准值
    return left; //此时left的前面值都小于基准值, 后面的值都大于基准值
}

void QuickSort(vector<int> &array, int &left, int &right) {
    if (left >= right) return;

    //分而治之
    int mid = Paritition(array, left, right);//分为两部分
    QuickSort(array, left, mid - 1);//前半部分
    QuickSort(array, mid + 1, right);//后半部分
}

int main(int argc, char* argv[]) {
    vector<int> array;
    int num;

    while (1) {
        cin >> num;
        array.push_back(num);

        if (cin.get() == '\n') break;
    }

    int left = 0, right = array.size() - 1;
    QuickSort(array, left, right);//快速排序函数

    for (int i = 0; i < array.size(); i++) {
        cout << array[i] << " ";
    }

    return -1;
}

//优化快速排序

以下代码有三种快速排序的写法,参考STL标准模板库对快速排序进行优化
#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

/**************************快排一********************/
void QuickSort_v1(vector<int> &array, int l, int r) {
    if (l >= r) return;

    int x = l, y = r, z = array[l];
    while (x < y) {
        while (x < y && array[y] >= z) --y;
        if (x < y) array[x++] = array[y];
        while (x < y && array[x] <= z) ++x;
        if (x < y) array[y--] = array[x];
    }
    array[x] = z;

    QuickSort_v1(array, x + 1, r);
    QuickSort_v1(array, l, x - 1);
    return;
}


/**************************优化——快排二********************/
#define Swap(a, b) { \
auto _a = a; \
a = b, b = _a; \
}

//三点取中法
inline int midian(int a, int b, int c) {
    if (a > b) Swap(a, b);
    if (a > c) Swap(a, c);
    if (b > c) Swap(b, c);
    return b;
}

void QuickSort_v2(vector<int> &array, int l, int r) {
    while (l < r) {
        int x = l, y = r, z = midian(array[l], array[(l + r) >> 1], array[r]);
        //无监督
        do {
            while (array[x] < z) ++x;
            while (array[y] > z) --y;
            if (x <= y) {
                Swap(array[x], array[y]);
                ++x, --y;
            }
        } while (x <= y);

        QuickSort_v2(array, x, r);
        r = y; //单边递归
    }
    return;
}


/**************************优化——快排三********************/

const int threshold = 16;

void _QuickSort_v3(vector<int> &arr, int l, int r) {
    while (r - l > threshold) {
        int x = l, y = r, z = midian(arr[l], arr[(l + r) >> 1], arr[r]);
        //无监督
        do {
            while (arr[x] < z) ++x;
            while (arr[y] > z) --y;
            if (x <= y) {
                Swap(arr[x], arr[y]);
                ++x, --y;
            }
        } while (x <= y);

        _QuickSort_v3(arr, x, r);
        r = y;//单边递归
    }
    return;
}

void InsertSort(vector<int> &arr, int l, int r) {
    int ind = l;
    for (int i = l + 1; i <= r; i++) {
        if (arr[ind] > arr[i]) ind = i;
    }
    Swap(arr[ind], arr[l]);

    for (int i = l + 2; i <= r; i++) {
        int j = i;
        while (arr[j] < arr[j - 1]) {
            Swap(arr[j], arr[j - 1]);
            --j;
        }
    }
    return;
}

void QuickSort_v3(vector<int> &arr, int l, int r) {
    _QuickSort_v3(arr, l, r);//数组大小超过16使用快速排序
    InsertSort(arr, l, r); //小于16用插入排序
    return;
}


int main(int argc, char* argv[]) {
    vector<int> array;
    int num;

    while (1) {
        cin >> num;
        array.push_back(num);

        if (cin.get() == '\n') break;
    }

    //QuickSort_v1(array, 0, array.size() - 1);
    //QuickSort_v2(array, 0, array.size() - 1);
    QuickSort_v3(array, 0, array.size() - 1);

    for (int i = 0; i < array.size(); i++) {
        cout << array[i] << " ";
    }

    return -1;
}

2.插入排序

在快速排序中第三个版本已使用。
先选定第一个值为最小(第一个for循环),之后从下一位开始进行比较填充,每次保证从第一位到第j位有序(for循环中使用while循环)

void InsertSort(vector<int> &arr, int l, int r) {
    int ind = l;
    for (int i = l + 1; i <= r; i++) {
        if (arr[ind] > arr[i]) ind = i;
    }
    Swap(arr[ind], arr[l]);

    for (int i = l + 2; i <= r; i++) {
        int j = i;
        while (arr[j] < arr[j - 1]) {
            Swap(arr[j], arr[j - 1]);
            --j;
        }
    }
    return;
}

3.选择排序

每次选中一个值,将该值与后面所有的值进行比较,最终给每个位置选择当前元素最小的值。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

void Swap(vector<int>& array, int i, int j) {
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}

void SelectSort(vector<int>& array) {
    if (array.size() < 2) return;

    for (int i = 0; i < array.size() - 1; i++) {
        int minCur = i;
        for (int j = i + 1; j < array.size(); j++) {
            minCur = array[j] < array[minCur] ? j : minCur;
        }
        Swap(array, i, minCur);
    }
}

int main(int argc, char* argv[]) {
    vector<int> array;
    int num;

    while (1) {
        cin >> num;
        array.push_back(num);

        if (cin.get() == '\n') break;
    }

    SelectSort(array);

    for (int i = 0; i < array.size(); i++) {
        cout << array[i] << " ";
    }

    return -1;
}

4.冒泡排序

重复地遍历要排序的数列,每次比较相邻的两个元素,如果它们的顺序错误就把它们交换过来,这样在最后的元素应该会是最大的数。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

void Swap(vector<int>& array, int i, int j) {
    array[i] = array[i] ^ array[j];
    array[j] = array[i] ^ array[j];
    array[i] = array[i] ^ array[j];
    /*int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;*/
}

void BubbleSort(vector<int>& array) {
    if (array.size() < 2) return;

    for (int i = 0; i < array.size(); i++) {
        for (int j = 0; j < array.size() - i - 1; j++) {
            if (array[j] > array[j + 1]) {
                Swap(array, j, j + 1); //在最后的元素应该会是最大的数
            }
        }

    }
}

int main(int argc, char* argv[]) {
    vector<int> array;
    int num;

    while (1) {
        cin >> num;
        array.push_back(num);

        if (cin.get() == '\n') break;
    }

    BubbleSort(array);

    for (int i = 0; i < array.size(); i++) {
        cout << array[i] << " ";
    }

    return -1;
}

5.归并排序

求出中点位置,先让左侧排好序,再让右边排好序(分而治之),之后往一块整合(类似合并两个有序数组),整合到辅助数组里,之后再整体放回原数组。
可以用来解决“逆序对”系列问题。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

void merge(vector<int>& arr, int l, int mid, int r) {
    vector<int> help;// (r - l + 1, 0); //每次递归都会是一个新的help数组

    int p1 = l;
    int p2 = mid + 1;

    int tmp = 0;
    while (p1 <= mid || p2 <= r) {
        if (p1 == mid + 1) {//越界
            tmp = arr[p2++];
        }
        else if (p2 == r + 1) { //越界
            tmp = arr[p1++];
        }
        else if(arr[p1] < arr[p2]){
            tmp = arr[p1++];
        }
        else{
            tmp = arr[p2++];
        }

        help.push_back(tmp);
    }

    for (int i = 0; i < help.size(); i++) {
        arr[l + i] = help[i];//但是arr不是新的数组
    }

    return;
}

void MergeSort(vector<int>& arr, int l, int r) {
    if (l == r) return;

    int mid = l + ((r - l) >> 1);

    MergeSort(arr, l, mid);
    MergeSort(arr, mid + 1, r);
    merge(arr, l, mid, r);
    return;
}

int main(int argc, char* argv[]) {
    vector<int> arr;
    int num;

    while (1) {
        cin >> num;
        arr.push_back(num);

        if (cin.get() == '\n') break;
    }

    MergeSort(arr, 0, arr.size() - 1);

    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }

    return -1;
}

6.堆排序

堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子结点的键值或索引总是小于(小根堆)(或者大于(大根堆))它的父节点。

先将第一位变为整个数组大根堆的父节点,再将最后一个数和第一个数做交换,这样可确保最大值在最后一位,然后整个数组大小(heapSize)减一(将最后一位“断联”)。
之后从第0位往下判断是否还能下移,以确保前面的数组依然为大根堆,之后交换,heapSize减一。知道heapSize为0,排序完成。

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

//左子节点:2*i+1
//右子节点:2*i+2
//父节点:(i-1)/2

#define Swap(a, b) { \
auto _a = a; \
a = b, b = _a; \
}

//堆插入
void HeapInsert(vector<int>& arr, int idx) {
    while (arr[idx] > arr[(idx - 1) / 2]) { //当前值和父的值作比较 看能否上移
        Swap(arr[idx], arr[(idx - 1) / 2]);
        idx = (idx - 1) / 2; //
    }
}

//某个数在idx位置(看作父位置),能否往下移动
void HeapIfy(vector<int>& arr, int idx, int heapSize) {
    int left = idx * 2 + 1; //左孩子的下标
    while (left < heapSize) { //下方还有孩子
        //两个孩子中,谁的值大,把下标给largest
        int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
        //父和较大的孩子之间,谁的值大,把下标给largest
        largest = arr[largest] > arr[idx] ? largest : idx;

        if (largest == idx) { //最大值就是当前父位置
            break;
        }

        Swap(arr[largest], arr[idx]); 
        idx = largest; //索引值的变化
        left = idx * 2 + 1; //
    }
}

void HeapSort(vector<int>& arr) {
    if (arr.empty() || arr.size() < 2) {
        return;
    }

    //把数组整体范围全变成大根堆
    /*for (int i = 0; i < arr.size(); i++) {
        HeapInsert(arr, i); //O(logN) //这时只能保证首位值最大,其他只是满足大根堆 不一定有序
    }*/
    //效率更高的办法
    for (int i = arr.size() - 1; i >= 0; i--) {
        HeapIfy(arr, i, arr.size());
    }

    int heapSize = arr.size();
    Swap(arr[0], arr[heapSize - 1]); //首位和最后一位做交换,此时最后一位必是最大值
    heapSize--; //最后一位“断联”

    while (heapSize > 0) {
        HeapIfy(arr, 0, heapSize);
        Swap(arr[0], arr[heapSize - 1]);
        heapSize--; //最后一位“断联”
    }
}

int main(int argc, char* argv[]) {
    vector<int> arr;
    int num;

    while (1) {
        cin >> num;
        arr.push_back(num);

        if (cin.get() == '\n') break;
    }

    HeapSort(arr);

    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }

    return -1;
}

7.计数排序

计数排序 的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

8.桶排序

桶排序 是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序。

9.基数排序

对每一位进行排序,从最低位开始排序。按照低位先排序,然后收集(根据count前缀和数组存入help数组);再按照高位排序,然后再收集;依次类推,直到最高位。(也可认为是优先级低的先排序,高优先级的再排序)。
即先根据个位数字排序,再根据十位数字排序,再根据百位数字排序......

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>

using namespace std;

//最大值有几个十进制位
int maxbits(vector<int>& arr) {
    int _max = INT_MIN;
    for (int i = 0; i < arr.size(); i++) {
        _max = max(_max, arr[i]);
    }

    int res = 0;
    while (_max |= 0) {
        res++;
        _max /= 10;
    }

    return res;
}

//将x的第d位的数字拿出来
int GetDigit(int x, int d) {
    for (int i = 1; i < d; i++) { //d >= 1
        x /= 10;        
    }
    x %= 10;
    return x;
}

//在left到right范围内排序
void _RadixSort(vector<int>& arr, const int left, const int right, int digit) {
    const int radix = 10; //"前缀和"的最大空间
    vector<int> help(right - left + 1);

    int i = 0;

    for (int d = 1; d <= digit; d++) { //入桶出桶的次数 //最大值的位数 //排序的次数
        int count[radix] = { 0 }; // //count[1]表示当前位是0和1的数字有多少个(小于等于1的个数)

        for (i = left; i <= right; i++) {
            int j = GetDigit(arr[i], d); //依次拿出arr数组中每个数 第d位上 值为j的总个数 (d = 1是个位)
            count[j]++; //个数放进count数组中
        }

        for (i = 1; i < radix; i++) {
            count[i] += count[i - 1]; //处理成前缀和
        }

        //数组从右往左遍历
        for (i = right; i >= left; i--) {
            int j = GetDigit(arr[i], d); //拿出位数        
            help[count[j] - 1] = arr[i]; //填进辅助数组            
            count[j]--; //前缀和那个词频(个数) 减一
        }

        //维护本次出桶的结果再放回arr
        for (i = left; i <= right; i++) { //
            arr[i] = help[i]; //
        }
    }
}

void RadixSort(vector<int>& arr) {
    if (arr.empty() || arr.size() < 2) {
        return;
    }
    _RadixSort(arr, 0, arr.size() - 1, maxbits(arr));
}


int main(int argc, char* argv[]) {
    vector<int> arr;
    int num;

    while (1) {
        cin >> num;
        arr.push_back(num);

        if (cin.get() == '\n') break;
    }

    RadixSort(arr);

    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }

    return -1;
}

10.希尔排序

希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本。

11.补充

【注】可以使用系统自带的sort函数与自己写的排序函数进行对数器比对,以测试自己写的是否正确。

产生随机数进行测试并检查排序函数:

#define MAX_N 100
vector<int> arr;
void GetRandData(int n) {
    for (int i = 0; i < n; i++) {
        arr.push_back(rand() % n);
    }
    return;
}

int Check(vector<int> &arr, int n) {
    for (int i = 1; i < n; i++) {
        if (arr[i] < arr[i - 1]) return 0;
    }
    return 1;
}

int main(int argc, char* argv[]) {
    vector<int> array;
    srand(time(0));
    GetRandData(MAX_N);
    array = arr;

    if (Check(array, MAX_N) == 1) {
        cout << "排序正确" << endl;
    }

    return -1;
}

超详细十大经典排序算法总结(java代码)c或者cpp的也可以明白

互联网学习 文章被收录于专栏

互联网知识点

全部评论
感谢大神分享的C++的排序
点赞
送花
回复
分享
发布于 2022-08-29 13:31 河南

相关推荐

整体时间线:2月末力扣从零开始。3月初刷题成瘾,中旬陆续开面开杀,被机试折磨,下旬纠结日常offer选择。4月入职淘天,从硬landing到上手业务快乐融入5月平静美好,顺利到我觉得直接转正是最佳选择,月底转暑期流程被hr直接挂,主管诱骗能转正,万幸蚂蚁暑期流程没拒掉,压哨发意向,手里也还有个腾讯offer兜底,毁约腾讯暑期到此结束。==============================一些感悟:永远保留后手,先拿了阿里国际日常,拿到网易伏羲offer之后才拒绝意向,中间难免要催hr尽量开在同一时间,后续等淘天oc的时候立马拒了网易意向。不会让手里超过2个offer,但是也不会在未确定的时候就拒掉到手的。在淘天的时候师兄主管都保证能转正别担心,甚至主管拉我进内部群一起团建,但是始终把腾讯offer抓在手里,也给了我撕破脸之后和主管谈判的底气。蚂蚁一面二面间隔一个半月,时不时反向保温一下面试官又没拒掉流程,真是我最明智的选择。==============================实习体验:研一在鹅厂AI&nbsp;Lab实习打杂纯快乐的,自己包装一下也是有产出的。遇到的所有人都很温和有礼貌,整体不卷年纪偏大,公司关怀好,不考虑城市的话应该会是第一选择。淘天业务组非常业务,技术不容易提升但是容易有产出,整体强度能承受分到的活也不多还挺核心的,师兄还是很nice的,往年转正待遇也挺好,小组整体年龄结构有中有小没老人,晋升空间不错。拒掉的offer里面,同花顺是做大模型部署加速的,给钱少太卷拒了;阿里国际是研究型实习生随便面的感觉面试官技术没有太懂;网易伏羲是llm+智能npc其实很有搞头,还是贪图大厂title拒了;腾讯这个最可惜,agent+游戏ai,而且在大部门实习过可以丝滑landing,腾讯招聘经常能看到校招社招广告,应该是团队扩张期,考虑到城市因素忍痛拒绝,释放一个hc给大家。==============================彩蛋:想看看牛u会做什么选择,感觉人生到了这个时间点,每个决策都会影响很大,已知和女友都是浙江人,她稳定杭州工作,计划后续杭州定居结婚。 #暑期实习# #腾讯# #阿里# #蚂蚁# #大模型# #淘天#
投递蚂蚁集团等公司10个岗位
点赞 评论 收藏
转发
6 26 评论
分享
牛客网
牛客企业服务