八大排序算法

排序算法

  • 排序的概述
  • 排序的分类
    • 分为5大类:
    • 优点及缺点
    • 如何选择排序算法
  • 八种排序之间的关系:
    • 一、插入排序
      • 直接插入排序
        • 动图详解
        • 代码实现
      • 希尔排序
        • 动图详解
        • 代码实现
    • 二、交换排序
      • 冒泡排序:
        • 动图详解
        • 代码实现
      • 快速排序:
        • 动图详解
        • 代码实现
    • 三、选择排序
      • 直接选择排序
        • 动图详解
        • 代码实现
      • 堆排序
        • 动图详解
        • 代码实现
    • 四、归并排序
      • 归并排序
        • 动图详解
        • 代码实现
    • 五、桶排序
      • 基数排序
        • 动图详解
        • 代码实现
      • 计数排序
        • 动图详解
        • 代码实现
  • end

排序的概述

在这里插入图片描述

排序的分类

分为5大类:

1.插入排序(直接插入排序、希尔排序)2.交换排序(冒泡排序、快速排序)3.选择排序(直接选择排序、堆排序)4.归并排序。
5.分配排序(箱排序、基数排序)

优点及缺点

所需辅助空间最多:归并排序
所需辅助空间最少:堆排序
平均速度最快:快速排序
不稳定:快速排序,希尔排序,堆排序。

在这里插入图片描述

如何选择排序算法

1.数据的规模;
2.数据的类型;
3.数据已有的顺序。
  • 一般来说,当数据规模较小时,应选择直接插入排序或冒泡排序
  • 任何排序算法在数据量小时基本体现不出来差距。考虑数据的类型。
    • 比如如果全部是正整数,那么考虑使用桶排序为最优。
  • 考虑数据已有顺序,快排是一种不稳定的排序(当然可以改进),对于大部分排好的数据,快排会浪费大量不必要的步骤。
  • 数据量极小,而且已经基本排好序,冒泡是最佳选择。我们说快排好,是指大量随机数据下,快排效果最理想。而不是所有情况。

在这里插入图片描述

八种排序之间的关系:

在这里插入图片描述

一、插入排序

1.直接插入排序。
2.希尔插入排序。

直接插入排序

说明:
    将一个记录插入到已经排序好的有序表中。
    1.sorted数组的第0个位置没有放数据。
    2.从sorted第二个数据开始处理。
    如果该数据比它前面的数据要小,说明该数据要往前面移动。
动图详解

直接插入排序

用法:
    a.首先将该数据备份放到sorted的第0位置当哨兵。
    b.然后将该数据前面那个数据后移。
    c.然后往前搜索,找插入位置。
    d.找到插入位置之后讲第0位置的那个数据插入对应位置。

O(n*n),当待排记录序列为正序时,时间复杂度提高至O(n)。

代码实现
/**
 * 从小到大排序
 * @param l 排序起始下标位置
 * @param r    排序结束下标位置
 * @param array 要排序的数组
 */
public static void insert_sort(int l, int r, int[] array) {
    for(int i=l+1;i<=r;++i) {
        for(int j=l;j<i;++j) {
            //要插入的数
            int temp=array[i];
            //找到比要插入的数大的位置
            if(array[j]>temp) {
                //将它们全部后移一位
                for(int k=i;k>j;--k)
                    array[k]=array[k-1];
                //插入到该位置
                array[j]=temp;
                break;
            }
        }
    }
}
//也可以使用以下写法,后续的希尔排序将采用如下写法
public static void insert_sort(int l, int r, int[] array) {
    for(int i=l+1;i<=r;++i) {
        int j=i-1, temp=array[i];
        while(temp) {j>=0 && array[j]>
            array[j+1]=array[j];
            j--;
        }
        array[j+1]=temp;
    }
}

希尔排序

1.先将整个待排记录序列分割成若干个子序列分别进行直接插入排序。
2.待整个序列中的记录基本有序时,再对全体记录进行一次直接插入排序。
动图详解

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l    排序起始位置
 * @param r    排序结束位置
 * @param array 要排序的数组
 */
public static void shell_sort(int l, int r, int[] array) {
    int length=r-l+1;
    //确定每次的增量
    for(int gap=length>>1;gap>=1;gap>>=1) {
        //对每个增量分组使用插排
        for(int i=gap;i<=r;++i) {
            //i插入到它所在的增量分组
            int temp=array[i], j=i-gap;
            while(j>=0 && array[j]>temp) {
                array[j+gap]=array[j];
                j-=gap;
            }
            array[j+gap]=temp;
        }
    }
}

二、交换排序

1.冒泡排序。
2.快速排序。

冒泡排序:

该算法是专门针对已部分排序的数据进行排序的一种排序算法。
如果在你的数据清单中只有一两个数据是乱序的话,用这种算法就是最快的排序算法。
如果你的数据清单中的数据是随机排列的,那么这种方法就成了最慢的算法了。
因此在使用这种算法之前一定要慎重。
这种算法的核心思想是扫描数据清单,寻找出现乱序的两个相邻的项目。
当找到这两个项目后,交换项目的位置然后继续扫描。
重复上面的操作直到所有的项目都按顺序排好。
动图详解

冒泡排序

代码实现
/**
 * 从小到大排序
 * @param l    排序起始下标位置
 * @param r    排序结束下标位置
 * @param array 要排序的数组
 */
public static void bubble_sort(int l, int r, int[] array) {
    boolean flag = true;
    for(int i=r;i>l;--i) {
        for(int j=l;j<i;++j) {
            //若逆序就交换相邻的两个数
            if(array[j]>array[j+1]) {
                int temp=array[j];
                array[j]=array[j+1];
                array[j+1]=temp;
                flag=false;
            }
        }
        //如果一趟排序下来一次交换都没有,说明序列已经有序,无需继续排序了
        if(flag) break;
    }
}

快速排序:

通过一趟排序,将待排序记录分割成独立的两个部分,
其中一部分记录的关键字均比另一部分记录的关键字小,
则可分别对这两部分记录继续进行排序,以达到整个序列有序。
具体做法是:
 	使用两个指针lowhigh,初值分别设置为序列的头,和序列的尾,
 	设置pivotkey为第一个记录,首先从high开始向前搜索第一个小于pivotkey的记录和pivotkey所在位置进行交换,
 	然后从low开始向后搜索第一个大于pivotkey的记录和此时pivotkey所在位置进行交换,
 	重复直到low=high了为止。
动图详解

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l 排序起始位置
 * @param r    排序结束位置
 * @param array 要排序的数组
 */
public static void quick_sort(int l, int r, int[] array) {
    int i=l,j=r,mid=array[(l+r+1)>>1];
    //等号必须有为了让终止条件不为i==j,否则递归的区间会在i==j时出现重合
    while(i<=j) {
        //没有等号、不能将mid定义成(l+r+1)/2然后这里改成array[mid],因为位于mid位置的数字是可能被交换走的!
        while(array[i]<mid) i++;
        while(array[j]>mid) j--;
        if(i<=j) {
            int temp=array[i];
            array[i]=array[j];
            array[j]=temp;
            i++;j--;
        }
    }
    if(l<j) quick_sort(l,j,array);
    if(i<r) quick_sort(i,r,array);
}

三、选择排序

1.直接选择排序。
2.堆排序

直接选择排序

第i次选取到arrayLength-1中间最小的值放在i位置。
动图详解

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l 排序起始下标位置
 * @param r 排序结束下标位置
 * @param array 要排序的数组
 */
public static void select_sort(int l, int r, int[] array) {
    int temp;
    for(int i=l;i<=r-1;++i) {
        //假设最值位于最前面,也就是i位置
        int min=i;
        //选出最值
        for(int j=i+1;j<=r;++j){
            if(array[j]<array[min]) min=j;
        }
        //如果真正的最值不在i处,就交换这两个位置的数
        if(min!=i) {
            temp=array[i];
            array[i]=array[min];
            array[min]=temp;
        }
    }
}

堆排序

首先,数组里面用层次遍历的顺序放一棵完全二叉树。
从最后一个非终端结点往前面调整,直到到达根结点,
这个时候除根节点以外的所有非终端节点都已经满足堆得条件了,
于是需要调整根节点使得整个树满足堆得条件,
于是从根节点开始,沿着它的儿子们往下面走
(最大堆沿着最大的儿子走,最小堆沿着最小的儿子走)。
主程序里面,首先从最后一个非终端节点开始调整到根也调整完,形成一个heap,
然后将heap的根放到后面去
(:每次的树大小会变化,但是root都是在1的位置,以方便计算儿子们的index,
所以如果需要升序排序,则要逐步大顶堆。
因为根节点被一个个放在后面去了,降序排序则要建立小顶堆。
动图详解

在这里插入图片描述

代码实现
public class Heap {
    public int[] array;//注意下标是从1开始

    private boolean type= Lower;//默认为小根堆

    private static final int default_capacity = 11;//初始容量为11

    private int size=0;

    public static final boolean Lower = true;
    public static final boolean upper = false;

    //默认建堆
    public Heap() {
        array = new int[default_capacity];
    }
    /**
     * @param type 指定优先级
     */
    public Heap(boolean type) {
        array = new int[default_capacity];
        this.type=type;
    }
    /**
     * @param array 指定堆中的内容
     * @param size 指定堆的大小
     */
    public Heap(int[] array,int size) {
        this.array = new int[size+1];
        this.size=size;
        System.arraycopy(array,0,this.array,1,size);
        build(size);
    }
    /**
     * @param array 指定堆中的内容
     * @param size 指定堆的大小
     * @param type 指定优先级
     */
    public Heap(int[] array,int size,boolean type) {
        this.array = new int[size+1];
        this.size=size;
        this.type=type;
        System.arraycopy(array,0,this.array,1,size);
        build(size);
    }
    //交换函数
    private void swap(int x,int y,int[] a){
        int tmp=a[x];
        a[x]=a[y]; a[y]=tmp;
    }
    /**
     * 优先级比较函数
     * @param x
     * @param y
     * @return 若为Lower模式,x<y返回1;若为upper模式,x>y返回1。返回1表示x的优先级更高,返回-1表示y的优先级更高,返回0表示优先级相同。
     */
    private int compare(int x,int y) {
        if(x>y) return (type? -1:1);
        if(x<y) return (type? 1:-1);
        return 0;
    }
    //上浮操作
    private void rise(int k) {
        if(k==1) return;//到达根结点就停止上浮
        int father=k>>1;
        if(compare(array[father],array[k])!=-1) return;
        swap(father,k,array);//若父结点的优先级低于子结点,就交换它们的位置
        rise(father);//父结点继续上浮
    }
    //下沉操作
    private void sink(int k) {
        int l=k<<1,r=l+1;//左子结点,右子结点
        if(l>size) return;//到达叶子结点就停止下沉
        int t=l;//假设左子结点优先级更高
        if(r<=size) t=compare(array[l],array[r])==1? l:r;//如果右子结点存在,则比较左右子结点的优先级,取优先级更高的
        if(compare(array[k],array[t])!=-1) return;
        swap(k,t,array);//若父结点的优先级低于子结点,就交换它们的位置

        sink(t);//交换后的子结点继续下沉
    }
    //高效建堆
    private void build(int tail) {
        int k=tail/2;
        while(k!=0) {
            sink(k);
            k--;
        }
    }
    //添加元素
    public void push(int x) {
        int oldCapacity = array.length-1;
        //堆满,就执行扩容
        if(oldCapacity == size) grow(oldCapacity);
        array[++size]=x;
        rise(size);
    }
    //扩容
    private void grow(int oldCapacity) {
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));
        int[] newArray = new int[newCapacity+1];
        System.arraycopy(array,1,newArray,1,size);
        array = newArray;
    }
    //删除优先级最高的元素
    public void pop() {
        array[1]=array[size--];
        sink(1);
    }
    //获取优先级最高的元素
    public int top() {
        return array[1];
    }
    //获取长度
    public int size() {
        return size;
    }
}

四、归并排序

归并排序

将两个或两个以上的有序表组合成一个新的有序表。
归并排序要使用一个辅助数组,大小跟原数组相同,递归做法。
每次将目标序列分解成两个序列,分别排序两个子序列之后,
再将两个排序好的子序列merge到一起。
动图详解

在这里插入图片描述

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l 排序起始下标位置
 * @param r    排序结束下标位置
 * @param array 要排序的数组
 * @param assist 辅助数组
 */
public static void binary_sort(int l, int r, int[] array, int[] assist) {
    if(l==r) return;
    int mid=(l+r)>>1;
    //分成两个子序列
    binary_sort(l,mid,array,assist);
    binary_sort(mid+1,r,array,assist);
    int i=l,j=mid+1,tag=l;
    //将两边的子序列合并成一个有序的序列
    while(i<=mid && j<=r) {
        if(array[i]<=array[j]) assist[tag++]=array[i++];
        else assist[tag++]=array[j++];
    }
    //若两边子序列长度不等,就要单独复制
    while(i<=mid) assist[tag++]=array[i++];
    while(j<=r) assist[tag++]=array[j++];
    for(int k=l;k<=r;++k) array[k]=assist[k];
}

五、桶排序

基数排序

使用10个辅助队列,假设最大数的数字位数为 x,则一共做x次,
从个位数开始往前,以第i位数字的大小为依据,将数据放进辅助队列,搞定之后回收。
下次再以高一位开始的数字位为依据。
动图详解

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l 排序起始位置
 * @param r    排序结束位置
 * @param array 要排序的数组
 */
public static void radix_sort(int l, int r, int[] array) {
    int max=array[l];
    for(int i=l;i<=r;++i) if(array[i]>max) max=array[i];
    int[][] assist = new int[10][r-l+1];
    //0~9
    int[] b = new int[10];
    for(int i=0;i<10;++i) b[i]=0;
    //最大的数有几位
    int length=(max+"").length();
    //最大的数有几位就排序几次
    for(int i=0,n=1;i<length;++i,n*=10) {
        //n=1时,按个位排序,n=10时按十位排序,以此类推
        for(int j=l;j<=r;++j) {
            //0~9,rest即为b[]的下标
            int rest=array[j]/n %10;
            //array[j]被放在rest处的第b[rest]个位置
            assist[rest][b[rest]]=array[j];
            b[rest]++;
        }
        int tag=0;
        //0~9
        for(int k=0;k<10;++k) {
            if(b[k]!=0) {
                for(int t=0;t<b[k];++t)
                    //取出放在k处的第t个位置的数字,k对应放入时的rest,t对应放入时的b[rest]
                    array[tag++]=assist[k][t];
                b[k]=0;
            }
        }
    }
}

计数排序

基本思想:
待排序的数不是存放在数组里,而是作为数组的下标存放,
数组中存放该数字在序列中出现的个数,所以叫计数排序。
由于每个数组都可以类比成一个桶,数组下标就是桶的标号,
当数字等于标号时就将其放入该桶中,所以也可以叫作桶排序,
不过真正的桶排序要比这复杂
该排序需要序列中的数字在一个明显的范围内。
最终只要按顺序输出数组值不为0的下标即可,
数组值等于多少就输出几次,时间复杂度为O(n)
动图详解

在这里插入图片描述

在这里插入图片描述

代码实现
/**
 * 从小到大排序
 * @param l 排序起始位置
 * @param r    排序结束位置
 * @param array 要排序的数组
 */
public static void bucket_sort(int l, int r, int[] array) {
    int max=array[l];
    for(int i=l;i<=r;++i) if(array[i]>max) max=array[i];
    int[] assist = new int[r-l+1];
    int[] b = new int[max+1];
    for(int i=0;i<=max;++i)    b[i]=0;
    for(int i=l;i<=r;++i)  b[array[i]]++;
    //统计前缀和
    for(int i=1;i<=max;++i)    b[i]+=b[i-1];
    //逆序遍历array
    for(int i=r;i>=l;--i) {
        assist[b[array[i]]-1]=array[i];
        b[array[i]]--;
    }
    //assist只是暂时存放数据,最后仍然要将数据放回array
    for(int i=l;i<=r;++i) array[i]=assist[i];
}

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/468863.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Echo服务器学习__01(基础)

ASIO是一个跨平台&#xff0c;主要用于实现异步网络和其他一些底层I/O操作的C库 可以基于ASIO实现Echo服务端&#xff0c;在这之前&#xff0c;学习一些基础的知识和概念 ​ 1&#xff1a;IO多路复用 简单的来说&#xff0c;一个线程同时监听多个I/O事件就是I/O多路复用。任…

边缘计算+WEB端应用融合:AI行为识别智能监控系统搭建指南 -- 边缘设备图像识别及部署(二)

专栏目录 边缘计算WEB端应用融合&#xff1a;AI行为识别智能监控系统搭建指南 – 整体介绍&#xff08;一&#xff09; 边缘计算WEB端应用融合&#xff1a;AI行为识别智能监控系统搭建指南 -- 边缘图像识别及部署&#xff08;二&#xff09; 前言边缘图像识别与推流整体思路原始…

【人工智能】Gitee AI 天数智芯有奖体验开源AI模型,一定能有所收货,快来体验吧

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章。 这是《人工智能》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 目录 前言两大赛道天数智芯1.模型地址2.天数智芯专区3.选择模型4.模型详情页5.部署模型6.成功部署7.执行例子8.移除模型 千模盲…

Flutter-仿淘宝京东录音识别图标效果

效果 需求 弹起键盘&#xff0c;录制按钮紧挨着输入框收起键盘&#xff0c;录制按钮回到初始位置 实现 第一步&#xff1a;监听键盘弹起并获取键盘高度第二步&#xff1a;根据键盘高度&#xff0c;录制按钮高度计算偏移高度&#xff0c;并动画移动第三步&#xff1a;键盘收起…

Unbuntu20.04 git push和pull相关问题

文章目录 Unbuntu20.04 git push和pull使用&#xff11;&#xff0e;下载[Git工具包](https://git-scm.com/downloads)&#xff12;&#xff0e;建立本地仓库&#xff13;&#xff0e;将本地仓库与github远程仓库关联&#xff14;&#xff0e;将本地仓库文件上传到github远程仓…

nuxt3项目总结

nuxt3项目总结 仓库 前言 大半年的时间&#xff0c;项目从秋天到春天&#xff0c;从管理后台到APP再到数据大屏&#xff0c;技术栈从vue3到uniApp再到nuxt3&#xff0c;需求不停的改&#xff0c;注释掉代码都快到项目总体的三分之一。 一、准备-搭建项目架子 1.1 创建一个…

图片怎么转jpg格式?一键完成图片格式转换

jpg图片格式作为最常用的图片类型之一&#xff0c;经常出现在不同的使用场景中&#xff0c;如果遇到手上的图片不是jpg格式的话&#xff0c;就需要图片转jpg之后再操作&#xff0c;那么该如何进行图片转换格式呢&#xff1f;试试本文分享的这个图片转格式的方法吧&#xff0c;利…

【经验分享】Wubuntu------体验Windows和Ubuntu的结合体

【经验分享】Wubuntu------体验Windows和Ubuntu的结合体 最近看到有一款Wubuntu的文章&#xff0c;对于习惯使用windows操作系统&#xff0c;又不熟悉ubuntu系统的程序员小白来说&#xff0c;可以说是福音了。目前的Wubuntu兼容性可能还有一点问题&#xff0c;如果再迭代几次的…

YOLOV5 改进:增加注意力机制模块(SE)

1、前言 本章将介绍yolov5的改进项目,为v5增加新的模块---注意力机制、SE模块 大部分更改的代码是重复的,只有少部分需要更改,下面会详细讲解 yolov5的yaml文件介绍:YOLOV5 模型:利用tensorboard查看网络结构和yaml文件介绍-CSDN博客 yolov5的模块更改,C3更改为C2f模块…

18个惊艳的可视化大屏(第27辑):安防与安全预警

可视化大屏在安防和安全预警领域具有以下几个重要作用&#xff1a; 实时监控和预警 可视化大屏可以将各种安防设备&#xff08;如摄像头、传感器等&#xff09;的监控画面和数据集中显示在一个屏幕上&#xff0c;实现对安全状况的实时监控。同时&#xff0c;通过设置预警规则…

【保姆级教程】YOLOv8_Pose多目标+关键点检测:训练自己的数据集

Yolov8官方给出的是单类别的人体姿态关键点检测&#xff0c;本文将记录如果实现训练自己的多类别的关键点检测。 一、YOLOV8环境准备 1.1 下载安装最新的YOLOv8代码 仓库地址&#xff1a; https://github.com/ultralytics/ultralytics1.2 配置环境 pip install -r requiremen…

JNDI+LDAP攻击手法

服务端&#xff1a; package com.naihe3; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL;import javax.net.ServerSocketFactory; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory;import com.unboundid.…

27-4 文件上传漏洞 - 黑名单绕过

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、黑名单绕过和黑白名单机制: 黑名单:黑名单中的文件不允许通过。白名单:白名单中的文件允许通过。二、黑白名单判断: 当输入一串后缀如"sfahkfhakj"时,黑名单不…

docker安装配置dnsmasq

docker下载安装 参考&#xff1a;docker安装、卸载、配置、镜像 如果是低版本的额ubuntu&#xff0c;比如ubuntu16.04.7 LTS&#xff0c;为了加快下载速度&#xff0c;参考&#xff1a;Ubuntu16.04LTS安装Docker。 docker安装dnsmasq 下载dnsmasq镜像 首先镜像我们可以选择…

Solidity Uniswap V2 Output amount calculation

现在&#xff0c;我们即将实现高级交换&#xff0c;包括链式交换&#xff08;例如&#xff0c;通过token B 将token A 交换为token C&#xff09;。在实现之前&#xff0c;我们需要了解 Uniswap 如何计算输出量。让我们先弄清楚金额与价格的关系。 什么是价格&#xff1f;就是你…

KVM安装-kvm彻底卸载-docker安装Webvirtmgr

KVM安装和使用 一、安装 检测硬件是否支持KVM需要硬件的支持,使用命令查看硬件是否支持KVM。如果结果中有vmx(Intel)或svm(AMD)字样,就说明CPU的支持的 egrep ‘(vmx|svm)’ /proc/cpuinfo关闭selinux将 /etc/sysconfig/selinux 中的 SELinux=enforcing 修改为 SELinux=d…

工业智能网关的功能特点、应用及其对企业产生的价值-天拓四方

一、工业智能网关的功能特点 工业智能网关是一种具备数据采集、传输、处理能力的智能设备&#xff0c;它能够将工业现场的各种传感器、执行器、控制器等设备连接起来&#xff0c;实现设备间的信息互通与协同工作。同时&#xff0c;工业智能网关还具备强大的数据处理能力&#…

超火短剧分销推广项目cps,现在做还不晚(完整教程)

短剧是一种介于短视频和长视频之间的中视频模式&#xff0c;以爽点和反转为特点&#xff0c;讲究引人入胜&#xff0c;刺激消费。更白话一点表达&#xff0c;短剧就是压缩版的电视剧&#xff0c;易上头上瘾&#xff0c;易冲动消费。 所以&#xff0c;使用“蜂小推”进行短剧分…

xss.pwnfunction(DOM型XSS)靶场

环境进入该网站 Challenges (pwnfunction.com) 第一关&#xff1a;Ma Spaghet! 源码&#xff1a; <!-- Challenge --> <h2 id"spaghet"></h2> <script>spaghet.innerHTML (new URL(location).searchParams.get(somebody) || "Somebo…