【数据结构】排序 —— 快速排序(quickSort)

news2024/12/29 14:23:44

Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
🌱🌱个人主页:奋斗的明志
🌱🌱所属专栏:数据结构、LeetCode专栏

在这里插入图片描述

📚本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。

在这里插入图片描述

快速排序

  • 前言
  • 一、快速排序
    • 1.概念
    • 2.快排递归实现的主框架
    • 3.Hoare法(方法一)
    • 4.挖坑法(方法二)
    • 5.前后指针法(方法三)
  • 二、快速排序优化
    • 1.三数取中法选key
    • 2.递归到小的子区间时,可以考虑使用插入排序
    • 3.快速排序非递归
  • 三、完整代码
  • 总结


前言

在这里插入图片描述

一、快速排序

1.概念

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为: 任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

2.快排递归实现的主框架


// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int[] array, int left, int right) {
    if (right - left <= 1) return;
// 按照基准值对array数组的 [left, right)区间中的元素进行划分
    int div = partion(array, left, right);
// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right) // 递归排[left, div)
    QuickSort(array, left, div);
// 递归排[div+1, right)
    QuickSort(array, div + 1, right);
}

与二叉树前序遍历规则非常像,在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。


3.Hoare法(方法一)

【图解】


在这里插入图片描述


在这里插入图片描述


在这里插入图片描述

【主要代码展示】

/**
     * Hoare法
     * @param array
     * @param left
     * @param right
     * @return
     */
    private static int partitionHoare(int[] array, int left, int right) {
        //这里以最左边的元素为基准元素
        int tmp = array[left];
        //要先记录下来 left 的值,因为在下面操作过程中,left 的值会变
        int i = left;
        while (left < right){

            while (left < right && array[right] >= tmp){
                right--;
            }

            while (left < right && array[left] <= tmp){
                left++;
            }

            //进行交换
            swap(array,left,right);
        }
        //将基准元素交换到中间
        swap(array,i,left);
        //返回此时基准元素的下标
        return left;
    }

【测试这个方法】

public static void testQuickArray(int[] array){
        int[] tmpArray = Arrays.copyOf(array,2 * array.length);
        long startTime = System.currentTimeMillis();
        Sort.quickSort(tmpArray);
        long endTime = System.currentTimeMillis();
        System.out.println("快速排序所用时间:" + (endTime - startTime));
    }
   public static void main(String[] args) {
        System.out.println("给100000个数据");
        int[] array = new int[10_0000];
        inorderArray(array);
//        notInorderArray(array);
//        initArray(array);

        testInsertArray(array);
        testShellArray(array);
        testSelectArray(array);
        testQuickArray(array);

    }

在这里插入图片描述

栈会溢出,可以修改idea里面的配置
在这里插入图片描述

因为这个方法时间复杂度:
* 最好情况下:O(N*logN)
* 最坏情况下:O(N^2) 有序 / 逆序
* 空间复杂度:
* 最好情况下:O(logN)
* 最坏情况下:O(N)
* 稳定性:
* 不稳定的排序


4.挖坑法(方法二)

【图解】


在这里插入图片描述


【主要代码展示】


    /**
     * 挖坑法
     * @param array
     * @param left
     * @param right
     * @return
     */
    public static int partition(int[] array,int left,int right){
        int tmp = array[left];
        while (left < right){

            while (left < right && array[right] >= tmp){
                right--;
            }
            array[left] = array[right];

            while (left < right && array[left] <= tmp){
                left++;
            }
            array[right] = array[left];
        }

        array[left] = tmp;
        return left;
    }

【测试这个方法】

public static void main(String[] args) {
        int[] array = {3,5,2,10,9,8,17};
        Sort.quickSort(array);
        System.out.println(Arrays.toString(array));
    }

在这里插入图片描述


5.前后指针法(方法三)

【写法一】

/**
     * 前后指针法
     */
    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 partition3(int[] array, int left, int right) {
        int d = left + 1;
        int pivot = array[left];
        for (int i = left + 1; i <= right; i++) {
            if (array[i] < pivot) {
                swap(array, i, d);
                d++;
            }
        }
        swap(array, d - 1, left);

        return d - 1;
    }

二、快速排序优化

1.三数取中法选key

private static int middleNum(int[] array, int left,int right){
        //中间位置的下标
        int mid = left + ((right - left) >> 1);
        //怎么拿到这个下标对应的中间数字呢

        if (array[left] < array[right]){
            if (array[mid] < array[left]){
                return left;
            }else if (array[right] < array[mid]){
                return right;
            }else {
                return mid;
            }
        }else {
            if (array[right] > array[mid]){
                return right;
            }else if (array[mid] > array[left]){
                return left;
            }else {
                return mid;
            }
        }
    }

2.递归到小的子区间时,可以考虑使用插入排序


在这里插入图片描述


public static void insertSort2(int[] array,int start,int end) {
        //要求数组不为空
        for (int i = start + 1; i <= end; i++) {
            //将 i 下标的值放入 tmp 中
            int tmp = array[i];
            int j = i - 1;
            for (; j >= start; j--) {
                //j下标的值和i下标的值进行比较
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
//                    array[j + 1] = tmp;
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

3.快速排序非递归

    /**
     * 快排非递归
     */
    public static void quickSortNor(int[] array){
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length - 1;
        int pivot = partition(array,left,right);
        if (pivot - 1 > left){
            stack.push(left);
            stack.push(pivot - 1);
        }
        if (pivot + 1 < right){
            stack.push(right);
            stack.push(pivot + 1);
        }
        while (!stack.isEmpty()){
            right = stack.pop();
            left = stack.pop();
            pivot = partition(array,left,right);
            if (pivot - 1 > left){
                stack.push(left);
                stack.push(pivot - 1);
            }

            if (pivot + 1 < right){
                stack.push(right);
                stack.push(pivot + 1);
            }
        }
    }

三、完整代码

public static void quickSort(int[] array) {
        //首先要对待排序的元素进行划分
        //找到一个点,使左边比这个点小,右边比这个点大
        //可以以最左边或者最右边的元素作为基准
        //调用下面的方法进行划分
        quick(array, 0, array.length - 1);
    }

    private static void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }

        if ((end - start + 1) <= 19) {
            insertSort2(array, start, end);
        }

        //找基准之前,先对三数取中
        int index = middleNum(array, start, end);
        //找到中间元素下标之后
        //进行交换
        //把中间大的下标和第一个数字交换

        swap(array, index, start);

        //找到划分之后 基准元素的下标
        int pivot = partition(array, start, end);
        //以基准元素为点,又分左右两边
        //类似与二叉树的前序遍历
        quick(array, start, pivot - 1);//左边
        quick(array, pivot + 1, end);//右边
    }

    public static void insertSort2(int[] array, int start, int end) {
        //要求数组不为空
        for (int i = start + 1; i <= end; i++) {
            //将 i 下标的值放入 tmp 中
            int tmp = array[i];
            int j = i - 1;
            for (; j >= start; j--) {
                //j下标的值和i下标的值进行比较
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
//                    array[j + 1] = tmp;
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

    private static int middleNum(int[] array, int left, int right) {
        //中间位置的下标
        int mid = left + ((right - left) >> 1);
        //怎么拿到这个下标对应的中间数字呢

        if (array[left] < array[right]) {
            if (array[mid] < array[left]) {
                return left;
            } else if (array[right] < array[mid]) {
                return right;
            } else {
                return mid;
            }
        } else {
            if (array[right] > array[mid]) {
                return right;
            } else if (array[mid] > array[left]) {
                return left;
            } else {
                return mid;
            }
        }
    }

    /**
     * Hoare法
     *
     * @param array
     * @param left
     * @param right
     * @return
     */
    private static int partitionHoare(int[] array, int left, int right) {
        //这里以最左边的元素为基准元素
        int tmp = array[left];
        //要先记录下来 left 的值,因为在下面操作过程中,left 的值会变
        int i = left;
        while (left < right) {

            while (left < right && array[right] >= tmp) {
                right--;
            }

            while (left < right && array[left] <= tmp) {
                left++;
            }

            //进行交换
            swap(array, left, right);
        }
        //将基准元素交换到中间
        swap(array, i, left);
        //返回此时基准元素的下标
        return left;
    }


    /**
     * 挖坑法
     *
     * @param array
     * @param left
     * @param right
     * @return
     */
    public static int partition(int[] array, int left, int right) {
        int tmp = array[left];
        while (left < right) {

            while (left < right && array[right] >= tmp) {
                right--;
            }
            if (left >= right) {
                break;
            }
            array[left] = array[right];

            while (left < right && array[left] <= tmp) {
                left++;
            }
            if (left >= right) {
                break;
            }
            array[right] = array[left];
        }

        array[left] = tmp;
        return left;
    }

    /**
     * 前后指针法
     */
    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 partition3(int[] array, int left, int right) {
        int d = left + 1;
        int pivot = array[left];
        for (int i = left + 1; i <= right; i++) {
            if (array[i] < pivot) {
                swap(array, i, d);
                d++;
            }
        }
        swap(array, d - 1, left);

        return d - 1;
    }

总结


  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度: O(N*logN)

在这里插入图片描述

  1. 空间复杂度: O(logN)
  2. 稳定性:不稳定

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Python酷库之旅-第三方库Pandas(067)

目录 一、用法精讲 266、pandas.Series.dt.second属性 266-1、语法 266-2、参数 266-3、功能 266-4、返回值 266-5、说明 266-6、用法 266-6-1、数据准备 266-6-2、代码示例 266-6-3、结果输出 267、pandas.Series.dt.microsecond属性 267-1、语法 267-2、参数 …

集合基础知识及练习

import java.util.ArrayList;public class Solution {//将字符串转化为整数public static void main(String[] args) {ArrayList<String> listnew ArrayList();list.add("aaa");list.add("aaa");list.add("bbb");list.add("ccc"…

【Python】Django Web 框架

一、常用的Web开发框架 1.Django Django是一个由Python写成的开放源代码的Web应用框架。这套框架的主要目标是使开发复杂、数据库驱动的网站变得简单。Django注重组件的重用性和“可拔插性”、敏捷开发和DRY(Dont Repeat Yourself)法则 2.Flask Flask是一个微型的Python开发…

音视频开发 sdl库

介绍 SDL (Simple DirectMedia Layer) 是一个跨平台的开源多媒体库,它提供了底层访问多种硬件的接口,如音频、视频、输入设备等。它主要用于游戏开发,但也可用于其他类型的多媒体应用程序。下面是 SDL 的一些主要特点: 跨平台性: SDL 支持多种操作系统,包括 Windows、macOS、L…

如何在linux系统上安装tomcat应用程序?

1&#xff09;首先查看安装包信息 yum info tomcat yum info tomcat 2&#xff09;安装 yum -y install tomcat yum -y install tomcat 3&#xff09;查看安装是否成功 rpm -q tomcat rpm -q tomcat 4&#xff09;如果输出一下内容则代表安装成功 tomcat-7.0.76-16.el7_9.n…

新手教学系列——Redis 实现分布式锁:让系统更高效的两种策略

在分布式系统中,分布式锁是一种常见的解决方案,用于确保同一资源不会被多个节点同时访问。Redis 作为一种高性能的内存数据库,提供了方便快捷的分布式锁实现方式。今天,我们将深入探讨如何使用 Redis 实现分布式锁,并且介绍两种常见的策略:占位锁和等待锁。 一、什么是分…

Linux源码阅读笔记13-进程通信组件中

架构图 代码分析 loff_t lnchannel_llseek(struct file *filp, loff_t offset, int whence) {loff_t newpos;switch(whence) {case 0:newpos offset;break;case 1:newpos filp->f_pos offset;break;case 2:return -EINVAL;}if (newpos < 0 || newpos > LNCHANNEL_…

剪映国际版(CapCut) 2024 下载 安装 汉化

将 剪映国际版&#xff08;CapCut&#xff09; 2024 压缩包解压到本地&#xff1a; 点击蓝色字体下载压缩包 提取码 jwsg 鼠标右键 点击 CapCut 3.0.0.exe 选择 以管理员身份运行&#xff1a; 勾选 Agree with CapCut Users License Agreement & Pricacy Policy 点击 Mor…

基于SSM的哈米音乐实战项目,Java课程设计作业,Java毕业设计项目,找工作前的实战项目,附部分源码以及数据库

1、项目所需技术 java基础&#xff0c;java反射&#xff0c;泛型html&#xff0c;css&#xff0c;JavaScript&#xff0c;jquery&#xff0c;bootstrap&#xff0c;layuijstl&#xff0c;el表达式&#xff0c;jsp&#xff0c;mysql&#xff0c;jdbc&#xff0c;xml&#xff0c…

HashMap及其相关知识点

一、HashMap插入与删除 通过将key转换为hashCode&#xff08;int&#xff09;&#xff0c;通过hashCode计算下标&#xff0c;int index hashCode & (length - 1)&#xff0c;从而实现插入与删除。 二、Hash冲突 Java8之前&#xff1a;通过数组链表的数据结构解决hash冲…

【Java数据结构】Map和Set超详细两万字讲解(内含搜索树+哈希表)

&#x1f512;文章目录&#xff1a; 1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; 2. Map和Set的基础概念 3.Map的基础使用 4.Set的基础使用 5. TreeMap的本质——红黑树 5.1二叉搜索树的概念 5.2二叉搜索树的模拟实现 二叉搜索树——查找 二…

东莞网页设计结构图

东莞网页设计结构图是一个网页设计师在设计网站时使用的工具&#xff0c;用来展示网页的布局、内容结构和功能模块等信息。在设计一个网页时&#xff0c;网页设计师通常会首先制作一个网页设计结构图&#xff0c;以便更好地组织和规划网站的内容和功能。东莞网页设计结构图可以…

PFA的特点及焊接方法和应用领域

一、PFA特点&#xff1a; 1、外观半透明&#xff0c;易观察试剂情况&#xff1b; 2、耐高低温&#xff1a;-200&#xff5e;260℃&#xff1b; 3、耐腐蚀&#xff1a;耐强酸、强碱、王水、HF和各种有机溶剂&#xff1b; 4、防污染&#xff1a;本底值低&#xff0c;金属元素…

SpringBoot 整合 Spring Security 、JWT 实现认证、权限控制

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 前言 Spring Security 和 JWT 实现认证、权限控制是一种在Web应用中保障安全的重要手段。 Spring Security是一个功能强大的身份验证和访问控制框架&#xff0c;它提供了完善的认证机制和方法级的授权功能。…

手机维修--学习笔记(一)

最近觉得手机主板维修比较有意思&#xff0c;观看许多师傅的维修视频&#xff0c;并做笔记如下&#xff1a; 手机主板维修&#xff1a; 【手机电路板怎么修&#xff0c;师傅对着电路图一步一步讲解&#xff0c;看完受益匪浅】https://www.bilibili.com/video/BV13A411v7wL?v…

梯度和反向传播

一.梯度 在机器学习的时候都了解过了&#xff0c;梯度是一个向量&#xff0c;导数变化最快的方向 损失函数&#xff1a; 通过梯度使损失降到最 用ywxb举例也就是使用梯度来更新w的值&#xff0c;ww-学习率*梯度。大于零就减小&#xff0c;反之增大 二.反向传播 就比如搭积木…

【源码+文档+调试讲解】古风生活体验交流网站

摘 要 二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一…

24/8/5算法笔记 逻辑回归sigmoid

今日是代码对sigmoid函数的实现和运用 #linear_model线性回归 #名字虽然叫逻辑回归&#xff0c;作用于分类 #分类&#xff1a;类别 #回归&#xff1a;预测 from sklearn.linear_model import LogisticRegression 实现函数 import numpy as np import matplotlib.pyplot as pl…

Linux笔记-3()

目录 一、Linuⅸ实操篇-定时任务调度 二、Linuⅸ实操篇-Linuⅸ磁盘分区、挂载 三、Linux实操篇-网络配置 一、Linuⅸ实操篇-定时任务调度 1 crond任务调度---crontab进行定时任务的设置1.1 概述任务调度&#xff1a;是指系统在某个时间执行的特定的命令或程序。任务调度分类…

【python】OpenCV—Image Colorization

文章目录 1、CIELAB 色彩空间2、作色问题定义3、Caffe 模型4、代码实现——Image5、代码实现——Video6、参考 1、CIELAB 色彩空间 Lab颜色空间&#xff0c;也称为Lab色彩空间或CIELAB色彩空间&#xff0c;是一种基于人类视觉感知特性的颜色模型。它是在1931年国际照明委员会&…