首页 > 编程语言 >十大排序算法的C++实现

十大排序算法的C++实现

时间:2024-04-05 22:00:01浏览次数:25  
标签:arr int void C++ 算法 vector 排序

2024年4月5日 排序算法

一、稳定性的定义

排序算法的稳定性是指排序过程中,相同元素的相对位置是否会发生变化。
稳定的排序算法在排序过程中不会改变元素彼此的位置的相对次序,
而不稳定的排序算法经常会改变这个次序。
稳定性是一个特别重要的评估标准,排序算法的稳定性是一个特别重要的参数衡量指标依据。

二、各大排序算法的稳定性

排序算法:平均时间复杂度辅助空间是否稳定
冒泡排序O(n²)O(1)
选择排序O(n²)O(1)
插入排序O(n²)O(1)
归并排序O(nlog2n)O(n)
快速排序O(nlog2n)O(nlog2n)
堆排序O(nlog2n)O(1)
希尔排序O(nlog2n)O(nlog2n)
计数排序O(n+k)O(n+k)
桶排序O(n+k)O(n+k)
基数排序O(n*k)O(n+k)

三、排序算法分类

排序算法可以分为内部排序和外部排序,
内部排序是数据记录在内存中进行排序,
而外部排序是因排序的数据很大,
一次不能容纳全部的排序记录
,在排序过程中需要访问外存。

常见的内部排序算法有:

  • 插入排序
  • 希尔排序
  • 选择排序
  • 冒泡排序
  • 归并排序
  • 快速排序
  • 堆排序
  • 基数排序
  • ……
算法竞赛中,我们主要使用的都是内部排序算法,

四、各大排序算法介绍

1.冒泡排序

冒泡排序是一种简单的排序算法。
它重复地遍历要排序的数列,
一次比较两个元素,
如果它们的顺序错误就把它们交换过来。

代码实现

void BubbleSort(vector<int>& arr){
    int n = arr.size();
    for(int i=0;i<n-1;i++){ // 遍历第一个元素
        for(int j=0;j<n-i-1;j++){ //遍历第二个元素
            if(arr[j]>arr[j+1]){ //比较两个元素
                swap(arr[j],arr[j+1]); //交换
            }
        }
    }
    return;
}

2.选择排序

选择排序是一种简单直观的排序算法。
它的工作原理:首先在未排序序列中找到最小(大)元素,
存放到排序序列的起始位置,
然后,再从剩余未排序元素中继续寻找最小(大)元素,
然后放到已排序序列的末尾。
以此类推,直到所有元素均排序完毕。

代码实现

void SelectionSort(vector<int>& arr){
    int n = arr.size();
    for(int i=0;i<n-1;i++){ // 遍历第一个元素
        int min_index = i; // 假设第一个元素是最小的
        for(int j=i+1;j<n;j++){ // 遍历第二个元素
            if(arr[j]<arr[min_index]){ // 比较两个元素
                min_index = j; // 更新最小元素的下标
            }
        }
        swap(arr[i],arr[min_index]); // 交换
    }
    return ;
}

3.插入排序

插入排序也是一种简单直观的排序算法。
它的工作原理是通过构建有序序列,
对于未排序数据,在已排序序列中从后向前扫描,
找到相应位置并插入。

代码实现

void InsertionSort(vector<int>& arr){
    int n = arr.size();
    for(int i=1;i<n;i++){ // 遍历第一个元素
        int key = arr[i]; // 取出待排序的元素
        int j = i-1; // 已排序序列的最后一个元素
        while(j>=0 && arr[j]>key){ // 从后向前扫描
            arr[j+1] = arr[j]; // 向后移动元素
            j--; // 向前移动
        }
        arr[j+1] = key; // 插入元素
    }
    return 0;
}

4.希尔排序

希尔排序是插入排序的一种更高效的改进版本。
但希尔排序是非稳定排序算法。
希尔排序是基于增量序列的插入排序算法,
也称为递减增量排序算法。
希尔排序的效率比O(nlog2n)的排序算法低,
但比O(n²)的排序算法高。

代码实现

void ShellSort(vector<int>& arr){
    int n = arr.size();
    for(int gap=n/2;gap>0;gap/=2){ // 遍历增量序列
        for(int i=gap;i<n;i++){ // 遍历每个元素
            int key = arr[i]; // 取出待排序的元素
            int j = i-gap; // 已排序序列的最后一个元素
            while(j>=0 && arr[j]>key){ // 从后向前扫描
                arr[j+gap] = arr[j]; // 向后移动元素
                j -= gap; // 向前移动
            }
            arr[j+gap] = key; // 插入元素
        }
    }
    return ;
}

5.归并排序

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

代码实现

void Merge(vector<int>& arr,int left,int mid,int right){
    int i = left,j = mid+1;
    vector<int> temp;
    while(i<=mid && j<=right){
        if(arr[i]<=arr[j]) temp.push_back(arr[i++]);
        else temp.push_back(arr[j++]);
    }
    while(i<=mid) temp.push_back(arr[i++]);
    while(j<=right) temp.push_back(arr[j++]);
    for(int k=0;k<temp.size();k++){
        arr[left+k] = temp[k];
    }
    return ;
}

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

6.堆排序

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。
堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

代码实现

void swap(int& a,int& b){
    int temp = a;
    a = b;
    b = temp;
    return ;
}

void Max_Heapify(vector<int>& arr,int start,int end){
    int dad = start;
    int son = dad*2+1;
    while(son<=end){
        if(son+1<=end && arr[son]<arr[son+1]) son++;
        if(arr[dad]>arr[son]) return ;
        else{
            swap(arr[dad],arr[son]);
            dad = son;
            son = dad*2+1;
        }
    }
    return ;
}

void Build_Max_Heap(vector<int>& arr){
    int len = arr.size();
    for(int i=len/2-1;i>=0;i--) Max_Heapify(arr,i,len-1);
    return ;
}

void HeapSort(vector<int>& arr){
    Build_Max_Heap(arr);
    for(int i=arr.size()-1;i>=1;i--){
        swap(arr[0],arr[i]);
        Max_Heapify(arr,0,i-1);
    }
    return ;
}

7.计数排序

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

代码实现

void CountingSort(vector<int>& arr){
    int len = arr.size();
    if(len<=1) return ;
    int max = arr[0],min = arr[0];
    for(int i=1;i<len;i++) max = max>arr[i]?max:arr[i];
    for(int i=1;i<len;i++) min = min<arr[i]?min:arr[i];
    vector<int> count(max-min+1,0);
    for(int i=0;i<len;i++) count[arr[i]-min]++;
    int index = 0;
    for(int i=min;i<=max;i++)
        while(count[i-min]--) arr[index++] = i;
    return ;
}

8.桶排序

桶排序是计数排序的升级版。
它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。

代码实现

void BucketSort(vector<int>& arr){
    int len = arr.size();
    if(len<=1) return ;
    int max = arr[0],min = arr[0];
    for(int i=1;i<len;i++) max = max>arr[i]?max:arr[i];
    for(int i=1;i<len;i++) min = min<arr[i]?min:arr[i];
    int d = max-min;
    vector<vector<int> > bucket(d+1);
    for(int i=0;i<len;i++) bucket[arr[i]-min].push_back(arr[i]);
    int index = 0;
    for(int i=0;i<=d;i++)
        sort(bucket[i].begin(),bucket[i].end());
    for(int i=0;i<=d;i++)
        for(int j=0;j<bucket[i].size();j++)
            arr[index++] = bucket[i][j];
    return ;
}

9.基数排序

基数排序是按照低位先排序,
然后收集;再按照高位排序,然后再收集;以此类推,直到最高位。
有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的结果就是一种排序方式。

代码实现

void RadixSort(vector<int>& arr){
    int len = arr.size();
    if(len<=1) return ;
    int max = arr[0],min = arr[0];
    for(int i=1;i<len;i++) max = max>arr[i]?max:arr[i];
    for(int i=1;i<len;i++) min = min<arr[i]?min:arr[i];
    int d = max-min;
    vector<vector<int> > bucket(10);
    for(int i=0;i<len;i++) bucket[(arr[i]-min)/d%10].push_back(arr[i]);
    int index = 0;
    for(int i=0;i<10;i++)
        sort(bucket[i].begin(),bucket[i].end());
    for(int i=0;i<10;i++)
        for(int j=0;j<bucket[i].size();j++)
            arr[index++] = bucket[i][j];
    return ;
}

10.快速排序

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,
其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,
以达到整个序列有序。

代码实现

void QuickSort(vector<int>& arr,int left,int right){
    
    if(left>=right) return ;
    int i = left,j = right,x = arr[left];
    while(i<j){
        while(i<j&&arr[j]>=x) j--;
        if(i<j) arr[i++] = arr[j];
        while(i<j&&arr[i]<=x) i++;
        if(i<j) arr[j--] = arr[i];
    }
    arr[i] = x;
    QuickSort(arr,left,i-1);
    QuickSort(arr,i+1,right);
    return ;
}
//快速排序优化 三数取中法
void QuickSort2 (vector<int>& nums,int left,int right){
    if(left >= right) {
        return;
    }
    int mid = left + (right - left) / 2;
    int pivot = nums[mid];
    int i = left, j = right;

    while (i <= j) {
        while (nums[i] < pivot) {
            i++;
        }
        while (nums[j] > pivot) {
            j--;
        }
        if (i <= j) {
            swap(nums[i], nums[j]);
            i++;
            j--;
        }
    }
    QuickSort2(nums, left, j);
    QuickSort2(nums, i, right);
}

标签:arr,int,void,C++,算法,vector,排序
From: https://blog.csdn.net/user120416/article/details/137410475

相关文章

  • 洛谷P1000超级玛丽游戏C++
    题目描述超级玛丽是一个非常经典的游戏。请你用字符画的形式输出超级玛丽中的一个场景。********************####....#.#..###.....##....###.......############......
  • C++(语法以及易错点2)
    1.内联函数 1.1概念 以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。​intADD(inta,intb){returna+b;}​ 1.2特性 1.inline是一种以空间换时间的做法,如果编......
  • Java实现排序算法(1)
    七大常见排序算法直接插入排序希尔排序选择排序堆排序冒泡排序快速排序归并排序下文后三种排序算法(后三种排序算法详解)直接插入排序算法描述:定义两个下标(i和j).i从第二个元素开始,j从i前面开始,进行循环,途中定义一个temp,每次循环将i下标的元素放到temp中,与......
  • C++(语法以及易错点.1)
    1.函数重载 1.1函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或类型或类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。include<iostream>usingnamespacestd;//1、参数类型不同int......
  • m基于深度学习的32QAM调制解调系统频偏估计和补偿算法matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:  2.算法涉及理论知识概要        在无线通信系统中,接收端收到的信号由于各种原因可能会存在载波频率偏差(FrequencyOffset,FO)。在32-QAM系统中,频偏会导致星座图旋转和幅度失真,严重影响解调性能。因此,准确快速地估计并......
  • C++:数组元素逆置
    问题描述:请声明一个含有5个元素的数组,并且将元素逆置。如数组中的元素为1,3,2,5,4,逆置后为4,5,2,3,1。解题思路:1.创建一个含有5个元素的数组,并将其初始化2.实现逆置    2.1记录首元素下标start    2.2记录尾元素下标end    2.3交换首尾元素    ......
  • 【Qt\C++】二维图形化故障树
    文章目录一、故障树是什么?二、相关知识点三、生成故障树1、故障树节点2、定义故障树的树状结构以及读取保存1.使用QTreeView和QStandardItemModel来显示故障树2.使用QXmlStreamReader和QXmlStreamWriter来保存故障树3、定义故障树的图形化结构1.自定义......
  • [C++][C++11][智能指针]分析详解 + 代码模拟
    目录0.智能指针三要素:)1.为什么需要智能指针?2.内存泄漏1.什么是内存泄漏?内存泄漏的危害?2.内存泄漏分类(了解)3.如何检测内存泄漏4.如何避免内存泄漏3.RAII4.智能指针原理5.auto_ptr(失败设计)6.unique_ptr7.shared_ptr1.实现原理:通过引用计数的方式来实现多个shared_ptr......
  • 蓝桥杯_省_21B_E_路径(c++)
    题目描述小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。小蓝的图由2021个结点组成,依次编号1至2021。对于两个不同的结点a,b,如果a和b的差的绝对值大于21,则两个结点之间没有边相连;如果a和b的差的绝对值小于等于21,则两个点之间......
  • 有关哈希表算法
    例题一解法(哈希表):算法思路:•如果我们可以事先将「数组内的元素」和「下标」绑定在⼀起存⼊「哈希表」中,然后直接在哈希表中查找每⼀个元素的target-nums[i],就能快速的找到「⽬标和的下标」。•这⾥有⼀个⼩技巧,我们可以不⽤将元素全部放⼊到哈希表之后......