【排序】八大排序算法简介及它们各自的特点总结

2023-11-07

概述:

一般使用的八大排序算法是:插入排序、选择排序、冒泡排序、希尔排序、归并排序、快速排序、堆排序、基数排序,每个方法有其适合的使用场景,可以根据具体数据进行选择.
几个概念:
内部排序:排序期间元素全部存放在内存中的排序;
外部排序:排序期间元素无法全部存放在内存中,必须在排序过程中根据要求不断地进行内外存之间移动地排序;
(这八种排序算法中除了多路归并排序是外部排序,其他都是内部排序)
稳定性:指的是经过排序后,值相同的元素保持原来顺序中的相对位置不变.

各算法时间复杂度、空间复杂度、所需辅助空间与稳定性如下:
在这里插入图片描述

实现方法(这里都是进行从小到大的排序):

1.冒泡排序:

冒泡排序思想很简单,就是对每个下标i,取j从0到n-1-i(n是数组长度)进行遍历,如果两个相邻的元素s[j]>s[j+1],就交换,这样每次最大的元素已经移动到了后面正确的位置.

void bubbleSort(vector<int> &s){
    for (int i = 0; i < s.size(); i++){
        for (int j = 0; j < s.size() - 1 - i; j++){
            if (s[j] > s[j + 1])  swap(s[j], s[j + 1]);
        }
    }
}
2.插入排序:

插入排序又分为简单插入排序和折半插入排序;简单插入排序思想是每趟排序把元素插入到已排好序的数组中,折半插入排序是改进的插入排序,由于前半部分为已排好序的数列,这样我们不用按顺序依次寻找插入点,可以采用折半查找的方法来加快寻找插入点的速度.

简单插入排序:
void insertSort(vector<int> &s){
    if (s.size() > 1){
        for (int i = 1; i < s.size(); i++){
            for (int j = i; j > 0 && s[j] < s[j - 1]; j--){
                swap(s[j], s[j - 1]);
            }
        }
    }
}
折半插入排序:
void binaryInsertSort(vector<int> &s){
    int low, high, m, temp, i, j;
    for (i = 1; i < s.size(); i++){
        //采用折半查找方法,寻找应该插入的位置
        low = 0;
        high = i - 1;
        while (low <= high){
            m = (low + high) / 2;
            if (s[m] > s[i])  high = m - 1;
            else  low = m + 1;
        }
        //统一移动元素,将该元素插入到正确的位置
        temp = s[i];
        for (j = i; j > high + 1; j--){
            s[j] = s[j - 1];
        }
        s[high + 1] = temp;
    }
}

插入排序的特点:
1.稳定;
2.最坏情况下比较n*(n-1)/2次,最好情况下比较n-1次;
3.第k次排序后,前k个元素已经是从小到大排好序的

3.简单选择排序:

选择排序思想是对每个下标i,从i后面的元素中选择最小的那个和s[i]交换.

void selectSort(vector<int> &s){
    if(s.size()>1){
        for(int i=0;i<s.size();i++){
            int min=i;
            for(int j=i+1;j<s.size();j++){
                if(s[j]<s[min])  min=j;
            }
            swap(s[i], s[min]);
        }
    }
}

选择排序的特点:
1.不稳定;
2.每趟排序后前面的元素肯定是已经排好序的了,每次排序后可以确定一个元素会在其最终位置上.

4.快速排序:

快速排序是内排序中平均性能较好的排序,思想是每趟排序时选取一个数据(通常用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它的左边,所有比它大的数都放到它的右边.

void quickSort(vector<int> &s, int low, int high){
    if (low >= high)  return;
    int l = low, r = high, val = s[low];
    while (l < r){
        while (l < r&&s[r] >= val)  r--;
        if (l < r)  s[l++] = s[r];
        while (l < r&&s[l] <= val)  l++;
        if (l < r)  s[r--] = s[l];
    }
    s[l] = val;
    quickSort(s, low, l - 1);
    quickSort(s, r + 1, high);
}

快速排序的特点:
1.不稳定;
2.快速排序过程中不会产生有序子序列,但每一趟排序后都有一个元素放在其最终位置上;
3.每次选择的关键值可以把数组分为两个子数组的时候,快速排序算法的速度最快,当数组已经是正序或逆序时速度最慢;
4.递归次数与每次划分后得到的分区的处理顺序无关;
5.对n个关键字进行快速排序,最大递归深度为n,最小递归深度为log2n;

上面的快速排序算法是递归算法,非递归算法使用栈来实现:

//进行区域的划分
int partition(vector<int> &s, int low, int height){
    int val = s[low];
    while (low < height){
        while (low < height&&s[height] >= val)
            height--;
        s[low] = s[height];
        while (low < height&&s[low] <= val)
            low++;
        s[height] = s[low];
    }
    s[low] = val;
    return low;
}
//排序
void quickSortNonRecursive(vector<int> &s, int low, int height){
    stack<int> p;
    if (low < height){
        int mid = partition(s, low, height);
        if (mid - 1 > low){
            p.push(low);
            p.push(mid - 1);
        }
        if (mid + 1 < height){
            p.push(mid + 1);
            p.push(height);
        }
        while (!p.empty()){
            int qHeight = p.top();
            p.pop();
            int pLow = p.top();
            p.pop();
            int pqMid = partition(s, pLow, qHeight);
            if (pqMid - 1 > pLow){
                p.push(pLow);
                p.push(pqMid - 1);
            }
            if (pqMid + 1 < qHeight){
                p.push(pqMid + 1);
                p.push(qHeight);
            }
        }
    }
}
5.希尔排序:

希尔排序是基于插入排序的一种排序算法,思想是对长度为n的数组s,每趟排序基于间隔h分成几组,对每组数据使用插入排序方法进行排序,然后减小h的值,这样刚开始时候虽然分组比较多,但每组数据很少,h减小后每组数据多但基本有序,而插入排序对已经基本有序的数组排序效率较高.

void shellSort(vector<int> &s){
    int n = s.size(), h = 1;
    while (h < n / 3)  h = 3 * h + 1;
    while (h >= 1){
        for (int i = h; i < n; i++){
            for (int j = i; j >= h && s[j] < s[j - h]; j -= h){
                swap(s[j], s[j - h]);
            }
        }
        h /= 3;
    }
}

希尔排序的特点:
1.不稳定;
2.每次排序后不能保证有一个元素在最终位置上

6.归并排序:
const int maxn=500000,INF=0x3f3f3f3f;
int L[maxn/2+2],R[maxn/2+2];
void merge(vector<int> &a,int n,int left,int mid,int right){
    int n1=mid-left,n2=right-mid;
    for(int i=0;i<n1;i++)  L[i]=a[left+i];
    for(int i=0;i<n2;i++)  R[i]=a[mid+i];
    L[n1]=R[n2]=INF;
    int i=0,j=0;
    for(int k=left;k<right;k++){
        if(L[i]<=R[j])  a[k]=L[i++];
        else  a[k]=R[j++];
    }
}
void mergesort(vector<int> &a,int n,int left,int right){
    if(left+1<right){
        int mid=(left+right)/2;
        mergesort(a,n,left,mid);
        mergesort(a,n,mid,right);
        merge(a,n,left,mid,right);
    }
}

归并排序特点:
1.稳定;
2.可以用在顺序存储和链式存储的结构;
3.时间复杂度在最好和最坏情况下都是O(nlogn)

7.堆排序:

堆排序是基于选择排序的一种排序算法,堆是一个近似完全二叉树的结构,且满足子结点的键值或索引总是小于(或者大于)它的父节点。这里采用最大堆方式:位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,堆要时刻保持这样的结构,所以一旦堆里面的数据发生变化,要对堆重新进行一次构建。

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;
        }
    }
}

void heap_sort(vector<int> &arr, int len) {
    //初始化,i从最後一个父节点开始调整
    for (int i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    //先将第一个元素和已经排好的元素前一位做交换,再从新调整(刚调整的元素之前的元素),直到排序完毕
    for (int i = len - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

堆排序特点:
1.不稳定;
2.最坏,最好,平均时间复杂度均为O(nlogn)

8.基数排序:

基数排序是一种非比较型整数排序算法,其原理是将数据按位数切割成不同的数字,然后按每个位数分别比较,在类似对百万级的电话号码进行排序的问题上,使用基数排序效率较高

//寻找数组中最大数的位数作为基数排序循环次数
int KeySize(vector<int> &s, int n){
    int key = 1;
    for (int i = 0; i < n; i++){
        int temp = 1;
        int r = 10;
        while (s[i] / r > 0){
            temp++;
            r *= 10;
        }
        key = (temp > key) ? temp : key;
    }
    return key;
}

//基数排序
void RadixSort(vector<int> &s, int n){
    int key = KeySize(s, n);
    int bucket[10][10] = { 0 };
    int order[10] = { 0 };
    for (int r = 1; key > 0; key--, r *= 10){
        for (int i = 0; i < n; i++){
            int lsd = (s[i] / r) % 10;
            bucket[lsd][order[lsd]++] = s[i];
        }
        int k = 0;
        for (int i = 0; i < 10; i++){
            if (order[i] != 0){
                for (int j = 0; j < order[i]; j++)
                    s[k++] = bucket[i][j];
            }
            order[i] = 0;
        }
    }
}

基数排序特点:
1.稳定;
2.时间复杂度为O (nlog®m),其中r为所采取的基数,m为堆数
3.在某些时候,基数排序法的效率高于其它的稳定性排序法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【排序】八大排序算法简介及它们各自的特点总结 的相关文章

随机推荐

  • 华为OD机试 - 简易内存池(Python)

    题目描述 请实现一个简易内存池 根据请求命令完成内存分配和释放 内存池支持两种操作命令 REQUEST和RELEASE 其格式为 REQUEST 请求的内存大小 表示请求分配指定大小内存 如果分配成功 返回分配到的内存首地址 如果内存不足
  • 华为OD机试题-java-华为机试题及答案

    1 通过键盘输入任意一个字符串序列 字符串可能包含多个子串 子串以空格 分隔 请编写一个程序 自动分离出各个子串 并使用 将其分隔 并且在最后 也补充一个 并将子串存储 测试 输入 abc def gh i d 输出 abc def gh
  • 数据更新——数据的更新

    UPDATE语句的基本语法 1 改变表中数据的UPDATE语句 UPDATE lt 表名 gt SET lt 列名 gt lt 表达式 gt 2将更新对象的列和更新后的值都记述在 SET 子句中 指定条件的UPDATE语句 1 更新部分数据
  • 【Linux】imx6ull Kernel 源码下载和编译环境配置

    文章目录 1 获取 ARM 官方交叉编译工具链 2 在IMX官网下载IMX6ULL相关资料 3 下载源码和编译kernel 4 编译验证kernel 5 相关文章 1 获取 ARM 官方交叉编译工具链 在ubuntu linux环境下编译i
  • Shell--基础--08--echo命令

    Shell 基础 08 echo命令 1 介绍 用于字符串的输出 格式如下 echo string 2 显示普通字符串 root zhoufei echo aaa bbb aaa bbb root zhoufei 双引号可以省略 root
  • Node事件环 JS单线程 阻塞与非阻塞

    NodeJS 1 基于Chrome V8引擎的JS运行环境 2 让JS能运行在服务端 3 Node运行环境只包含JS中的ES部分 Node模块和NodeAPI 4 事件驱动 事件完成通知 异步 5 非阻塞式I O 异步的输入输出 6 外部依
  • 虚拟机装的linux,打开终端后无法使用ifconfig命令查看ip地址

    先在root用户下setup 然后选择里面的network configure那一个选项 按空格选中第一行 中的内容 保存在退出 重启
  • Depth Peeling浅析

    详情见 Interactive Order Independent Transparency 概述 利用shadowmap技术实现不计算折射的透明度渲染 1 对不同的层利用shadowmap渲染 Layer 0 Layer 1 Layer
  • 如何dump SKP,SKP抓取

    1 如何dump SKP 我们知道绘制的操作 主要都是在SkiaPipline renderframe中进行的 frameworks base libs hwui pipeline skia SkiaPipeline cpp 429 voi
  • glog/log_severity.h :找不到

    vs2017添加了GLOG NO ABBREVIATED SEVERITIES还是没用 后来去githup问题中也没找到 问题解决 cmake是默认的 不在build的头文件 src下面 干 把他copy到你项目的include下面就好了
  • el-upload上传图片以后获取图片的宽、高、大小、名字。。。

    template
  • Windows7 64位下vs2008配置OpenCV2.3.1

    1 下载OpenCV2 3 1 http www opencv org cn index php Download 2 下载后解压缩 OpenCV 2 3 1 win superpack exe 生成一个opencv文件夹 3 下载CMak
  • Shell信号发送与捕捉

    9 1 Linux信号类型 信号 Signal 信号是在软件层次上对中断机制的一种模拟 通过给一个进程发送信号 执行相应的处理函数 进程可以通过三种方式来响应一个信号 1 忽略信号 即对信号不做任何处理 其中有两个信号不能忽略 SIGKIL
  • 转载:WebSocket 原理介绍及服务器搭建

    文章非原创 转载自 http blog csdn net yl02520 article WebSocket 1 WebSocket API简介 WebSocket是html5新增加的一种通信协议 目前流行的浏览器都支持这个协议 例如Chr
  • 如何修改Tomcat端口号

    1 首先需要了解Tomcat默认的端口号是 8080 2 点击进入 Tomcat 目录下的 conf 目录 找到 server xml 配置文件并打开 3 找到Connector标签 修改port属性为你想要的端口号 端口号范围 1 655
  • 第零课 python与pycharm的安装

    首先安装anaconda 安装好Anaconda之后创建一个python环境 然后安装pycharm 在Pycharm中选择好Anaconda中创建的环境 这样就完成了程序环境的安装与配置
  • 在 Visual Studio 中使用 Qt 开发桌面应用的环境配置

    本文阐述在Visual Studio 2019中建立Qt项目的方法 Visual Studio 的安装 官网下载地址 Visual Studio 面向软件开发人员和 Teams 的 IDE 和代码编辑器 下载Community版本即可 安装
  • 应用内版本更新库UpdateVersion

    应用内版本更新库UpdateVersion UpdateVersion是一个Android版本更新库 GitHub仓库地址 引入 gradle allprojects repositories maven url https jitpack
  • ddl是什么意思网络语_ddl是什么

    数据库模式定义语言并非程序设计语言 DDL数据库模式定义语言是SQL语言 结构化查询语言 的组成部分 SQL语言包括四种主要程序设计语言类别的语句 数据定义语言 DDL 数据操作语言 DML 数据控制语言 DCL 和事务控制语言 TCL 那
  • 【排序】八大排序算法简介及它们各自的特点总结

    概述 一般使用的八大排序算法是 插入排序 选择排序 冒泡排序 希尔排序 归并排序 快速排序 堆排序 基数排序 每个方法有其适合的使用场景 可以根据具体数据进行选择 几个概念 内部排序 排序期间元素全部存放在内存中的排序 外部排序 排序期间元