首页 > 编程语言 >基础算法-快速排序

基础算法-快速排序

时间:2023-04-21 20:13:52浏览次数:39  
标签:arr int 元素 指针 枢轴 算法 排序 快速 left

思路

快速排序是一种常见的排序算法,它的基本思路是通过分治的方法将一个大的问题分解成小的问题进行解决。具体而言,快速排序的核心思路是选取一个枢轴元素,将序列分为两个子序列,其中一个子序列的所有元素都比枢轴元素小,而另一个子序列的所有元素都比枢轴元素大,然后对这两个子序列分别进行递归排序,直到子序列的长度为1或0。

在这个实现中,quickSort 方法接受一个整数数组、左边界 left 和右边界 right 作为参数。如果 left 小于 right,则递归地对左边的子序列和右边的子序列进行排序。partition 方法用于将数组分成两个子序列,并返回枢轴元素的索引。在 partition 方法中,我们选取第一个元素作为枢轴元素,并将数组分成两个子序列。然后,我们使用两个指针 ij 分别从左边和右边开始扫描,直到找到一个大于等于枢轴元素的元素和一个小于等于枢轴元素的元素。然后,我们交换这两个元素,并将指针向中间移动,继续扫描直到指针相遇。最后,我们将枢轴元素与指针 j 所指向的元素交换,并返回 j 作为枢轴元素的索引。

public class QuickSort {

    public static void quickSort(int arr[], int left, int right) {
        if (left < right) {
            // 获得枢轴位置
            int pivot = partition(arr, left, right);
            // 快速排序左边
            quickSort(arr, left, pivot - 1);
            // 快速排序右边
            quickSort(arr, pivot + 1, right);
        }
    }

    public static int partition(int arr[], int left, int right) {
        int pivot = arr[left]; // 枢轴
        int i = left + 1;
        int j = right;

        while (true) {
            while (i <= j && arr[i] < pivot)
                ++i; // 找,直到i<=j或者arr[i]的值小于枢轴值
            while (i <= j && arr[j] > pivot)
                --j; // 找,直到i<=j或着arr[j]的值大于枢轴值

            // 上面的条件不满足了,先看看是不是i>j了
            if (i >= j) {
                break;
            }
            // 不是,那么当arr[i]的值小于枢轴值或者arr[j]的值大于枢轴值
            // 一个是去左边,一个是去右边
            swap(arr, i, j);
            ++i;
            --j;
        }
        // 一次划分结束,把j和枢轴的位置换一下(应为j就是新的枢轴位置)
        // 为什么呢,应为j右边都是大于枢轴的,j左边都是小于枢轴的,那么现在枢轴就应该在j这个位置
        // 应为pivot值在left这个位置,所以left和j换
        swap(arr, j, left);
        // 换完了,返回这次的枢轴值
        // 以这个枢轴值为界,进行下次快速排序
        return j;
    }

    public static void swap(int[] arr, int i, int j) {
        arr[i] = arr[i] ^ arr[j];
        arr[j] = arr[i] ^ arr[j];
        arr[i] = arr[j] ^ arr[i];
  

疑问

为什么partition代码里最后需要将left和j位置上的值调换呢

在快速排序中,swap(arr, left, j) 是将枢轴元素交换到正确的位置上,即将枢轴元素放在左边子序列和右边子序列之间的位置上。

具体而言,j 是从右边开始扫描的指针,当它扫描到一个小于等于枢轴元素的元素时,它会停止扫描。此时,我们需要将这个元素与枢轴元素交换位置,以便将枢轴元素放在正确的位置上。而枢轴元素原本所在的位置是 left,因此我们需要将 arr[left]arr[j] 交换位置。

例如,对于数组 [3, 5, 1, 6, 4, 8, 2, 9, 7],假设我们选取第一个元素 3 作为枢轴元素,然后执行一次 partition 操作,得到子序列 [2, 1][3][6, 4, 8, 9, 7, 5]。在这个过程中,j 会停在值为 2 的位置上,然后执行 swap(arr, left, j),即交换 arr[0]arr[2],得到 [2, 1, 3, 6, 4, 8, 9, 7, 5],此时枢轴元素 3 就在子序列 [2, 1][6, 4, 8, 9, 7, 5] 之间的位置上了。

如果不执行 swap(arr, left, j),那么枢轴元素 3 就会留在原地,即子序列 [2, 1, 3][6, 4, 8, 9, 7, 5] 之间,这样会导致后续的排序操作出现错误。

为什么当它扫描到一个小于等于枢轴元素的元素时,它会停止扫描,明明他前面还有个i<=j的条件,如果该条件不成立同样会跳出循环

在快速排序中,循环条件 i <= j 的作用是保证左右指针不会越界,即在扫描过程中,左指针 i 不会超过右指针 j 的位置。如果左指针 i 超过了右指针 j,那么循环就应该停止,因为此时扫描过程已经完成。

而当右指针 j 扫描到一个小于等于枢轴元素的元素时,它就停止扫描,因为这个元素应该被放在左边子序列中,而此时左指针 i 可以继续向右扫描。因此,我们需要将枢轴元素放在左右子序列之间的位置上,即将枢轴元素和右指针 j 所指向的元素交换位置,然后左指针 i 继续向右移动。

当左指针 i 扫描到一个大于等于枢轴元素的元素时,它也会停止扫描,因为这个元素应该被放在右边子序列中,而此时右指针 j 可以继续向左扫描。同样地,我们需要将右指针 j 所指向的元素和枢轴元素交换位置,然后右指针 j 继续向左移动。

在快速排序的扫描过程中,左指针 i 和右指针 j 是互相交替向前移动的,因为它们的移动方向是相反的。当左指针 i 和右指针 j 相遇时,扫描过程就结束了,因为此时左右子序列已经被划分好了。最后,我们需要将枢轴元素放在正确的位置上,即将枢轴元素和右指针 j 所指向的元素交换位置。

标签:arr,int,元素,指针,枢轴,算法,排序,快速,left
From: https://www.cnblogs.com/maoqifansBlog/p/17341618.html

相关文章

  • 基础算法-堆排序
    思路堆是一种完全二叉树,其中每个节点的值都大于或等于其子节点的值,被称为“大根堆”;或者每个节点的值都小于或等于其子节点的值,被称为“小根堆”。在堆排序中,我们使用的是大根堆,即根节点的值是最大的元素。堆排序的基本思路是:建立一个大根堆。将待排序的序列构建成一个大根堆,......
  • 基本算法-基数排序
    思想当我们需要对一组数据进行排序时,常规的排序算法(如快速排序、归并排序等)通常是比较排序,即通过比较元素之间的大小关系来进行排序。但有时候我们需要对一组数据按照它们的“数字位”进行排序,此时比较排序并不是最优的选择,这时候基数排序就显得非常有效了。基数排序是一种非比......
  • ruoyi+EasyCode的快速开发
    https://www.cnblogs.com/SjhCode/p/EasyCode.html1.导入ruoyi模板项目若依官网:http://doc.ruoyi.vip/ruoyi/例子:https://baijiahao.baidu.com/s?id=1716723778024031543&wfr=spider&for=pc代码:gitclonehttps://gitee.com/y_project/RuoYi.git初学ruoyi可以看这位博主的视......
  • 基于RL(Q-Learning)的迷宫寻路算法
    强化学习是一种机器学习方法,旨在通过智能体在与环境交互的过程中不断优化其行动策略来实现特定目标。与其他机器学习方法不同,强化学习涉及到智能体对环境的观测、选择行动并接收奖励或惩罚。因此,强化学习适用于那些需要自主决策的复杂问题,比如游戏、机器人控制、自动驾驶等。强化......
  • Redis 为何使用Nearly LRU 算法淘汰数据
    Redis使用该LRU算法淘汰过期数据吗?不是的。由于LRU算法需要用链表管理所有的数据,会造成大量额外的空间消耗。大量的节点被访问就会带来频繁的链表节点移动操作,从而降低了Redis性能。Redis的内存空间是很宝贵的,而维护LRU的双向链表需要使用比较多的额外空间,至少需要一......
  • JVM垃圾回收机制之对象回收算法
    在前面的文章中,介绍了JVM内存模型分为:堆区、虚拟机栈、方法区、本地方法区和程序计数器,其中堆区是JVM中最大的一块内存区域,在Java中的所有对象实例都保存在此区域,它能被所有线程共享。在Java中还有一个重要的机制:GC(垃圾收集器),堆是GC管理的主要区域,本文会带大家了解GC机制。GC......
  • Python用哈希算法查找相似图片(包括不同分辨率,不同大小,不同格式的图片)
    #-*-coding:utf-8-*-'''Python用哈希算法查找相似图片并放入[_df]的文件夹中相似图片包括不同分辨率,不同大小,不同格式,只要图片相似就会算重复文件安装cv2pipinstallopencv-python'''importosimportcv2importnumpyasnpimportshutilimportrandomclas......
  • springboot框架快速整合websocket
    1、【pom.xml】<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>2、【MsgType.java】/***@authorJHL*2019-08-109:56*/publicenumM......
  • 从原理聊JVM(一):染色标记和垃圾回收算法
    作者:京东科技 康志兴1JVM运行时内存划分1.1运行时数据区域•方法区属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池,属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用。JDK1.8之前,Hotspot虚拟机对......
  • Diffie-Hellman密钥交换算法
    目录前置知识密钥交换算法隐私计算常用到各种加密算法,那么双方如何协商得到同一个不被泄露的密钥呢?一种做法是基于RSA,拥有公钥的一方将随机私钥加密提供给对方,对方再利用私钥解密出密钥。于是双方都得到了会话密钥。上面这种基于非对称加密的方法是SSL最古老的密钥协商方式,早......