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

目录

题外话

正题

 快速排序

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指向leftint prev=left;//cur指向prev+1int cur=prev+1;//当cur没有走完rightwhile (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去调用,而且调用三个方法的时候只需要把调用方法名字修改一下就可以了,很方便

我们已经讲完了

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

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

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

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

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BTS441RGATMA1 N沟道 43V 17A高侧电源开关芯片 英飞凌

BTS441RGATMA1是一款由Infineon Technologies制造的高侧开关电源芯片。 BTS441RGATMA1具有以下功能&#xff1a; 高端电源开关&#xff1a;BTS441RGATMA1是一种N通道功率场效应晶体管&#xff08;FET&#xff09;&#xff0c;用于高端电源开关应用。 高侧开关&#xff1a;该器件…

数据结构-堆详解

堆 图片&#xff1a; 二叉堆的父节点为这个子树的最值。 如何维护它。 我们发现它是一棵二叉树&#xff0c;那就自然满足若父节点为 x x x 则左儿子节点为 x 2 x\times2 x2 右儿子为 x 2 1 x\times 2 1 x21 这是显然的&#xff0c;但如果写成指针或结构体就太麻烦了&…

MacOS Docker 部署 Solr 搜索引擎

一、简介 Solr 是 Apache 下的一个顶级开源项目&#xff0c;采用 Java 开发&#xff0c;它是基于 Lucene 的全文搜索服务器。Solr 可以独立运行在 Jetty、Tomcat 等这些 Servlet 容器中。Solr 提供了比 Lucene 更为丰富的查询语言&#xff0c;同时实现了可配置、可扩展&#x…

免费使用Kimi的API接口,kimi-free-api真香

今年&#xff0c;随着各大GLM模型的开源和算力的提升&#xff0c;尤其是最近比较火热的月之暗面的Kimi 模型&#xff0c;AI应用场景中的各种智能体如雨后春笋般涌现。许多同学们纷纷表达了加入AI应用的学习和测试的愿望&#xff0c;然而各大模型提供商所提供的API的免费tokens在…

集群管理命令总结

前言 在进行TPC性能测试时用到两个集群管理命令&#xff1a;clush 和 pssh&#xff0c;这俩命令都可以在多台服务器上并发执行相同命令&#xff0c;其中个人比较推荐 clush&#xff0c;因为感觉 clush 比较好用&#xff0c;本文来总结一下 clush 和 pssh 的安装配置和使用。 …

读所罗门的密码笔记20_更好的自己

1. 民营部门 1.1. 障碍最大的一步&#xff0c;与履行协议所需要的人才和技能有关 1.2. 多样性很好&#xff0c;不要过分依赖任何一种资源&#xff0c;因为政府会出于政治原因淡化问题&#xff0c;不尊重科学&#xff0c;甚至退出协议 1.2.1. 民营部门和非政府组织的强大联盟…

在Mac中打开终端的3种方法

在使用Mac时&#xff0c;有时需要深入研究设置&#xff0c;或者完成一些开发人员级的命令行任务。为此&#xff0c;你需要终端应用程序来访问macOS上的命令行。下面是如何启动它。 如何使用聚焦搜索打开终端 也许打开终端最简单、最快的方法是通过聚焦搜索。要启动聚焦搜索&a…

算法1: 素数个数统计

统计n以内的素数个数 素数&#xff1a;只能被1和自身整除的自然数&#xff0c;0和1除外&#xff1b; 举例&#xff1a; 输入&#xff1a;100 输出&#xff1a;25 import java.util.*; class Test1{public static void main(String[] args){int a 100; //输入数字//…

rocketmq和rabbitmq总是分不清?

1. 官方解答 摘自百度搜索&#xff1a; 2. 通俗易懂的回答

CentOS7离线升级OpenSSH_8.8p1

一、环境 centos7.9,升级openssh到8.8p1最新版本 二、下载升级包 # openssl和zlib为相关依赖 wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-8.8p1.tar.gz wget https://www.openssl.org/source/openssl-1.1.1j.tar.gz wget http://www.zlib.net/zl…

C语言之offsetof实现分析(九十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

计算机网络——实现smtp和pop3邮件客户端

实验目的 运用各种编程语言实现基于 smtp 协议的 Email 客户端软件。 实验内容 1. 选择合适的编程语言编程实现基于 smtp 协议的 Email 客户端软件。 2. 安装 Email 服务器或选择已有的 Email 服务器&#xff0c;验证自己的 Email 客户端软件是否能进行正常的 Email 收发功…