数据结构---快速排序

news2024/11/17 20:43:55

快速排序

  • 分治法思想
  • 基准元素的选择
  • 元素交换
    • 双边循环法
    • JAVA实现
    • 单边循环法
    • JAVA实现

快速排序也是从冒泡排序演化而来
使用了 分治法(快的原因)

快速排序和冒泡排序共同点:通过元素之间的比较和交换位置来达到排序的目的。
快速排序和冒泡排序不同点:冒泡排序在每一轮中只把1个元素冒泡到数列的一端,而快速排序则
在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分(分治法)
在这里插入图片描述

分治法思想

在这里插入图片描述

在分治法的思想下,原数列在每一轮都被拆分成两部分,每一部分在下一轮又分别被拆分成两部分,直到不可再分为止。

快速排序算法总体的平均时间复杂度是O(nlogn)。

基准元素的选择,以及元素的交换,都是快速排序的核心问题。

基准元素的选择

随机选择一个元素作为基准元素,并且让基准元素和数列首元素交换位置。
原理:这样一来,即使在数列完全逆序的情况下,也可以有效地将数列分成两部分。
在这里插入图片描述

当然,即使是随机选择基准元素,也会有极小的几率选到数列的最大值或最小值,同样会影响分治的效果。
所以,虽然快速排序的平均时间复杂度是O(nlogn),但最坏情况下的时间复杂度是O(n2)。

元素交换

选定了基准元素以后,我们要做的就是把其他元素中小于基准元素的都交换到基准元素一边,大于基准元素的都交换到基准元素另一边。

  1. 双边循环法
  2. 单边循环法

双边循环法

在这里插入图片描述

就是left指针指向的元素逐次和基准元素pivot比较,right指针指向的元素逐次和基准元素pivot比较。

具体流程为:从right指针开始,让指针所指向的元素和基准元素做比较。如果大于或等于pivot,则指针向左移动;如果小于pivot,则right指针停止移动,切换到left指针。轮到left指针行动,让指针所指向的元素和基准元素做比较。如果小于或等于
pivot,则指针向右移动;如果大于pivot,则left指针停止移动

最后:让left和right指针所指向的元素进行交换。
在这里插入图片描述
在这里插入图片描述
具体过程如下:
在这里插入图片描述

JAVA实现

省去了随机选择基准元素的过程,直接把首元素作为基准元素

package mysort.quickSorting;

public class quickSorting {


    /**
     *
     * @param arr    待交换的数组
     * @param startIndex  起始下标
     * @param endIndex    结束下标
     * @return
     */
    private static int partition(int[]arr ,int startIndex,int endIndex){
        // 取第1个位置(也可以选择随机位置)的元素作为基准元素
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;

        while (left!=right){
            //控制right 指针比较并左移
            while (left<right&&arr[right]>pivot){
                right--;
            }
            //控制left指针比较并右移
            //有等于是因为:int pivot = arr[startIndex]; int left = startIndex;
            while (left<right&&arr[left]<=pivot){
                left++;
            }
            //left和right都找到后,交换这俩的值
            if (left<right){
                int p =arr[left];
                arr[left]=arr[right];
                arr[right]=p;
            }
        }
        //到这一步的时候left==right了
        //执行pivot 和指针重合点交换
        arr[startIndex] = arr[left];
        arr[left] = pivot;
        //至此,依据第一个元素(基准元素pivot),把数组大于第一个元素的放在他右边,小于的放在左边,并返回基准元素(pivot)最终的下标
        return left;
    }

    public static void quickSort(int []arr,int startIndex,int endIndex){
        //递归出口:startIndex大于或等于endIndex时
        //也就是分的不能再分的时候startIndex==endIndex
        if(startIndex>=endIndex){
            return;
        }
        //选取第一个元素为基准元素,把数组排序后,返回基准元素的位置
        int pivotIndex = partition(arr,startIndex,endIndex);
        //在根据基准元素,把数组划分为俩部分(分治法),对俩个子部分分别执行快排
        quickSort(arr,startIndex,pivotIndex-1);
        quickSort(arr,pivotIndex+1,endIndex);
    }
}

其中:quickSort方法通过递归的方式,实现了分而治之的思想。(把大问题划分为一个一个的小问题)
partition方法则实现了元素的交换,让数列中的元素依据自身大小,分别交换到基准元素的左右两边。

单边循环法

首先选定基准元素pivot。同时,设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界
在这里插入图片描述
如果遍历到的元素大于基准元素,就继续往后遍历。

如果遍历到的元素小于基准元素,则需要做两件事:第一,把mark指针右移1位,因为小于pivot的区域边界增大了1;第二,让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域。

蓝色表示当前遍历的位置
遍历到元素7,7>4,所以继续遍历。mark指针不动
在这里插入图片描述
遍历到的元素是3,3<4,所以mark指针右移1位。让元素3和mark指针所在位置的元素交换,因为元素3归属于小于pivot
的区域。
在这里插入图片描述
在这里插入图片描述
之后的步骤为:
在这里插入图片描述

其中分治都是递归实现的,区别就在于:双边循环法和单边循环法的区别在于partition函数的实现

JAVA实现

package mysort.quickSorting;

import java.util.Arrays;

//都是从小到大排序(左-->右)
public class quickSorting2 {

    /**
     * 单边循环法
     * @param arr         待交换的数组
     * @param startIndex  起始下标
     * @param endIndex    结束下标
     * @return
     */
    private static int partition(int[]arr,int startIndex,int endIndex){
        // 取第1个位置(也可以选择随机位置)的元素作为基准元素
        int pivot = arr[startIndex];
        //mark指针代表小于基准元素的区域边界
        int mark = startIndex;

        for (int i = startIndex+1;i<=endIndex;i++){
            //如果当前元素比 基准元素 小
            if(arr[i]<pivot){
                //小于基准元素的区域边界扩张一个单位
                mark++;
                //让最新遍历到的元素和mark指针所在位置的元素交换位置
                int p = arr[mark];
                arr[mark] = arr[i];
                arr[i] = p;
            }
        }
        //遍历结束后,把基准元素(pivot)放到相应的位置去(mask位置)
        arr[startIndex] = arr[mark];
        arr[mark] = pivot;
        //最后返回基准元素下标,为下一步分治提供划分的依据
        return mark;
    }

    public static void quickSort(int[]arr,int startIndex,int endIndex){
        //递归出口
        //也就是分治法划分的不能再划分的最小单位
        if(startIndex>=endIndex){
            return;
        }
        //得到基准元素位置
        int pivotIndex = partition(arr,startIndex,endIndex);
        //根据基准元素,分成两部分进行递归排序(分治法)
        quickSort(arr,startIndex,pivotIndex-1);
        quickSort(arr,pivotIndex+1,endIndex);
    }

    public static void main(String[] args) {
        int[]arr = new int[]{4,4,6,5,3,2,8,1};
        quickSort(arr,0,arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
}

快速排序实现方法,都是以递归为基础的。其实快速排序也可以基于非递归的方式来实现。(栈)
绝大多数的递归逻辑,都可以用栈的方式来代替
每次进入一个新方法,就相当于入栈;每次有方法返回,就相当于出栈。

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

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

相关文章

JavaWeb核心:HTTPTomcatServlet

HTTP 概念: Hyper Text Transfer Protocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则。 HTTP-请求数据格式 HTTP-响应数据格式 响应状态码的大的分类 常见的响应状态码 Tomcat 简介 概念: Tomcat是Apache 软件基金会一个核心项目&#…

【云原生】Prometheus 自定义告警规则

文章目录一、概述二、告警实现流程三、告警规则1&#xff09;告警规则配置1&#xff09;监控服务器是否在线3&#xff09;告警数据的状态四、实战操作1&#xff09;下载 node_exporter2&#xff09;启动 node_exporter3&#xff09;配置Prometheus加载node_exporter4&#xff0…

这样也可以让图像正向扩散

🍿*★,*:.☆欢迎您/$:*.★* 🍿 怎样的扩散取决于b是不是随机噪声 是随机噪声 则是扩散模型 如stable diffision 如果是非噪声则是方向模型 方向模型是指 在已知几个连续的输入 后可以通过模型的辅助预测扩散的方向 而 stable diffision 是通过预测反扩散方向 本质就…

VS2017中OpenCV编程插件Image Watch安装和使用介绍

安装 下载适合vs2017最新版本的Image Watch(ImageWatch.vsix)&#xff0c;下载地址 安装ImageWatch&#xff0c;双击ImageWatch.vsix进行安装即可&#xff1b; 使用 打开一个OpenCV工程&#xff0c;在Debug下设置断点&#xff0c;通过view -> other windows -> Image W…

基于51单片机宠物自动投料喂食器控制系统仿真设计( proteus仿真+程序+讲解视频)

基于51单片机宠物自动投料喂食器控制系统仿真设计( proteus仿真程序讲解视频&#xff09; 仿真图proteus 7.8及以上 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0029 视频讲解 基于51单片机的宠物自动投料喂食器控制系统proteu…

数据结构—最小生成树

目录 一、生成树 二、最小生成树&#xff08;代价最小树&#xff09; 三、求最小生成树 1、Prim算法&#xff08;普里姆&#xff09; 2.Kruskal 算法&#xff08;克鲁斯卡尔&#xff09; 3.Prim算法和Kruskal算法对比 一、生成树 连通图的生成树是包含图中全部顶点的一个…

[附源码]Nodejs计算机毕业设计基于框架的秧苗以及农产品交易网站Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

算法分析专业工具——大O记法

本文内容借鉴一本我非常喜欢的书——《数据结构与算法图解》。学习之余&#xff0c;我决定把这本书精彩的部分摘录出来与大家分享。 写在前面 从之前的章节中我们了解到&#xff0c;影响算法性能的主要因素是其所需的步数。 然而&#xff0c;我们不能简单地把一个算法记为“…

Postman下载,安装,汉化,注册及登录教程

目录 一、Postman简介 二、Postman的注册 1、首先下载Postman&#xff0c;进入官网&#xff1a;Download Postman | Get Started for Free 2、安装Postman 3、下载汉化包 4、找到所下载的app.zip文件&#xff0c;将文件进行解压&#xff0c;放置到此路径下 Postman\app9…

代码随想录Day52|300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

文章目录300.最长递增子序列674.最长连续递增序列718.最长重复子数组300.最长递增子序列 文章讲解&#xff1a;代码随想录 (programmercarl.com) 题目链接&#xff1a;300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 给你一个整数数组 nums…

Eclipse常用开发配置

Eclipse常用开发配置1. 编码配置1.1 输出中文乱码问题1.2 Java文件中文乱码2. 切换JDK、修改JRE3. 错误&#xff1a;找不到或无法加载主类4. 修改字体大小4.1 修改编辑窗口字体大小4.2 修改编译器字体大小5. 新建Java项目6. 导入项目6.1 导入git6.2 导入已有Java项目7. 运行中文…

.net core AutoMapper的简单使用。

AutoMapper主要处理对象与对象之间的映射&#xff0c;减少程序员自己编写代码的工作量&#xff0c;提高开发效率。 应用场景&#xff1a; 假如你想对原始数据&#xff0c;进行部分字段展示&#xff0c;那么你需要创建一个对应的DTO类&#xff0c;进行手动映射&#xff0c;这样…

在电网上使用的储能系统模拟(simulink)

目录 1 概述 2 配电系统 3 动态负载模型 4 光伏电场和TMY3数据 5 储能系统 (ESS) 6 案例 7 仿真结果 8 Simulink&Matlab代码实现 1 概述 (1)目标展示了SimPowerSystems在不到一分钟的模拟时间内&#xff0c;以相量模式模拟电路和控制系统的能力。 (2)说明与能量存储…

对话顶立欧雅纳特丨传统制造企业的“人货场”重构该从何入手?

链条长、客单价高、标准化程度低、交付周期长......作为传统制造行业中颇具代表性的领域&#xff0c;家居建材一直被视为“距离互联网最远”行业之一&#xff0c;平均仅有10%的数字化率&#xff0c;行业整体的数字化转型相对滞后。随着外部环境的变动与行业生态的发展&#xff…

RK3588平台开发系列讲解(AUDIO篇)Android音频调试--tiny-alsa 工具

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、工具介绍二、工具的使用2.1 tinyplay2.1 tinycap2.3 tinymix2.4 tinypcminfo沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍Android下audio调试工具tiny-alsa的使用方法。 一、工具介绍 RK平台…

FFT学习笔记(快速傅里叶变换)

用途 快速傅里叶变换&#xff08;Fast Fourier Transformation&#xff0c;简称FFT&#xff09; 一般用来加速多项式乘法。求两个nnn次多项式相乘&#xff0c;朴素算法需要O(n2)O(n^2)O(n2)&#xff0c;但FFT只需要O(nlog⁡n)O(n\log n)O(nlogn)就能解决。 多项式 系数表示法…

基于java的扫雷游戏的设计-计算机毕业设计

项目介绍 扫雷游戏的基本功能&#xff1a;点击鼠标左键于未知区域,如果未知区域有雷,游戏停止,显示所有的地雷。如果没雷,则显示周围雷数,如果周围没雷,则再查看周围八个区域是否有雷直到有雷为止并显示,玩家需要尽快找出雷区中的所有不是地雷的方块&#xff0c;而不许踩到地雷…

jsp+ssh+mysql实现的Java web学生考勤管理系统源码附带视频指导运行教程

今天给大家演示的是一款由jspsshmysql实现的Java web学生考勤管理系统&#xff0c;其中struts版本是struts2。本系统实现了管理员、学生、教师三个角色的功能&#xff0c;其中管理员可以管理基本信息&#xff0c;如班级信息、课程信息、用户信息、课程表等。教师可以管理自己班…

mongodb实现请求日志存储

引言 最近学习了mongodb&#xff0c;想实际应用到项目中&#xff0c;就先简单实现了一个存储请求日志的功能&#xff1b; 为什么使用mongodb存储日志&#xff0c;主要是因为日志数据量大、低价值、写入频繁&#xff0c;并且对事务要求不高&#xff0c;使用传统的关系型数据库…

Java强软弱虚引用和ThreadLocal工作原理(二)

1. 前言 读本篇文章之前&#xff0c;请移步到上一篇文章Java强软弱虚引用和ThreadLocal工作原理&#xff08;一&#xff09;_broadview_java的博客-CSDN博客 我们继续来讲一下java的强软弱引用在Android开发中的使用&#xff0c;并深入理解一下ThreadLocal的原理 2. 强软弱引…