各大排序算法

目录

插入排序

希尔排序(缩小增量排序)

冒泡排序

快速排序

选择排序

归并排序


插入排序

插入排序的基本思想是,将N个待排序元素分为一组有序表和一个无序表,一开始有序表只有一个元素,无序表中有N-1个元素,排序过程中每次取无序表的第一个元素依次与有序表的元素进行对比,插入到适当的位置使之成为新的有序表

我们可以看到在插入排序中排序需要进行的轮次是N-1轮(第一个元素默认有序),那么接下来看这个例子的第一轮的代码实现,以及代码解释

这就是得到的第一轮的结果,那么第二、第三轮也是一样如此操作,我们只需使用外部循环条件即可实现

希尔排序(缩小增量排序)

在进行希尔排序的介绍之前我们先来看看插入排序的一些问题,现在我们给出这样一组数据[2,3,4,5,6,1], 那么他在进行插入排序时会产生很多次数的位移

对于这种极端情况,那么插入排序就显得效率低下,而希尔排序也是插入排序的一种,属于优化后的插入排序

希尔排序的思想是把记录按下标的一定增量进行分组,对每组进行插入排序进行排序,随着增量的逐渐减小,每组包含的元素越来越多,当增量缩小为1时,整个数组被分为一组,进行插入排序后则有序

这种是交换法的希尔排序,图中的进行插入排序其实不是真正的直接插入排序,而只是对比两个数进行交换

这就是希尔排序的图解,接下来我们来看代码实现

根据希尔三次排序我们可以摸索到希尔排序的循环规则,接下来我们将其整合起来

import java.util.Arrays;

public class MyShellSort {
    public static void main(String[] args) {
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        ShellSort(arr);
    }

    //使用逐步推导的方式进行分析希尔排序
    public static void ShellSort(int[] arr) {
        int temp = 0; //定义临时变量,用于交换
        int count = 0;
        for(int gap = arr.length/2;gap>0;gap/=2){
            for (int i = gap; i < arr.length; i++) {
                for (int j = i - gap; j >= 0; j -= gap) {
                    //如果当前元素大于加上增量后的那个元素,进行交换
                    if (arr[j] > arr[j + gap]) {
                        temp = arr[j];
                        arr[j] = arr[j + gap];
                        arr[j + gap] = temp;
                    }
                }
            }
            System.out.println("第"+(++count)+"轮希尔排序后的结果:" + Arrays.toString(arr));
        }
    }
}

我们看这段代码可以发现他嵌套了三个for循环,可想而知他的效率肯定是不高的,而介绍这种交换法的希尔排序主要是让我们理解增量这个概念,接下来我们来学习希尔排序移动法(进行直接插入排序)

移动法的希尔排序就是在分组后,对各组进行排序不是使用简单的交换,而是进行直接插入排序,即找到了合适的位置才插入(交换法效率低下的原因就是因为无脑进行交换,导致交换量过大)

import java.util.Arrays;

public class MyShellSort {
    public static void main(String[] args) {
        int[] arr = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
        System.out.println("希尔排序前的数组"+Arrays.toString(arr));
        ShellSort(arr);
        System.out.println("希尔排序后的数组"+Arrays.toString(arr));
    }

    //使用逐步推导的方式进行分析希尔排序
    //交换法希尔排序
    public static void ShellSort(int[] arr) {
        for(int gap = arr.length/2;gap>0;gap/=2){
            //从gap元素开始,在对其所在的组进行插入排序
            for (int i = gap;i<arr.length;i++){
                int j = i;
                int temp = arr[j];//待插入元素
                if(arr[j] < arr[j-gap]){
                    while(j-gap >= 0 && temp < arr[j-gap]){
                        arr[j] = arr[j-gap]; //arr[j]这个待插入数的前一个数向后挪
                        j -= gap;
                    }
                    //当退出while循环后,说明找到了合适的位置了
                    arr[j] = temp;
                }
            }
        }
    }
}

这就是移动法的希尔排序中间的排序逻辑和插入排序类似,只是多引出了gap增量这个概念

冒泡排序

冒泡排序的基本思想是,对待待排序序列从前向后,依次比较相邻元素的值,如果发现逆序则进行交换,使值较大的元素逐渐从前移向后部(从小到大排序),就像水底的气泡向上冒

接下来给出一幅图理解冒泡排序的过程

在这张冒泡排序图解中我们可以发现,一共进行数组长度-1次循环,每一趟都使得当趟最大的值回到适合的位置,并且每趟排序的次数都在逐渐减少(因为已经有元素到了适合的位置)

接下来就来看看冒泡排序的代码演示,为了方便理解我将一步一步拆分

看这样的一段冒泡排序分解代码,想必你已经发现规律了,for循环体都是一样的,交换思想也是一样(使用临时变量存储值),那么我们就可以使用一个for循环将其整理为以下代码

import java.util.Arrays;

public class MyBubbleSort {
    public static void main(String[] args) {
        int[] arr = {3,9,-1,10,-2};
        BubbleSort(arr);
    }
    public static void BubbleSort(int[] arr){
        //第一趟排序将最大的值放在倒数第一位


        for(int i = 0;i< arr.length-1;i++){
            for(int j = 0;j<arr.length-1-i;j++){
                int temp = 0;//临时变量,进行交换元素时用到
                if(arr[j]>arr[j+1]){
                    //如果前面的元素比后面元素大,则进行交换(从小到大排序)
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
            System.out.println("第"+(i+1)+"趟排序后的结果:");
            System.out.println(Arrays.toString(arr));
        }

我们可以知道这种for循环嵌套for循环的时间复杂度是O(n^2),这样写的代码还不是最完美的,还可以进行优化,首先先看下面这一组数据的冒泡排序过程

从这组数据中我们可以发现,在第二趟交换了一次之后,数据已经变得有序了,那么接下来的操作都只是走"过程"而已

那么我们优化的思路就是,如果在某一趟排序中,没有发生任何一次的交换,那么就可以提前结束冒泡排序

这样我们就减少了一趟循环,也就优化成功了

快速排序

快速排序是对冒泡排序算法的一种改进,快速排序的基本思想是将要排序的数据分割成独立的两个部分,其中一部分的所有数据要比另外一部分的所有数据都要小,然后再按从方法对独立的两个部分进行快速排序,直到变得有序

接下来的的各种细节看代码演示,以及注解 

import java.util.Arrays;

public class MyQuickSort {
    public static void main(String[] args) {
        int[] arr = {-9,78,0,23,-567,70};
        //定义每段快速排序的最左边的位置
        int left = 0;
        //定义每段快速排序最右边的位置
        int right = arr.length-1;
        QuickSort(arr,left,right);
        System.out.println(Arrays.toString(arr));
    }

    public static void QuickSort(int[] arr,int l,int r){
        if(l<r){
            int pivotpos = partition(arr,l,r);
            //将pivot左边部分进行快速排序
            QuickSort(arr,l,pivotpos-1);
            //将pivot右边部分进行快速排序
            QuickSort(arr,pivotpos+1,r);
        }
    }

    public static int partition(int[] arr,int l,int r){
        int pivot = arr[l];
        while (l<r){
            //每次都要进行是否l<r的判断(因为极端的情况下比如右边都没有比pivot小的数,那么就会一直向前到不满足l<r)
            while(l<r&&arr[r]>=pivot){
                //若当前这个数不是比pivot小那么就进行向前找
                r--;
            }
            //跳出循环说明右边找到小于pivot的数了
            //那么就让其填入left的位置
            arr[l] = arr[r];

            while (l<r&&arr[l]<=pivot){
                //若当前这个数不是比pivot大那么就进行向后找
                l++;
            }
            //跳出循环说明找到了比pivot大的数
            //那么就让将其填入right的位置
            arr[r] = arr[l];
        }
        //这里也可以写arr[r] = pivot 因为跳出了大循环说明他们相遇了
        arr[l] = pivot;
        //放回当前相遇的位置,以便后续进行两部分的快速排序
        return l;
    }
}

选择排序

选择排序的基本思想很简单,就是在一组待排序数组arr中,第一次从arr[0]~arr[n-1]中寻找最小的元素,与arr[0]进行交换,第二次从arr[1]~arr[n-1]中寻找第二小的数与arr[1]进行交换,一直寻找当前待排序列的最小数进行交换直到有序为止

看着这张图进行选择排序的说明:

  • 选择排序一共有数组大小-1轮排序
  • 每一轮排序又是一个循环(目的是找到当前轮次最小值)

每一轮循环的规则如下:

  • 先假定当前元素是最小元素,然后与每个元素进行比较,遇到比当前最小元素小的数,则重新设置其为最小元素,并且得到他的下标,当遍历完数组后就得到了最小数以及其下标,最后进行交换即可

接下来看选择排序的代码实现

通过上方的分解代码我们也找到了规律,那么现在用外层嵌套for循环来完成

归并排序

归并排序的基本思想是采用分治策略(将大问题分成小问题然后递归求解),归并排序的思想比较难理解,我们先看案例图解

分阶段可以理解为递归拆分子序列,而治阶段可以简单用一幅图介绍

接下来通过代码以及注释来理解归并排序

import java.util.Arrays;

public class MyMergeSort {
    public static void main(String[] args) {
        int[] arr = {8,4,5,7,1,3,6,2};
        int[] temp = new int[arr.length]; //归并排序需要额外的空间
        mergeSort(arr,0, arr.length -1,temp);

        System.out.println(Arrays.toString(arr));
    }

    //分+合的代码
    public static void mergeSort(int[] arr,int left,int right,int[] temp){
        if(left<right){
            int mid = (left+right)/2;
            //先向左递归进行分解
            mergeSort(arr,left,mid,temp);
            //向右递归进行分解
            mergeSort(arr,mid+1,right,temp);

            //进行合并
            merge(arr,left,mid,right,temp);

        }
    }

    /**
     *
     * @param arr //排序的原始数组
     * @param left //左边有序序列的初识索引
     * @param mid //中间索引
     * @param right //右边索引
     * @param temp //临时数组
     */
    //合并的代码
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
        int i = left; //初始化i,左边有序序列的初识索引
        int j = mid+1; //初始化j,右边有序序列的初识索引
        int t = 0; //做为临时数组的索引

        //1.先把左右两边的有序序列按照规则,填充到临时数组,直到有一方完全填充完为止
        while (i<=mid && j<=right){
            if(arr[i]<=arr[j]){
                //左边的值比右边小,则填充左边的到临时数组,并且i++
                temp[t] = arr[i];
                i+=1;
                t+=1;  //临时数组的索引后移
            }else {
                //右边的值比左边小,则填充右边的到临时数组,并且j++
                temp[t] = arr[j];
                j+=1;
                t+=1;
            }
        }

        //2.把有剩余数据的一边依次全部填充到临时数组
        while(i<=mid){
            //说明左边没有填充完毕
            temp[t] = arr[i];
            i+=1;
            t+=1;
        }
        while (j<=right){
            //说明右边没有填充完毕
            temp[t] = arr[j];
            j+=1;
            t+=1;
        }

        //3.将临时数组元素拷贝到原数组
        //注意:这里不是每次都拷贝所有数据,比如进行递归的第一次合并,拷贝到原数组的元素只有0 1
        t = 0;
        int tempLeft = left; //并不是每次拷贝都是从头开始的,left拷贝的值是会发送变化的
        while (tempLeft<=right){ //第一次合并tempLeft=0 right=1 / 第二次合并tempLeft=2 right=3......
            arr[tempLeft] = temp[t];
            tempLeft+=1;
            t+=1;
        }
    }
}

这里需要注意的是理解递归的过程,合并并不是我们上方举例的最后一次合并,第一次合并只是合并两个元素,我们加上一些代码进行对合并内部的理解,结果为

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

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

相关文章

Leetcode260

260. 只出现一次的数字 III - 力扣&#xff08;LeetCode&#xff09; class Solution {public int[] singleNumber(int[] nums) {//通过异或操作,使得最终结果为两个只出现一次的元素的异或值int filterResult 0;for(int num:nums){filterResult^num;}//计算首个1(从右侧开始)…

c++ 虚函数常见问题

1 虚函数&#xff0c;虚表基础 虚函数&#xff0c;虚表基础 2 虚函数表保存在哪里 ? 虚函数表在编译的时候确定。在 linux 下&#xff0c;保存在只读数据段的重定位段&#xff0c;这个段的名字是 .data.rel.ro。 如下代码&#xff0c;编译之后&#xff0c;使用 readelf -t a…

vue3 vite项目配置了proxy代理情况下查看真实的接口调用地址

vite配置了proxy代理情况下如何查看真实的接口调用地址? 使用vite进行代理 在vite.config.ts配置了代理 在浏览器查看请求头和响应头发现只有代理前的url&#xff0c;没有显示代理后的路径 然后发现一个bypass函数&#xff0c;但是此函数只能修改res响应头的数据&#xff0…

Visual Studio 的调试(一)

最近事儿很多昂&#xff0c;更新速度相较以往慢了许多&#xff0c;备考六月份的四级&#xff0c;还有学校的期末等等&#xff0c;事儿真的太多啦&#xff0c;所以后面的更新速度也会放慢一点&#xff0c;实在是抽不开身啊诸位&#xff0c;相当抱歉&#xff0c;还望诸君见谅 言…

原哥花了1个多月的时间终于开发了一款基于android studio的原生商城app

大概讲一下这个app实现的功能和前后端技术架构。 功能简介 广告展示商品展示跳转淘宝联盟优惠卷购买发布朋友圈宝妈知识资讯商品搜索朋友圈展示/点赞/评论登陆注册版本升级我的个人资料商品和资讯收藏我的朋友圈意见反馈 安卓端技术选型 Arouter组件化daggerrxjavaretrofit…

遇见问题-VMware虚拟机打开运行一段时间后卡死

1.问题原因 因为Windows自带的虚拟化软件Hyper-V与VMware存在冲突。 2.关闭Hyper-V 1.打开【控制面板】-【程序和功能】-【启用或关闭Windows功能】3.关闭HV主机服务 1.右击计算机-》管理-》服务和应用名称-》服务-》找到HV主机服务-》右击属性停止服务 -》启动类型设置为禁…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(十三)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 我们&#xff0c;继续讲一…

乡村振兴的农业品牌建设:打造农业品牌,提升农产品附加值,增强乡村经济竞争力,实现美丽乡村经济繁荣

目录 一、引言 二、农业品牌建设的重要性 &#xff08;一&#xff09;提升农产品附加值 &#xff08;二&#xff09;增强乡村经济竞争力 &#xff08;三&#xff09;实现美丽乡村经济繁荣 三、农业品牌建设的现状及问题 &#xff08;一&#xff09;现状 &#xff08;二…

python-10(爬虫)

1.网络爬虫 1.1.引言 我们平时都说Python爬虫&#xff0c;其实这里可能有个误解&#xff0c;爬虫并不是Python独有的&#xff0c;可以做爬虫的语言有很多例如&#xff1a;PHP、JAVA、C#、C、Python。 为什么Python的爬虫技术会异军突起呢&#xff1f; Python火并不是因为爬…

兵器室管控系统|DW-306是一套成熟系统

概述 智慧兵器室管理系统&#xff08;DW-S306&#xff09;是依托互3D技术、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 本解决方案利用现有内部网络&#xff0c;部署部队智能兵器室管理系统&#xff…

使用Django框架搭建Web应用

文章目录 简介安装Django创建一个Django项目创建一个Django应用编写视图配置URL运行开发服务器总结与拓展数据库集成管理后台表单处理模板引擎安全性 简介 Django 是一款基于 Python 语言的开源 Web 应用框架&#xff0c;采用了 MVC&#xff08;模型-视图-控制器&#xff09;设…

数据库系统原理实验报告6 | 视图

整理自博主本科《数据库系统原理》专业课自己完成的实验报告&#xff0c;以便各位学习数据库系统概论的小伙伴们参考、学习。 专业课本&#xff1a; ​ ———— 本次实验使用到的图形化工具&#xff1a;Heidisql ​ 目录 一、实验目的 二、实验内容 1&#xff0e;根据EDUC数…

Matlab中函数或变量 ‘eeglab‘ 无法识别

EEGLAB 没有安装或添加到 MATLAB 路径中&#xff1a; 确保已经安装了 EEGLAB&#xff0c;并且将其添加到 MATLAB 的路径中。您可以通过在 MATLAB 命令窗口中运行 which eeglab 来检查是否能够找到 EEGLAB。 EEGLAB 函数路径设置错误&#xff1a; 如果已经安装了 EEGLAB&#x…

信息系统项目管理师0131:输出(8项目整合管理—8.7监控项目工作—8.7.3输出)

点击查看专栏目录 文章目录 8.7.3 输出8.7.3 输出 工作绩效报告工作绩效信息可以用实体或电子形式加以合并、记录和分发。基于工作绩效信息,以实体或电子形式编制形成工作绩效报告,以制定决策、采取行动或引起关注。根据项目沟通管理计划,通过沟通过程向项目干系人发送工作绩…

Top期刊:针对论文Figure图片的7个改进建议

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 通过对来自细胞生物学、生理学和植物学领域的580篇论文&#xff0c;进行检查和归纳总结&#xff0c;来自德国德累斯顿工业大学的Helena Jambor及合作者&#xff0c;在PLOS Bio…

Linux网络编程:HTTPS协议

目录 1.预备知识 1.1.加密和解密 1.2.常见加密方式 1.2.1.对称加密 1.2.2.非对称加密 ​编辑 1.3.数据摘要&#xff08;数据指纹&#xff09;和数据签名 1.4.证书 1.4.1.CA认证 1.4.2.证书和数字签名 2.HTTPS协议 2.1.自行设计HTTPS加密方案 2.1.1.只使用对称加密 …

Linux-组管理和权限管理

1 Liunx组的基本介绍&#xff1a; 在Linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在Linux中每个文件都有所有者、所在组、其他组的概念 所有者所在组其它组改变用户所在的组 2 文件/目录的所有者 一般文件的创建者&#xff0c;谁创建了该文件&#xff0c;就…

Selenium 自动化测试工具<2>(Selenium 常用API的使用方法)

文章目录 浏览器操作浏览器最大化设置浏览器的大小浏览器的前进和后退操作浏览器滚动条 键盘事件单个按键用法键盘组合键用法 鼠标事件不同窗口搜索定位一组元素定位多层框架下拉框定位alert、confirm、prompt 的处理上传文件操作自动截屏 继上一篇文章对 Selenium API 的使用&…

【微机原理及接口技术】可编程并行接口芯片8255A

【微机原理及接口技术】可编程并行接口芯片8255A 文章目录 【微机原理及接口技术】可编程并行接口芯片8255A前言一、8255A的内部结构和引脚1.与外设接口&#xff08;数据端口&#xff09;2.与处理器接口 二、8255A的工作方式三、8255A的编程1. 写入方式控制字&#xff1a;控制字…

想转行程序员的朋友,有什么想问的在评论区随便问,我知道的都告诉你。

你想转行程序员吗&#xff1f; 我自己是法学院毕业后&#xff0c;通过2年的努力才转行程序员成功的。 我发现对于一个外行来说&#xff0c;找不到一个适合自己的方向&#xff0c;光靠努力在一个新的行业里成功异常艰难。即使你非常努力&#xff0c;但方向错了也会做大量的无用…