七大排序算法——快速排序,通俗易懂的思路讲解与图解(完整Java代码)

news2024/11/23 7:44:21

文章目录

  • 一、排序的概念
    • 排序的概念
    • 排序的稳定性
    • 七大排序算法
  • 二、快速排序
    • 核心思想
    • Hoare法
    • 挖坑法
    • 前后指针法(选学)
  • 三、性能分析
  • 四、算法优化
    • 优化基准的选取
    • 优化少量数据时的排序方案
    • 优化后的完整代码
  • 五、七大排序算法


一、排序的概念

排序的概念

排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

排序的稳定性

在这里插入图片描述
上述待排序的数中,有两个5。 将前面的5标记一个a, 将后面的5标记一个b。

通过算法进行排序后,这一组数就有序了, 但是要看两个相同的5的位置是否有改变。
5a仍在5b前面,那么这个排序算法就是稳定的
5a跑到了5b后面,那么这个排序算法就是不稳定的

一个稳定的排序算法可以做到不稳定,
不稳定的排序算法一定做不到稳定。


至于为什么要讨论这个稳定性, 是为了以后应用到实际场景上。 比如,一场数学考试, 假设a用了30分钟做完了,并得了满分。
假设b用了一个小时做完了,并得了满分。 此时a与b都是得了满分,但是用的时间不一样,所以两个人的排名又会有所不同。


七大排序算法

在这里插入图片描述


二、快速排序

核心思想

基本思想任取待排序元素序列中的某元素作为基准值,将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
    	// 此处的一定要是大于等于
    	// 如果往下递归出现了piovt == 0的情况,
    	// 那么再递归进去,left == 0,right == -1,
    	// 此时就要结束递归
        if(left >= right) {
            return;
        }
        // partition方法功能:将选取的基准值放到合适的位置,并返回基准值下标
        int piovt = partition(array,left,right);
        
		// 通过递归的方式,对左右子序列分别找寻基准值并放到合适位置,从而达到排序目的
        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }

共有三种方法可以实现该思想。


Hoare法

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

代码

    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
        if(left >= right) {
            return;
        }
        
        int piovt = partition1(array,left,right);
        
        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }
    
    //Hoare法
    private static int partition(int[] array,int left,int right) {
        // 基准值
        int tmp = array[left];
        // 基准下标
        int index = left;
        while (left < right) {
            // 让right找比tmp小的数
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 让left找比tmp大的数
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 让left与right这两个数进行交换
            swap(array,left,right);
        }
        // 将基准值放到合适的位置
        swap(array,index,right);
        // 返回基准下标
        return right;
    }


挖坑法

挖坑发是后人对Hoare法的另一种实现方式。

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意left与right的区别,别看错了就搞不懂了。

代码

    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
        if(left >= right) {
            return;
        }
        int piovt = partition(array,left,right);
        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }
     //挖坑法
    private static int partition(int[] array,int left,int right) {
    	// 在left下标挖一个坑
        int tmp = array[left];
        while (left < right) {
        	// 让right下标去找比tmp小的数
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 填left下标的坑,此时right下标变成一个坑了
            array[left] = array[right];
            // 让left下标去找比tmp大的数
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 填right下标的坑,此时left下标变成一个坑了
            array[right] = array[left];
        }
        // 将基准值放到合适的位置
        array[left] = tmp;
        // 返回基准下标
        return left;
    }

前后指针法(选学)

图解

有一组待排序数列,我们进行升序排序。
在这里插入图片描述


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码

	public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
        if(left >= right) {
            return;
        }
        int piovt = partition(array,left,right);
        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }

	// 前后指针法
	private static int partition(int[] array, int left, int right) {
        int prev = left ;
        int cur = left+1;
        while (cur <= right) {
        	// 此处当第一个判断语句不满足时,
        	// 不会触发第二个判断语句,
        	// 会跳出if语句
        	// 因此不是每次if进行判断时,都会执行++prev
            if(array[cur] < array[left] && array[++prev] != array[cur]) {
                swap(array,cur,prev);
            }
            cur++;
        }
        swap(array,prev,left);
        return prev;
    }
    private static void swap(int[] array,int a,int b) {
    	int tmp = array[a];
    	array[a] = array[b];
    	array[b] = tmp;
    }

三、性能分析

最好情况

每次找到的基准都能平均将数据分为两组
在这里插入图片描述
那么此时一共有log(n)层,每层都有n个数据,这n个数据都要遍历一遍,因此
时间复杂度:O(n*log(n)),
此时会先遍历左树,因此空间复杂度为树的高度即层数
空间复杂度:log(n).

不相邻间元素发送了交换
稳定性:不稳定

最坏情况

需要排序的数列本身就是一个顺序或逆序的数列,此时找到的基准就会每次都分成极端的两组,一组一个数没有,另一组有n-1个数(这两组的边界一个是基准-1,一个是基准+1)

在这里插入图片描述
此时就会有n层,每层也有n个数据
此时
时间复杂度:O(n2)
空间复杂度为树的高度即层数
空间复杂度:n

不相邻间元素发送了交换
稳定性:不稳定


四、算法优化

快速排序在最坏情况下,时间复杂度竟然达到了O(n2),这哪里快速啊,所以下面就要进行优化了。

优化基准的选取

共有两种方案: 1️⃣随机选取基准法,这要是倒霉起来,依然有可能会次次都随机选到最极端最坏的情况,所以这个不用。 2️⃣三数取中法,这个可以保证不会让你选到最极端最坏的情况。

三数取中法:在上面的算法中,我们的基准选取的都是left下标,
而三数取中指的是在left,right,mid( (left + right)/2 )这三个下标在中选取一个中间值作为基准,不是最大也不是最小,就保证了不会出现极端情况。
出现了以上的最坏情况,也就是让快速排序变成了二分查找。

    private static int minThree(int[]array,int left,int right) {
        //三数取中法,优化递归实现的快速排序
        //使得最坏情况时,快速排序变为二分查找
        int mid = (left+right)/2;
        if(array[right] > array[left]) {
            int tmp = left;
            left = right;
            right = tmp;
        }
        if(array[mid] > array[left]) {
            return left;
        }
        if(array[mid] > array[right]) {
            return mid;
        }
        return right;
    }

优化少量数据时的排序方案

数据量大时
就像二叉树一样,每一组数据往下走一层都会被分成两组,而到了最后几层,则会因为数据量的庞大而被分成许多组进行递归,此时的递归开销就会很大,很有可能导致~~栈溢出~~,

因此我们可以设定一个数量闸口,当每组的数据小的到了这个闸口,就采用比较简单的直接插入排序。

而且在快速排序的不断递归下,数据一定是越来越有序的,直接插入排序的效率也会更高。

数据小时

此时即便是一开始就用直接插入排序,时间也会相差无几。


优化后的完整代码

public class QuickSort {
    /**
     * 快速排序
     * 时间复杂度:代码未优化时:最好情况(满二叉树或完全二叉树):O(n*logn),    最坏情况(顺序和逆序时):O(n^2)
     * 空间复杂度:代码未优化时:最好情况(满二叉树或完全二叉树):O(logn),    最坏情况(顺序和逆序时):O(n)
     * 稳定性:不稳定
     * @param array
     */


    public static void quickSort(int[] array) {
        quick(array,0, array.length-1);
    }
    private static void quick(int[] array,int left,int right) {
        if(left >= right) {
            return;
        }
        // 设置数量闸口,
        // 数量小,使用直接插入排序
        if(right - left + 1 < 14) {
            InsertSort(array);
            return;
        }
        
        // 将三数取中法取得的中间值换到left处
        swap(array,minThree(array,left,right),left);
        int piovt = partition(array,left,right);

        quick(array, left, piovt-1);
        quick(array,piovt+1,right);
    }

    //挖坑法
    private static int partition(int[] array,int left,int right) {
        // 在left下标挖一个坑
        int tmp = array[left];
        while (left < right) {
            // 让right下标去找比tmp小的数
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 填left下标的坑,此时right下标变成一个坑了
            array[left] = array[right];
            // 让left下标去找比tmp大的数
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 填right下标的坑,此时left下标变成一个坑了
            array[right] = array[left];
        }
        // 将基准值放到合适的位置
        array[left] = tmp;
        // 返回基准下标
        return left;
    }

    //Hoare法
    private static int partition3(int[] array,int left,int right) {
        // 基准值
        int tmp = array[left];
        // 基准下标
        int index = left;
        while (left < right) {
            // 让right找比tmp小的数
            while (right > left && array[right] >= tmp) {
                right--;
            }
            // 让left找比tmp大的数
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 让left与right这两个数进行交换
            swap(array,left,right);
        }
        // 将基准值放到合适的位置
        swap(array,index,right);
        // 返回基准下标
        return right;
    }


    //前后指针法
    private static int partition2(int[] array, int left, int right) {
        int prev = left ;
        int cur = left+1;
        while (cur <= right) {
            if(array[cur] < array[left] && array[++prev] != array[cur]) {
                swap(array,cur,prev);
            }
            cur++;
        }
        swap(array,prev,left);
        return prev;
    }

    private static int minThree(int[]array,int left,int right) {
        //三数取中法,优化递归实现的快速排序
        //使得最坏情况时,快速排序变为二分查找
        int mid = (left+right)/2;
        if(array[right] > array[left]) {
            int tmp = left;
            left = right;
            right = tmp;
        }
        if(array[mid] > array[left]) {
            return left;
        }
        if(array[mid] > array[right]) {
            return mid;
        }
        return right;
    }

    private static void swap(int[] array,int a,int b) {
        int tmp = array[a];
        array[a] = array[b];
        array[b] = tmp;
    }

}



五、七大排序算法

在这里插入图片描述

想学哪个点哪个
归并排序讲解
快速排序讲解
直接插入排序讲解
希尔排序讲解
直接选择排序讲解
堆排序讲解
冒泡排序讲解

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于ChatGPT和私有知识库搭建Quivr项目

准备工作 安装docker和docker-compose申请supabase账号 拉取Quivr代码 git clone https://github.com/StanGirard/Quivr.git 复制.XXXXX_env文件 cp .backend_env.example backend/.env cp .frontend_env.example frontend/.env 更新backend/.env和frontend/.env文件 ba…

靶场的安装

sqli-lab 1.将安装包解压放到WWW目录下 2.修改 db-creds.inc文件里面的数据库的用户名密码为自己的用户名密码 路径&#xff1a;D:\phpStudy_64\phpstudy_pro\WWW\sqli-labs-master\sql-connections\db-creds.inc 3. 更改php版本位5.9版本&#xff0c;不然会报错 4.安装数…

【采用有限元法技术计算固有频率和欧拉屈曲荷载】使用有限元法的柱子的固有频率和屈曲荷载(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Android JNI 异常处理 (十一)

🔥 Android Studio 版本 🔥 🔥 创建包含JNI的类 JNIException.java 🔥 package com.cmake.ndk1.jni;public class JNIException {static {System.loadLibrary("exception-lib");}public native void nativeInvokeJavaException();public native void nativ…

B站这些“搬运工”还能有这么高的流量吗?

飞瓜数据&#xff08;B站版&#xff09;观察发现&#xff0c;B站经常有一些搬运视频能够获得超高流量。 比如拉取近15天的B站热门视频&#xff0c;位列前排的就有两个是搬运二创视频&#xff0c;播放量高达900万上下&#xff0c;可以说是爆款视频了。 这些视频有一个相同的点就…

Qt Https通信: TLS initialization failed 解决方法

Qt Https通信&#xff1a; TLS initialization failed 解决方法&#xff0c;Window端使用Qt 做开发请求Https资源时&#xff0c;会经常遇到 TLS initialization failed。 原因分析&#xff1a; 在Qt中并未包含 SSL所包含的库&#xff0c;因此需要开发者&#xff0c;自己将库拷贝…

最新华为鸿蒙4.0安装谷歌服务框架,安装Play商店,谷歌Google,GMS

最近华为推出了最新鸿蒙4.0开发者Beta版本&#xff0c;让用户测试体验。那么测试体验的机器主要是最近发布的几款机器为P60,P60 Pro, mate50,mate50 pro等几款产品可以先期进行体验测试鸿蒙4.0&#xff0c;那么很多的用户在疑问我升级到鸿蒙4.0。是不是还是可以使用Google谷歌服…

LINUX环境小实验

实验报告 实验名称 小环境搭设 实验目的 1.搭建DHCP服务器&#xff08;IP&#xff1a;192.168.100.253静态IP网卡vmnet1&#xff09; 2.搭建DNS&#xff08;通过DHCP服务器分到指定的IP&#xff1a;192.168.100.252&#xff09; 3.搭建网站服务&#xff08;通过DHCP服务器分…

波分复用(WDM)基本原理

文章目录 波分复用WDMDWDM解决问题&#xff0c;特点&#xff0d;超长距离无电中继传输&#xff0c;降低成本 波分系统的基本组成DWDM网元基本类型波分常见站点类型OM/OD技术&#xff0d;波分复用器主要参数 DWDM系统关键技术光转发技术 OM/OD技术&#xff0d;波分复用器件 波分…

Bun 0.6.14发布,1.0版预计发布于9月7日

Bun 是一个 JavaScript 运行时。 Bun 是一个从头开始构建的新 JavaScript 运行时&#xff0c;旨在服务现代 JavaScript 生态系统。它有三个主要设计目标&#xff1a; 速度。包子启动快&#xff0c;运行也快。它扩展了 JavaScriptCore&#xff0c;即为 Safari 构建的注重性能的 …

Office如何通过VSTO进行PPT插件开发?

文章目录 0.引言1.工具准备2.PPT外接程序创建和生成3.外接程序生成并使用 0.引言 VSTO&#xff08;Visual Studio Tools for Office &#xff09;是VBA的替代&#xff0c;是一套用于创建自定义Office应用程序的Visual Studio工具包。VSTO可以用Visual Basic 或者Visual C#扩展O…

EIK+Filebeat+Kafka

目录 Kafka 概述 为什么需要消息队列&#xff08;MQ&#xff09; 使用消息队列的好处 消息队列的两种模式 Kafka 定义 Kafka 简介 Kafka 的特性 Kafka 系统架构 Partation 数据路由规则&#xff1a; 分区的原因 部署 kafka 集群 1.下载安装包 2.安装 Kafka 修改配…

【979. 在二叉树中分配硬币】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给定一个有 N 个结点的二叉树的根结点 root&#xff0c;树中的每个结点上都对应有 node.val 枚硬币&#xff0c;并且总共有 N 枚硬币。 在一次移动中&#xff0c;我们可以选择两个相邻的结点&#x…

【C++】list简单介绍

list基本功能介绍 前言正式开始构造函数push_backiteratorpush_frontinserterasespliceremoveuniquereversesortmerge 前言 本篇不会讲太多细节&#xff0c;就说一下STL库中一些函数的基本用法&#xff0c;如果想要了解细节上的东西的话&#xff0c;建议看我string的介绍&…

QT ui_xxx.h: no such file or directory”

使用QT新建子窗口后,编译无法通过 mainwindow.obj:-1: error: LNK2019: 无法解析的外部符号 "public: __cdecl labelwindow::labelwindow(class QWidget *)" (??0labelwindowQEAAPEAVQWidgetZ)&#xff0c;该符号在函数 "private: void __cdecl MainWindow::o…

android studio 添加并读取json配置文件

第一步&#xff1a;在android studio中添加json文件&#xff1b; 第二步&#xff1a;读取文件的函数 private String[] getJosnData(){String result[] null;List<String> list new ArrayList<>();try {//获取本地的Json文件AssetManager assetManager mConte…

界面控件DevExtreme v23.1新版亮点 - 全新的DateRangeBox组件

DevExtreme拥有高性能的HTML5 / JavaScript小部件集合&#xff0c;使您可以利用现代Web开发堆栈&#xff08;包括React&#xff0c;Angular&#xff0c;ASP.NET Core&#xff0c;jQuery&#xff0c;Knockout等&#xff09;构建交互式的Web应用程序。从Angular和Reac&#xff0c…

【Qt】 自定义列表控件

一、效果图 二、思路 先实现单个item控件&#xff0c;之后根据所需个数new出来插入布局中。item过多时支持滑动操作&#xff0c;可以把item放入scrollArea中&#xff0c;如需实现滑动效果可以使用eventFilter&#xff0c;计算坐标配合scrollArea->verticalScrollBar()->…

2023数字化转型研讨会:RockPlus MOM系统引领制造业变革

7月13日在淄博张店举办的2023年企业数字化转型研讨会上&#xff0c;RockPlus MOM制造运营系统闪耀登场&#xff0c;凭借其将精益生产与信息化相结合的理念&#xff0c;成为会议的一大焦点。该系统是由合共软件精心打造的&#xff0c;目标在供应链管理(SRM)、仓储物流(WMS)、计划…