三种简单排序(冒泡、插入、选择)的比较和图解

2023-05-16

冒泡排序

这种排序方式是最容易理解的,主体思想就是:

指针重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。

接下来我们来看看冒泡排序的图解,这里一个推荐网站:https://visualgo.net/ ,里面有很多算法实现的动画,可以对运行过程有一个更直观的了解):
这里写图片描述

冒泡排序一次只比较两个数字,并且这两个数字是相邻的。
从图上我们可以看到,原数组为【30,8,15,18,12】。
第一次比较首先比较第0位和第一位,并且将结果大的数字往后移动,也就是交换。第0位是40, 第1 位是8,40>8,所以我们将大的数字往后移动。
同理,如果第0位<第1 位,我们就不进行交换。

之后我们在比较第1位和第2位的数字。将结果大的往后移动。

就这样,通过n-1次比较后(n为数组长度),我们就将最大的数字移到了数组的最末端。然后再继续进行第二轮比较,找出第二大的数字,往后以此类推。

一共需要比较的次数为: (n-1)+(n-2)+(n-3)+·····+1;

以下是JAVA代码实现:

public class BubbleSort {
    public static void sort(int[] a) {
        for(int i=a.length-1;i>=0;i--){ //i控制比较的轮数
            for(int j=0;j<i;j++){ //j每一轮比较的次数
                if(a[j]>a[j+1]){
                    a[j]=a[j]^a[j+1];
                    a[j+1]=a[j]^a[j+1];
                    a[j] = a[j]^a[j+1];
                }
            }
        }
    }
}

选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

我们来看一下图解:

这里写图片描述

选择排序就是对数组中的元素进行比较选择,然后直接放置在排序后的位置。
首先指针K先指向数组0号位置,K相当于指明一个目标位置。然后另一个指针min从K开始,往后一次比较,找到最小的值,并存储在min中,比较了一轮后,min中存储的数就是整个数组中最小的数字。这是直接将min中的数字和K指向的数字交换即可。然后找到数组中第二小的数,让他跟数组中第二个元素交换一下值,以此类推。

JAVA代码实现:

public class StraightSelectionSort {
    public static void sort(int a[]) {  
        int k=0;
        for(int i=0;i<a.length-1;i++){
            k=i;
            for(int j=i+1;j<a.length;j++){
                if(a[j]<a[k]){
                    k=j;
                }
            }
            if(k!=i){
                a[k] = a[k]^a[i];
                a[i] = a[k]^a[i];
                a[k] = a[k]^a[i];
            }
        }
    }
}

插入排序

要求在这个已经排好的数据序列中插入一个数,但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法,插入排序的基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。

  • 直接插入排序

直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。

我们来看一下图解:
这里写图片描述

这张图虽然只有一步,但是很好的表现了插入排序的运行过程。
例如,已知待排序的一组记录是:
1,3,5,8,2,1
加入之前的1,3,5,8已经按照直接插入排序排序好了,现在我们需要对2进行排序。 首先将2存在一个临时变量temp中, 然后指针从2的前一位开始判断,如果前一位比2大,则将前一位往后移,以此类推,直到找到比2小的元素。这时将2插入进去。

JAVA代码实现:

public static void StraightInsertSort(int[] a) {
        int temp = 0,j = 0;
        for(int i = 1;i < a.length;i++){
            temp = a[i];
            j = i;
            while(j > 0 && a[j-1] >= temp){
                a[j] = a[j-1];
                j--;
            }
            a[j] = temp;
        }
    }
  • 二分插入排序

将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法。在处理A[i]时,A[0]……A[i-1]已经按关键码值排好序。所谓折半比较,就是在插入A[i]时,取A[i-1/2]的关键码值与A[i]的关键码值进行比较,如果A[i]的关键码值小于A[i-1/2]的关键码值,则说明A[i]只能插入A[0]到A[i-1/2]之间,故可以在A[0]到A[i-1/2-1]之间继续使用折半比较;否则只能插入A[i-1/2]到A[i-1]之间,故可以在A[i-1/2+1]到A[i-1]之间继续使用折半比较。如此担负,直到最后能够确定插入的位置为止。一般在A[k]和A[r]之间采用折半,其中间结点为A[k+r/2],经过一次比较即可排除一半记录,把可能插入的区间减小了一半,故称为折半。执行折半插入排序的前提是文件记录必须按顺序存储。

JAVA代码实现:

public static void binaryInsertSort(int array[],int size)  
    {  
        int i,j,k,temp;  
        for(i=1;i<size;i++)  
        {  
            temp=array[i];  
            if(array[i]<array[0])  
                k=0;  
            else  
                k = binarySearch(array,0,i,temp);  

            for(j=i;j>k;j--)  
            {  
                array[j]=array[j-1];  
            }  
            array[k]=temp;  
            System.out.println(Arrays.toString(array));  
        }  
    }
  • 希尔排序

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminshing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

下面我们来看图解:

希尔排序

希尔排序意在减少直接排序中的数字移动次数,通过设置一个间隔,每次进行比较的都是间隔的数字。
比如说如图:
第一次的间隔是5,则可以将数字分为5组,每一组两个数字,将这两个数字进行比较,逆序就交换。这样我们第一次排序就完成了。
这时候的数组肯定还是存在许多的逆序对,所以我们需要将间隔缩小,在进行比较。如图,这时候间隔变成了3,也就是可以将4个数字分为一组比较,这时候可以使用直接插入排序的思想,在组内进行直接插入排序比较。只是跨度为间隔数而不是1了。
之后以此类推,直到间隔为1的时候,这时候就直接是直接选择排序了。

关于间隔的选择

这个间隔怎么确定,是个数学难题,至今没有解答。但是通过大量的实验,还是有个经验值。

减小间隔

上面已经演示了以5为初始间隔对包含10个数据项的数组进行排序的情况。对于更大的数组开始的间隔也应该更大。然后间隔不断减小,直到间隔变成1。

举例来说,含有1000个数据项的数组可能先以364为增量,然后以121为增量,以40为增量,以13为增量,以4为增量,最后以 1为增量进行希尔排序。用来形成间隔的数列被称为间隔序列。这里所表示的间隔序列由Knuth提出,此序列是很常用的。数列以逆向形式从1开始,通过递归表达式

h=3*b+1

来产生,初始值为1。

在排序算法中,首先在一个短小的循环中使用序列的生成公式来计算出最初的间隔。h值最初被赋为1,然后应用公式h=3*h+1生成序列1,4,13,40,121,364,等等。当间隔大于数组大小的时候,这个过程停止。对于一个含有1000个数据项的数组,序列的第七个数字,1093就太大了。因此,使用序列的第六个数字作为最大的数字来开始这个排序过程,作364-增量排序。然后,每完成一次排序全程的外部循环,用前面提供的此公式倒推式来减小间隔:

h=(h-1)/3

这个倒推的公式生成逆置的序列364,121,40,13,4,1。从364开始,以每一个数字作为增量进行排序。当数组用1-增量排序后,算法结束。

希尔排序比插入排序快很多,它是基于什么原因呢?当h值大的时候,数据项每一趟排序需要移动元素的个数很少,但数据项移动的距离很长。这是非常有效率的。当h减小时,每一趟排序需要移动的元素的个数增多,但是此时数据项已经接近于它们排序后最终的位置,这对于插入排序可以更有效率。正是这两种情况的结合才使希尔排序效率那么高。

注意后期的排序过程不撤销前期排序所做的工作。例如,已经完成了以40-增量的排序的数组,在经过以13-增量的排序后仍然保持了以40-增量的排序的结果。如果不是这样的话,希尔排序就无法实现排序的目的。

选择间隔序列可以称得上是一种魔法。至此只讨论了用公式h=h*3+1生成间隔序列,但是应用其他间隔序列也取得了不同程序的成功,只是一个绝对的条件,就是逐渐减小的间隔最后一定要等于1,因此最后一趟排序是一次普通的插入排序。

在希尔的原稿中,他建议初始的间距为N/2,简单地把每一趟排序分成了两半。因此,对于N=100的数组逐渐减小的间隔序列为50,25,12,6,3,1。这个方法的好处是不需要在不开始排序前为找到初始的间隔而计算序列;而只需要用2整除N。但是,这被证明并不是最好的数列。尽管对于大多数的数据来说这个方法还是比插入排序效果好,但是这种方法有时会使运行时间降到O(N2),这并不比插入排序的效率更高。

这个方法的一个变形是用2.2而非2来整除每一个间隔。对于N=100的数组来说,会产生序列45,20,9,4,1。这比用2整除显著改善了效果,因为这样避免了某些导致时间复杂度为O(N2)的最坏情况的发生。不论N为何值,都需要一些额外的代码来保证序列的最后取值为1。

JAVA代码实现:

public static void sort(int[] a) {
        int h = 1;
        while (h < a.length / 3) {
            h = h * 3 + 1;
        }
        int temp = 0, j = 0;
        while (h >= 1) {
            for (int i = h; i < a.length; i++) {
                temp = a[i];
                for (j = i; j >= h && a[j - h] >= temp; j -= h) {
                    a[j] = a[j - h];
                }
                a[j] = temp;
            }
            h = (h - 1) / 3;
        }
    }

几种排序算法的时间比较

这里写图片描述

这里的稳定性意思是,拿选择排序举例子。选择排序是给每个位置选择当前元素最小的,比如给第一个位置选择最小的,在剩余元素里面给第二个元素选择第二小的,依次类推,直到第n-1个元素,第n个元素不用选择了,因为只剩下它一个最大的元素了。那么,在一趟选择,如果一个元素比当前元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中两个5的相对前后顺序就被破坏了,所以选择排序是一个不稳定的排序算法。

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

三种简单排序(冒泡、插入、选择)的比较和图解 的相关文章

  • base64文件上传Java解析表格并实例化

    excel表格文件怎么解析 xff1f 别慌 xff0c Apache早已有解决方式 以下 xff0c 从应用的角度实现Excel文件上传并解析 语言 xff1a Java Vue 1 文件转base64格式传输 span class to
  • 洛谷 P1605 迷宫

    题目 题目背景 迷宫 问题描述 给定一个N M方格的迷宫 xff0c 迷宫里有T处障碍 xff0c 障碍处不可通过 给定起点坐标和 终点坐标 xff0c 问 每个方格最多经过1次 xff0c 有多少种从起点坐标到终点坐标的方案 在迷宫 中移
  • 输入一个百分制的成绩,将其转换成对应的等级。c++

    输入一个百分制的成绩t xff0c 将其转换成对应的等级 xff0c 具体转换规则如下 xff1a 90 100为A 80 89为B 70 79为C 60 69为D 0 59为E 输入数据有多组 xff0c 每组占一行 xff0c 由一个整
  • 对于给定的一个字符串,统计其中数字字符出现的次数。c++

    对于给定的一个字符串 xff0c 统计其中数字字符出现的次数 输入数据有多行 xff0c 第一行是一个整数n xff0c 表示测试实例的个数 xff0c 后面跟着n行 xff0c 每行包括一个由字母和数字组成的字符串 对于每个测试实例 xf
  • Boost库的编译

    Boost的编译 一 编译环境 Win7 sp1 64位旗舰版 43 VS2008 sp1 43 boost 1 63 二 下载 boost http www boost org users history version 1 63 0 h
  • 给定一段连续的整数,求出他们中所有偶数的平方和以及所有奇数的立方和。c++

    给定一段连续的整数 xff0c 求出他们中所有偶数的平方和以及所有奇数的立方和 输入数据包含多组测试实例 xff0c 每组测试实例包含一行 xff0c 由两个整数m和n组成 对于每组输入数据 xff0c 输出一行 xff0c 应包括两个整数
  • 虚拟机和宿主机之间无法拖拽/安装增强功能失败,用共享文件夹来做替代

    一般为了免去配置作业环境的麻烦 xff0c 很多课程都会要求使用虚拟机 xff0c 使用虚拟机挂载给定的虚拟硬盘文件后就不用配置环境了 安装虚拟机 xff0c 配置虚拟机一般都是比较容易的 xff0c 但是安装增强功能真的爪巴 一般有两种方
  • SPFA思路讲解,如何判断最短路中是否存在负权环路,例题(计算过路税收)。c++

    Bellman Ford和SPFA思路 在解单源最短路径的时候第一个想法是dijkstra 但是dijkstra存在一定的局限性 xff0c 图中存在负权边的时候没有办法保证它的正确性 xff0c 为了解决相应的问题 xff0c 使用bel
  • visual studio2019 +配置OpenGL

    下载VS2019 到官网https visualstudio microsoft com zh hans downloads 选择下载社区版 安装时勾选 c 43 43 模块 xff0c 注意 vs 组件占用较大 xff0c 目的地址要有充
  • 拯救C盘:转移虚拟内存

    安装文件默认路径一时爽 事后清理磁盘火葬场 C盘爆红的那天我记得很清楚 xff0c 是我安装完了VS studio 2019 xff0c 但那时的我并没在意 xff0c 直到我有一天打开虚拟机用着用着 xff0c 虚拟机暂停报错磁盘问题 这
  • 计算无法被整除的第k大正整数

    题面 xff1a 任务给定两个数字 xff0c 分别表示 n 和 k xff0c 要求给出无法被 n 整除的第 k 大的正整数 例如 n 61 3 xff0c k 61 7 xff0c 则前 7 个无法被 n 整除的正整数为 1 2 4 5
  • 血与泪的教训:127.0.0.1由于目标积极拒绝,无法连接

    在尝试python socket编程的时候 xff0c 首先将编写好的客户端和服务器端都部署在本地进行 xff0c 并且使用同一台宿主机 客户端使用127 0 0 1 xff0c 服务器端使用0 0 0 0或者直接 刚开始我端口是随意选择的
  • 在ubuntu上安装charm-crypto

    在ubuntu上安装charm crypto 在google groups上看到相关安装信息 xff0c 现在均无法完全在windows上运行charm crypto xff0c 所以选择在虚拟机上进行环境的配置 ubuntu 环境 使用
  • 论文阅读 :A survey of visual analytics techniques for machine learning

    题目 xff1a A survey of visual analytics techniques for machine learning A survey of visual analytics techniques for machin
  • 使用Django管理员在后台添加数据库时出现no such table: main.auth_user__old解决方法

    在学习Django开发时 xff0c 创建好管理员账号后 xff0c 准备通过Django内置的管理网页来测试能否在表中添加记录 xff0c 选择save后出现如上图所示的报错 解决方案是升级Django的版本 xff0c 原Django
  • nano 命令

    Nano命令指南 打开文件与新建文件 使用nano打开或新建文件 xff0c 只需键入 xff1a 代码 1 1 打开或新建文件 nano 文件名 Nano是一种单模式编辑器 xff0c 你可以直接输入文字 如果你要编辑一个像 etc fs
  • 数据处理时踩坑总结【持续更新版】

    DataFrame的iterrows迭代中无法直接修改源数据 在iterrows中 xff0c 尝试使用index和row对DataFrame类型的变量直接进行更改 xff0c 但是输出时发现值没有改变 这是因为使用row 列名 修改的值是
  • 单片机串口通讯产生乱码

    64 有关串口通讯乱码 今天做了一个51单片机的proteus仿真实验 xff0c 用到串口通信 xff0c 但是无论怎么调试都是输出乱码 一般产生乱码都是因为波特率不对 xff0c 可能你所用的晶振 以及定时器T1产生的波特率 xff0c

随机推荐

  • Ubuntu双系统安装(一次安装成功)

    Ubuntu双系统安装主要有关键地两步 xff1a 一 制作启动硬盘 二 为Ubuntu分配磁盘空间 第二点是安装过程中非常重要的一步 制作启动硬盘 xff1a 1 下载Ubuntu LTS xff0c 可以去官网下载 2 下载UltraI
  • linux环境下,一步步教你命令行搭建自己的git服务器和客户端

    前言 xff1a 先说下我的git服务器环境 xff0c git服务端的搭建我用的是阿里的ubantu云服务器 xff0c 毕竟云服务器上可以在任何联网的电脑上访问嘛 xff0c 方便 局域网也可以 xff0c svn和git这两种最常用的
  • Windows中的WSL(子系统)开机启动配置

    常规做法 通常在Linux中开机启动可以通过 1 编辑 etc rc loacl 2 在 etc init d 下添加启动脚本 3 配置systemd 但这几种方式在子系统中无法使用 xff0c 我们可以通过Windows 间接的启动子系统
  • STM32串口控制LED灯的亮灭

    STM32中的串口控制LED灯的亮灭 xff0c 分为两种方式 xff0c 一种是直接发送数字0和1来控制灯的亮灭 xff0c 另一种是通过发送字符串来控制 我所使用的开发板主控芯片是STM32F401RET6 xff0c 主频84MHz
  • Windows使用快捷键

    Windows电脑快捷键汇总 Windows电脑快捷键汇总1 win快捷键 xff1a 2 Ctr快捷键 xff08 文本编辑使用较多 xff09 3 ALT快捷键4 shift快捷键5 FN快捷键6 常规快捷键7 指法练习 Windows
  • 基于Centos7搭建Samba服务

    准备两台安装好centos7的虚拟机 服务器端172 16 1 11 24 客户端172 16 1 12 24 root 64 server yum install samba samba client y 服务器端安装软件包 xff08
  • 【BUG】【Raspberry】解决最新版树莓派远程连接蓝/黑屏不显示问题

    文章目录 一 bug如图二 解决办法三 参考四 请教问题 一 bug如图 远程连接登陆后全蓝色 xff0c 没有树莓派桌面 二 解决办法 1 执行下面代码 xff0c 删除两个文件目录 注意 xff1a pi 替换为自己的用户名 xff01
  • 【教程】【记录】树莓派Raspberry+motion+摄像头实现拍照、录像、实时视频功能

    刚接触树莓派 xff0c 还请多多指教 目录 一 准备工作二 操作步骤1 进入设置打开摄像机模块2 拍照3 录像4 实时监控 三 总结补充文章 xff1a 一 准备工作 1 树莓派4B 2 树莓派摄像头500W像素 xff08 淘宝十几块钱
  • linux查看日志命令

    常用的几种linux查看日志的命令 一 tail n 是显示行号 xff1b 相当于nl命令 xff1b 例子如下 xff1a tail 100f localhost yyyy MM dd log 实时监控100行日志 tail n 10
  • 用c++ 的可变模板参数递归来表达 著名的斐波那契数列

    span class token macro property span class token directive keyword include span span class token string lt iostream gt s
  • debian squid透明代理简单配置

    实验环境 主机用途IP客户端10 10 100 11网关10 10 100 12 xff0c 192 168 65 160网站192 168 65 161 客户端配置 配置网关 route add default gw 10 10 100
  • Matlab中的抽象函数的求值

    Matlab中的抽象函数的求值 采用匿名函数 y 61 64 x x 2 43 2 x 8 建立匿名函数y y 4 返回当x 61 4时 xff0c y的结果 上面的命令在命令窗口可以实现 xff0c 但是在M文件中没有实现 使用matla
  • 白嫖5T空间Onedrive并搭建下载站

    更好的阅读体验欢迎访问博客白嫖5T空间Onedrive并搭建下载站 前言 白嫖一个微软E5账号不仅能自己使用office全家桶 xff0c 还能造福25个小伙伴 xff0c 何乐而不为 xff1f 这里借助onedirve的API和onei
  • 阿里云Linux(Debian) + Tomcat搭建网站

    工具 xff1a Linux 我买的是阿里云的主机Linux Debian64位的 Tomcat Java Web服务器 putty 连接远程主机的客户端 WinSCP 远程主机的可视化界面 xff0c 方便操作文件 jdk1 8 需要配置
  • 动态链接库(DLL)开发基础

    本周我的博客涉及到动态链接库的基础开发 我在刚开始学习动态链接库的开发 在网上找DLL开发基础知识教程时发现网上的资料还是太杂 xff0c 对初学者不太友好 xff0c 于是我就着手写了这篇博客 xff0c 本篇博客知识有DLL简介 DLL
  • Mybatis:使用Mybatis执行SQL多出“limit?“,原来这样就可以解决!!!

    问题展示说明 业务需要只展示分组排序后的前15条 xff0c 数据写了如下sql xff1a 启动项目访问接口后 xff0c 报如下错误 xff1a 解决办法 参考了一下MyBatis官网和其他博客发现需要清理一下之前设置过的Page缓存
  • .NET编程——利用C#实现远程桌面连接(WinForm)

    通过学习利用利用C 实现登录功能后 xff0c 本文将通过Visual Studio 2019运行实现远程桌面连接 目录 引言 前期准备 连接固定计算机 连接指定计算机 可能遇到的问题 引言 实现远程桌面有一个大前提是不可忽略的 xff0c
  • 单片机基础:什么是中断系统、中断系统如何用(附中断系统应用实例)

    中断系统 1 前言2 什么是中断3 什么是中断系统4 中断的流程5 中断的优先级控制6 中断源外部中断 7 与中断有关的特殊功能寄存器7 1 定时 计数器控制寄存器 96 TCON 96 7 2 串行口控制寄存器 96 SCON 96 7
  • 单片机基础:MCS-51单片机的硬件结构(附硬件结构框图)

    单片机硬件结构知识点非常琐碎 xff0c 通过一次两次的学习是不太可能记住的 想要熟练掌握硬件结构 xff0c 最好的方法是在实验中练习 xff0c 通过编程多见多用才能牢固的掌握 MCS 51单片机硬件结构 1 硬件系统框图2 单片机功能
  • 三种简单排序(冒泡、插入、选择)的比较和图解

    冒泡排序 这种排序方式是最容易理解的 xff0c 主体思想就是 xff1a 指针重复地走访过要排序的数列 xff0c 一次比较两个元素 xff0c 如果他们的顺序错误就把他们交换过来 走访数列的工作是重复地进行直到没有再需要交换 xff0c