数据结构之排序了如指掌(三)

目录

题外话

正题

 快速排序

Hoare法

Hoare法思路

Hoare法代码详解

挖坑法

挖坑法思路

挖坑法代码

前后指针法

前后指针法思路

前后指针法代码

小结


题外话

我们接着把没有写完的排序内容完成,快速排序其实大同小异,大家好好把思路整理一下

正题

 快速排序

快速排序一共有三种方法

1.Hoare法

2.挖坑法

3.前后指针法

Hoare法

Hoare法是以快速排序创始人Hoaer命名的方法

Hoare法思路

这三种方法都是通过递归排序

1.首先我们创建left和right分别指向0下标和数组最后一个元素下标

2.先建立一个基准值0下标元素,先让right从右到左一个一个去找到比0下标元素小的元素,再让left从左到右去一个一个去找比0下标大的元素

3.当right找到比基准值小的元素,并且left也找到比基准值大的元素之后,我们让right下标元素和left下标元素交换

4.当left和right相遇之后,我们把基准值和其中任意一个下标元素交换,这样基准值左边的元素就比基准值小,基准值右边的元素就比基准值大

5.然后我们再给基准值左边设立left和right然后再去循环这个过程排序,基准值右边也是同理

如果不理解请看下图

看到这两幅图会不会有种二叉树的感觉呢,一层一层的去遍历排序就是这样


Hoare法代码详解

先说一下咱们这个写代码的框架

因为框架真的很重要!!

我们写代码的时候要尽量解耦合,并且提高扩展性

下图A方法中包含B方法

当我们要修改A方法的时候为了正常可以运行,可能会影响到B方法,还要去B方法里修改B方法

而当我们写代码的时候让A和B是一个独立的个体,再由C去调用他们,当修改A或者B中的任意一个代码的时候不会影响到另一方

我们本篇内容要写Hoare法,挖坑法和前后指针法

下面代码大家可以自己观察一下框架

我先列出我们要做的几件事

1.找到基准值,并且使基准值已经交换到数组中间位置

2.从找到的基准值左边去递归数组并排序,并且再次找左边基准值

3.从找到的基准值右边去递归数组并排序,并且再次找右边基准值

4.递归这个过程

代码详解

public void quickSort(int[] arr)
    {
        //调用quick方法并传参数
        quick(arr,0, arr.length-1);
    }


    private void quick(int[] arr,int start,int end)
    {
        //start如果大于等于end说明已经相遇,也说明当前这边已经排完序了
        if (start>=end)
        {
            return;
        }
        //调用Hoare法先让第一次的基准值到数组中间位置
        int pivot=partitionHoare(arr,start,end);
        //然后递归基准值左边并排序
        quick(arr,start,pivot-1);
        //递归基准值右边并排序
        quick(arr,pivot+1,end);
    }

    private int partitionHoare(int[] arr, int left, int right) {
        //先将基准值元素给到tmp
        int tmp=arr[left];
        //并将基准值下标给到i,以免找不到基准值下标,无法交换
        int i=left;
        //当left小于right时
        while (left<right)
        {
            //我们在此处while循环也需要加上left<right条件,否则left可能大于right
            //并且right下标元素大于等于基准值的时候right--
            while (left<right&&arr[right]>=tmp)
            {
                right--;
            }
            //走到这里说明right找到比基准值小的元素
            //left开始找比基准值大的元素,如果left下标元素小于基准值,left++
            while (left<right&&arr[left]<= tmp)
        {
            left++;
        }
            //走到这里说明left找到了比基准值大的元素,right找到了比基准值小的元素,此时将left元素和right元素交换
            //并继续进行while循环
            swap(arr,left,right);
        }
        //走到这里说明left和right相遇,则交换基准值元素和相遇点元素
        swap(arr,i,left);
        //交换之后,此时left就是基准值下标,返回即可
        return left;
    }
    //挖坑法
    private int partitionHole(int[] arr,int left,int right)
    {
        int tmp=arr[left];
        while (left<right)
        {
            while (left<right&&arr[right]>=tmp)
            {
                right--;
            }
            arr[left]=arr[right];

            while (left<right&&arr[left]<= tmp)
        {
            left++;
        }
            arr[right]=arr[left];
        }
        arr[left]=tmp;
        return left;
    }
//交换代码
 public void swap(int[] arr,int a,int b)
    {
        int tmp=arr[a];
        arr[a]=arr[b];
        arr[b]=tmp;
    }

挖坑法

挖坑法思路

1.将基准值保存在临时变量(相当于挖掉挖坑),并且从右边找比基准值小的,左边找比基准值大的

2.从右边找到比基准值小的,放入坑中,此时又出现一个新的坑

3.从左边找比基准值大的,放入坑中,此时又新出现一个坑

4.循环上述过程,当left和right相遇的时候,无论是谁相遇谁,left和right相遇位置一定会有个坑,然后把临时变量也就是基准值放入坑中

5.从左边建立新的基准值递归这个过程

6.从右边建立新的基准值递归这个过程

以上觉得我写的无法理解清楚,请看下图

所有的思路就是这样,就是先把基准值挖坑,然后从右边找,从左边找去填坑,然后相遇位置一定是坑,把基准值放入坑,递归左边和右边即可

挖坑法代码

public void quickSort(int[] arr)
{
    //调用quick方法并传参数
    quick(arr,0, arr.length-1);
}

private void quick(int[] arr,int start,int end)
{
    //start如果大于等于end说明已经相遇,也说明当前这边已经排完序了
    if (start>=end)
    {
        return;
    }
    //调用挖坑法,先让第一次的基准值到数组中间位置
    int pivot=partitionHole(arr,start,end);
    //然后递归基准值左边并排序
    quick(arr,start,pivot-1);
    //递归基准值右边并排序
    quick(arr,pivot+1,end);
}
private int partitionHole(int[] arr,int left,int right)
{
    //默认为最左边为基准值,将基准值挖坑放入临时变量
    int tmp=arr[left];
    //当left没有和right相遇
    while (left<right)
    {
        //当left没有和right相遇并且right还没有找到比基准值小的元素
        while (left<right&&arr[right]>=tmp)
        {
            //向左继续寻找
            right--;
        }
        //走到这说明找到了,然后把坑填上
        arr[left]=arr[right];
        //当left和right没有相遇,并且left没有找到比tmp大的元素
        while (left<right&&arr[left]<= tmp)
    {
        //继续向右寻找
        left++;
    }
        //找到了,此时将right位置的坑填上
        arr[right]=arr[left];
    }
    //走到这里说明left和right相遇了,把基准值填入相遇点
    arr[left]=tmp;
    //返回基准值
    return left;
}

前后指针法

这个方法和上述两个方法略有不同

前后指针法思路

1.让0下标作为基准值

2.left指向0下标,right指向数组最后一个元素

3.创建prev指向left,创建cur指向prev+1

4.当cur没有走完数组最后一个元素,也就是cur<=right

5.让cur一直从左往右去找到比cur小的元素并且满足++prev!=cur,说明prev再往前走一个,此时prev等于cur

6.prev只有在cur找到比基准值小的元素才会往前走,而cur无论有没有找到prev都会往前走

7.prev没有往前走,也就说明了,cur往前走的时候,找到的元素大于基准值

8.如果满足上面条件,就交换cur和prev元素,cur继续往前走,直到走到right位置

9.当走完right,让基准值和prev元素交换,最后返回prev,继续递归遍历prev左边和prev右边

这就有一种cur推着prev走的感觉,只有cur满足条件,prev才有可能往后走

画个图大家就懂了

大家只要清楚的明白cur和prev的满足条件即可

前后指针法代码

public void quickSort(int[] arr)
{
    //调用quick方法并传参数
    quick(arr,0, arr.length-1);
}
private void quick(int[] arr,int start,int end)
{
    //start如果大于等于end说明已经相遇,也说明当前这边已经排完序了
    if (start>=end)
    {
        return;
    }
    //先让第一次的基准值到数组中间位置
    int pivot=partition(arr,start,end);
    //然后递归基准值左边并排序
    quick(arr,start,pivot-1);
    //递归基准值右边并排序
    quick(arr,pivot+1,end);
}
private int partition(int[] arr,int left,int right)
{
    //让prev指向left
    int prev=left;
    //cur指向prev+1
    int cur=prev+1;
    //当cur没有走完right
    while (cur<=right)
    {
        //重点!!  如果cur中的元素比基准值小并且++prev后所在的位置不是cur的位置
        if (arr[cur]<arr[left]&&arr[++prev]!=arr[cur])
        {
            //才会交换prev和cur元素值
            swap(arr,prev,cur);
        }
        //cur不管满不满足以上条件都会往后走,而prev只有满足arr[cur]<arr[left]这个条件,才会往后走,因为是短路与
        //前面条件不满足,不会进行后面条件判断
        cur++;
    }
    //当cur走完right位置,就把基准值和prev交换位置,返回基准值下标
    swap(arr,left,prev);
    return prev;
}

小结

我们可以看这三个方法代码,他们被调用的时候都是通过quick去调用,而且调用三个方法的时候只需要把调用方法名字修改一下就可以了,很方便

我们已经讲完了

直接插入排序(摆扑克牌)

希尔排序(分组摆扑克牌)

冒泡排序(相邻两个元素只要左边比右边大就交换位置,每趟会将数组现存最大元素放到最后面)

堆排序(建立大根堆,然后堆顶元素必然是堆中最大的元素,然后将堆顶元素和最后一个元素交换位置,除去交换的元素,将交换后的堆重新建立大根堆并循环以上过程)

选择排序(找基准值然后排序,再从基准值左边找基准值排序,再从基准值右边找基准值排序)

昨晚熬了个小夜,今天更要加油努力!!!!

喜欢的话记得给个三连!!!(点赞关注收藏)

有问题请在评论区留言或者私信!!

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

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

相关文章

WideDeep

这里写目录标题 1. 背景2. 贡献3 模型结构&#xff08;1&#xff09;任务定义&#xff08;2&#xff09;The Wide Component&#xff08;3&#xff09;The Deep Component&#xff08;4&#xff09;联合训练Wide和Deep Model 4. 参考 1. 背景 (1) 广义线性回归通常被用于推荐模…

树莓派+Openwrt连接校园网,打破校园网设备限制

前言 因为本校学生校园网只允许最多三个设备登录&#xff0c;对于同时拥有多个联网设备的我十分不友好&#xff0c;而且大多单片机如esp32的wifi模块是只允许一般的WPA/WPA2认证的&#xff0c;是不支持校园网的portal认证。所以我决定搞一个路由器。 然后我上网买了一个TP-Li…

Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决

Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决 文章目录 Android Studio 新建Android13 代码提示Build Tools revision XX is corrupted无法编译解决一、前言二、分析解决1、原因分析2、解决方法 三、其他1、Android13 新项目无法编…

采用matplotlib可视化kitti

配置kitti_object_vis没成功&#xff0c;用kitti_object_vis的一些函数加上matplotlib进行可视化 import numpy as np import matplotlib.pyplot as pltimport numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def roty(t):"&quo…

JavaWeb-登录校验

会话技术 浏览器使用的是http协议&#xff0c;多次请求间数据是不能共享的&#xff0c;例如我们要去访问用户数据的接口&#xff0c;但这时候用户是否已经登入了呢&#xff1f;是不知道的&#xff0c;为了解决这个问题&#xff0c;于是引入了会话跟踪技术。 会话&#xff1a;…

05—js对象

一、初识对象 JavaScript是面向对象编程&#xff08;Object Oriented Programming&#xff0c;OOP&#xff09;语言。 面对象是一种复合值&#xff1a;它将很多值集合在一起&#xff0c;可通过名字访问这些值。对象也可看做一种无序的数据集合&#xff0c;由若干个“键值对”…

iced 入门一

&#x1f4d5;作者简介&#xff1a; 过去日记&#xff0c;致力于Java、GoLang,Rust等多种编程语言&#xff0c;热爱技术&#xff0c;喜欢游戏的博主。 &#x1f4d8;相关专栏Rust初阶教程、go语言基础系列、spring教程等&#xff0c;大家有兴趣的可以看一看 &#x1f4d9;Jav…

基于ssm的企业在线培训系统论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装企业在线培训系统软件来发挥其高效地信息处理的作用&#x…

每日一题

腐烂的苹果_牛客题霸_牛客网 思路分析:广度优先遍历&#xff0c;找到所有腐烂的苹果同时向四方扩散&#xff0c;就是第一轮把所有腐烂的苹果加入队列中&#xff0c;这就跟MQ的消息队列的原理差不多&#xff0c;第一次记录队列的长度&#xff0c;广度遍历一次&#xff0c;长度--…

第一个STM32F767IGT6核心板

一. 制作原因 起先是因为参加计算机设计大赛准备的板子&#xff0c;其作用是连接OV5640摄像头来识别车牌号&#xff0c;主要外设有摄像头&#xff0c;SDRAM&#xff0c;网口等。 二. 原理图和PCB 原理图 PCB 三. 测试 1. 测试SDRAM功能 按下按键我们可以在串口中看到内存…

【基础IO】谈谈动静态库(怒肝7000字)

文章目录 前言实验代码样例静态库生成一个静态库归档工具ar静态库的链接 动态库创建动态库加载动态库 动静态链接静态链接动态链接动静态链接的优缺点 前言 在软件开发中&#xff0c;库&#xff08;Library&#xff09;是一种方式&#xff0c;可以将代码打包成可重用的格式&…

【C语言】内存函数-memcpy-memmove-memset...用法及实现,沉淀自己!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 目录 1. memcpy函数使用和模拟实现 2. memmove使用和模拟实现 3. memset函数的使用 4. memcmp函数的使用 1. memcpy函数使用和模拟实现 <string.h>-------…

机器学习理论基础—神经网络算法公式学习

机器学习理论基础—神经网络公式学习 M-P神经元 M-P神经元&#xff08;一个用来模拟生物行为的数学模型&#xff09;&#xff1a;接收n个输入(通常是来自其他神经 元)&#xff0c;并给各个输入赋予权重计算加权和&#xff0c;然后和自身特有的阈值进行比较 (作减法&#xff0…

pytorch-MNIST测试实战

这里写目录标题 1. 为什么test2. 如何做test3. 什么时候做test4. 完整代码 1. 为什么test 如下图&#xff1a;上下两幅图中蓝色分别表示train的accuracy和loss&#xff0c;黄色表示test的accuracy和loss&#xff0c;如果单纯看train的accuracy和loss曲线就会认为模型已经train…

【优质书籍推荐】Vue.js+Node.js全栈开发实战

大家好&#xff0c;我是爱编程的喵喵。双985硕士毕业&#xff0c;现担任全栈工程师一职&#xff0c;热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。…

von Mises-Fisher Distribution (代码解析)

torch.distribution 中包含了很多概率分布的实现&#xff0c;本文首先通过均匀分布来说明 Distribution 的具体用法, 然后再解释 von Mises-Fisher 分布的实现, 其公式推导见 von Mises-Fisher Distribution. 1. torch.distribution.Distribution 以下是 Uniform 的源码: cl…

怎么使用JMeter进行性能测试?

一、简介 JMeter是Apache软件基金会下的一款开源的性能测试工具&#xff0c;完全由Java开发。它专注于对我们应用程序进行负载测试和性能测量&#xff0c;最初设计用于web应用程序&#xff0c;现在已经扩展到其他测试功能&#xff0c;比如&#xff1a;FTP、Database和LDAP等。…

Vue 3 项目构建与效率提升:vite-plugin-vue-setup-extend 插件应用指南

一、Vue3项目创建 前提是已安装Node.js&#xff08;点击跳转Node官网&#xff09; npm create vuelatest这一指令将会安装并执行 create-vue&#xff0c;它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示&#xff1a; ✔ Projec…

跟着Carl大佬学leetcode之977 有序数组的平方

来点强调&#xff0c;刷题是按照代码随想录的顺序进行的&#xff0c;链接如下https://www.programmercarl.com/本系列是记录一些刷题心得和学习过程&#xff0c;就看到题目自己先上手试试&#xff0c;然后看程序员Carl大佬的解释&#xff0c;自己再敲一遍修修补补&#xff0c;练…

electron打包dist为可执行程序后记【electron-quick-start】

文章目录 目录 文章目录 前言 一、直接看效果 二、实现步骤 1.准备dist文件夹 2.NVM管理node版本 3.准备electron容器并npm run start 4.封装成可执行程序 1.手动下载electron对应版本的zip文件&#xff0c;解决打包缓慢问题 2.安装packager 3.配置打包命令执行内容…