简单(炫酷)的单链表快速排序写法

2023-05-16

昨天在复习快排的时候,在B站看到一个小哥哥说某大厂的面试让写一个单链表的快速排序. 我们见的最多的快排写法都是从两端向中间扫描,这种写法在单链表上不能实现. 哥们分析道:快排的核心思想是每次扫描后,所有pivot左侧的元素都比pivot小, 右侧的元素都比pivot大, 然后递归就可以了. 不过这位小哥哥写的代码稍微有点复杂, 而且在数组上的写法leetcode过不了. 于是我找了找博客,找到一个简单版本的. 原文来自:分享一种比较简单易懂的快速排序思路,图解.

简单版数组快排: leetcode912 AC

class Solution{
public:
	vector<int> sortArray(vector<int>& arr) {
		quickSort(arr, 0, arr.size()-1);
		return arr;
	}
    void quickSort(vector<int>& arr, int low, int high) {
       if (low >= high) return; 
       int slow = low; 
       int pivot = arr[high];
       for (int fast = low; fast < high; fast++) {
           if (arr[fast] <= pivot) {
               swap(arr[fast], arr[slow]);
               slow++; 
           }
       }
       swap(arr[slow], arr[high]); 
       quickSort(arr, low, slow - 1);
       quickSort(arr, slow + 1, high);
   }
};

原文是java代码,这里我改成C++的代码了。这种写法思路明确, 实现简单. 主要思想就是每次递归将数组的最后一个元素作为pivot, 用两个指针辅助遍历. fast指针指向待遍历的元素, slow指针指向下一个待交换的位置,这个位置的左边所有元素都小于pivot. 因为slow指针指向的元素一定大于pivot, 所以一次遍历完成之后把high位置的元素和pivot元素交换即可完成一次递归.

把上面的代码简化下:

class Solution{
public:
	vector<int> sortArray(vector<int>& arr) {
		quickSort(arr, 0, arr.size()-1);
		return arr;
	}
    void quickSort(vector<int>& arr, int low, int high) {
       if (low >= high) return; 
       int slow = low;
       for (int fast = low; fast < high; fast++) if (arr[fast] <= arr[high]) swap(arr[fast], arr[slow++]);
       swap(arr[slow], arr[high]); 
       quickSort(arr, low, slow - 1);
       quickSort(arr, slow + 1, high);
   }
};

简化后的代码quickSort函数只有6行, 很简单, 但是不建议这么些, 代码写太简化和太复杂,个人认为可读性都比较差,哈哈.
OK, 数组的简单快排写好了,那就可以写链表的了. 不过仔细一分析, 问题就出现了. 如果拿最后一个元素做每次迭代的pivot, 那么起始的参数需要指向链表最后一个元素的指针, 也就是说, 第一次迭代之前还要遍历一次数组, 这种写法显然不够炫酷. 除此之外,每次向下迭代的的时候还需要一个pre指针记录slow指针的前驱指针, emmmm, 还是比较麻烦的.
那就来尝试修改代码, 让每次递归的pivot为数组的第一个元素.如果直接修改代码那问题又来了. 因为slow指针在遍历结束之后指向的元素一定是第一个大于pivot的, 那跟pivot交换之后, pivot左边的子数组的第一个元素还是大于pivot,不满足我们的要求. 那我们的思路就是让每次遍历完之后slow指针都指向最后一个小于等于pivot的元素, 之后跟pivot交换,就能实现一次递归了. 为了达到这个目的, 需要对slow的初始位置和前进条件做点修改. 我们把待遍历的数组看成两部分: pivot部分和剩余部分. 划分开就是arr[0] 和 arr[1]-arr[n]两部分(n为数组最后一个元素下标). 那为了实现slow最后指向最后一个小于等于pivot的数, 我们就要slow指针的元素与fast指针的元素修改后, slow指针不移动. 但是slow不移动的话, 不就原地打转了吗. 我们可以把slow的移动条件修改为,

if (arr[fast] <= pivot){
slow++;
swap(arr[slow], arr[fast]);
}

初始条件为:

slow = low;
fast = low + 1;

也就是指针指向带交换位置的前一个元素, fast指针发现有小于pivot的元素后, slow指针向后移动. 那代码实现起来就成了:

class Solution{
public:
	vector<int> sortArray(vector<int>& arr) {
		quickSort(arr, 0, arr.size()-1);
		return arr;
	}
    void quickSort(vector<int>& arr, int low, int high) {
       if (low >= high) return;
       int slow = low; 
       int pivot = arr[low];
       for (int fast = low+1; fast <= high; fast++) {
           if (arr[fast] <= pivot) {
               slow++; 
               swap(arr[fast], arr[slow]);
           }
       }
       swap(arr[slow], arr[low]); 
       quickSort(arr, low, slow-1);
       quickSort(arr, slow + 1, high);
   }
};

OK, 这样的代码修改成链表的就很简单了. 直接上代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* sortInList(ListNode* head) {
        quickSort(head, NULL);
        return head;
    }

    void quickSort(ListNode* head, ListNode* high){
		if(head == high || head->next == high) return;
		// head==high 也可以, 但是会判断长为1的子链表, 加上 head->next == high 就可以跳过长为1的子链表
		
        int pivot = head->val;
        
        ListNode* slow = head;
        ListNode* fast = head->next; 
        while(fast!=high){
            if(fast->val <= pivot){
                slow = slow->next;
                swap(slow->val, fast->val);
            }
            fast = fast->next;
        }
        
        swap(head->val, slow->val);
        quickSort(head, slow);
        quickSort(slow->next, high);
    }
};

到此, 简单(炫酷)的单链表快排写法就完成了. 本文所写的顺向数组在leetcode912题通过测试. 链表排序在leetcode148中, 测试样例通过,但是超时. 我把leetcode148题官方解答的归并排序和我写的代码在牛客上对比之后, 该代码比牛客148的归并反而快一点. 可能原因是来自leetcode的测试用例, 因为快排时间复杂度最坏为O(N^2), 而归并是O(logN). 快排在leetcode的测试用力中遇到了较坏的情况. 牛客的题号为NC70.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

简单(炫酷)的单链表快速排序写法 的相关文章

  • c++ 数学库

    链接 link
  • vscode使用restClient实现各种http请求

    vscode使用restClient实现各种http请求 一 xff0c 安装插件 首先 xff0c 我们要在vscode的扩展中 xff0c 搜索rest Client xff0c 然后安装它 xff0c 这里我已经安装过了 安装后 xf
  • K210和STM32串口通信(亲测有效)

    声明 最近想做一个K210数字识别和寻迹 xff0c 方便完成2021年电赛F题 xff0c 完成了数字训练和脱机运行就想赶紧进行一次通信 xff0c 调了好几天 郁闷 xff0b 自闭几天 按照官方的历程看 xff0c 配置的没问题但是会
  • 简单Rabbitmq 发送消息和接收消息

    简单Rabbitmq 发送消息和接收消息 1 先在Rabbitmq配置文件中预先创建好交换器 xff0c 队列 xff0c 路由等信息 2 创建生产者发送消息 64 Autowired private RabbitTemplate rabb
  • Elasticsearch(ES6)------(4) ES设置用户名密码访问

    Elasticsearch ES xff08 1 xff09 下载 安装 43 kibana 下载 xff08 2 xff09 本机多节点启动 43 ElasticSearch head插件使用 xff08 3 xff09 索引 文档概念和
  • Elasticsearch(ES6) --根据条件修改字段值

    POST index name doc update by query 34 query 34 34 match 34 34 version 34 34 12 22 34 34 script 34 34 inline 34 34 ctx s
  • redis限流使用lua脚本

    lua脚本 xff0c 计数器限流 5秒内限流10次 64 param key 64 return public boolean acquire String key long now 61 System currentTimeMillis
  • ES6分页from+size、search_after两种查询

    1 from 43 size 分页查询 64 RequestMapping value 61 34 get 34 method 61 RequestMethod GET public BaseResponse lt List lt Obje
  • 使用activiti总结--bpmn画流程图

    假期结束 xff0c 赶紧总结一下前几天使用的Activiti工作流的一些方法 简单介绍一下Activiti Activiti一套完整的方便的业务流程管理 xff08 BPM xff09 框架 xff0c 它是覆盖了业务流程管理 工作流 服
  • clock函数 使用以及问题

    使用 clock 函数是一个计算程序运行时间 xff08 其实简略的理解为占用CPU的使用时间 xff09 其实如果使用sleep函数 xff0c 程序是放弃CPU的使用权 xff0c 直到某个时间的到来 xff0c 当然就不会存在占用CP
  • 使用activiti总结--发布,办理,查询

    接上一篇文章 xff0c 使用创建好的流程图 xff0c 总结一下activiti发布到查询使用的方法和测试代码 流程图 1 引用配置文件 activiti cfg xml xff0c 不引用或者引用失败的话在创建流引擎的时候会报空指针异常
  • Could not open JDBC Connection for transaction

    操做 xff1a 访问20次数据库没问题 xff0c 超过20次调用后报如下错误 详细报错 xff1a org springframework transaction CannotCreateTransactionException Cou
  • 【可信计算】第八次课:可信软件栈编程开发

    TPM2开源软件包 目前在github上TPM2开源软件一共包含六个项目tpm2 tools tpm2 tss tpm2 pkcs11 tpm2 tss engine tpm2 abrmd tpm2 totp 1 tpm2 tools 这一
  • 国赛----可见光室内定位

    初期试探 拿到题目后 xff0c 反复读了题目 首先队内结合网上资料形成了两种方案 xff0c 原理相同就是利用光信号到达的强度来定位 两者差距在于算法不同而已 xff0c 一种利用计算得出位置 xff0c 另一种就是经过测量得到一种位置与
  • 各种Arduino外部中断程序

    一 中断 Interrupt 的基本概念 中断 xff08 Interrupt xff09 是计算机的一个重要概念 xff0c 现代计算机普遍采用中断技术 什么是中断呢 xff1f CPU执行时原本是按程序指令一条一条向下顺序执行的 但如果
  • PX4通过参数脚本给飞控导入参数

    PX4通过参数脚本给飞控导入参数 先找一架正常能飞的无人机连接地面站 在参数页面右上角点击工具 gt 保存到文件 保存的时候文件名注明参数的相关信息 然后将需要加载参数的无人机连接至地面站 xff0c 注意需要加载参数的无人机必须和保存的参
  • 深蓝 运动规划

    文章目录 CH1 Introduction一 Motion Planning 概念二 Front end xff1a Path finding1 Search based methods2 Sampling based methods3 K
  • 接收机/DTU 安装调试 读取gps数据

    文章目录 一 整体连接图二 DTU配置三 接收机设置 在这里记录一下 xff0c 实验室平台上 xff0c 安装gps接收机和配置的一些步骤 一 整体连接图 二 DTU配置 首先 DTU 需要通过 COM 口和电脑端连接 xff0c 注意这
  • OOQP 安装和使用

    OOQP 安装和使用 1 安装2 使用 1 安装 需要先安装 blas 和 ma27 BLAS xff1a span class token builtin class name cd span my lib span class toke
  • 系统指标

    目录 1 cpu xxx 1 1 cpu空闲率cpu idle cpu idle表示除硬盘IO等待时间以外其它等待时间 xff0c 这个值越大 xff0c 表示cpu越空闲 xff0c 还可以执行更多的任务 xff0c 反之亦然 xff0c

随机推荐

  • stm32 MPU6050 6轴姿态传感器的介绍与DMP的应用

    最近应用到三轴姿态传感器 xff0c 因为之前有MPU6050 xff08 6轴传感器 xff0c 这是6轴的 xff09 xff0c 进行搭配使用 xff0c 通过三轴姿态传感器进行舵机的角度调整 内容来源学习正点原子的教程 xff09
  • 室内外融合北斗+uwb终端数据监听和发送控制方法

    UDP接收GNGGA报文同时转发UDP报文的方法 span class token keyword package span span class token class name Frame span span class token p
  • Ubuntu使用终端命令安装谷歌Chrome浏览器

    使用命令行安装谷歌浏览器稳定版 span class token function sudo span span class token function wget span http www linuxidc com files repo
  • 无人机PX4使用动捕系统mocap的位置实现控制+MAVROS

    动捕系统Optitrack xff0c 有很高的定位精度 xff0c 能够给无人机提供比较精确的位置信息 xff0c 因此如果实验室有条件 xff0c 都可以买一套动捕系统 动捕系统的原理 xff1a 光学式动作捕捉依靠一整套精密而复杂的光
  • Optitrack使用ros完成实时接收刚体的位置与四元数信息

    1 Opitrack系统标定 工作环境 xff1a 运行Motive的Windows主机 和一台安装有ROS的ubuntu电脑 标定步骤 1 准备 优化捕获设置 xff1b 2 在相机预览窗口 xff08 Camera Preview xf
  • RoboMaster机甲大师比赛入门?我们从STM32开始!

    同步博客地址 xff1a 从STM32开始的RoboMaster生活 xff1a 入门篇 项目 amp 教程仓库 xff1a STM32 RoboMaster 1 0 STM32是什么 1 1 定义 ST 43 M 43 32 61 STM
  • C++头文件定义类的方法

    新手在写C 43 43 程序定义类的时候 xff0c 可能会犯一个错误 xff0c 就是在main函数文件里定义很多类 xff0c 一个文件中包含很多函数 xff0c 这样程序看起来很冗杂 今天总结一下如何在C 43 43 中使用头文件来定
  • 相机光学(十五)——如何提高相机标定的精度

    为了提高单目相机标定的精度 xff0c 认真看了张正友标定法的原文 xff0c 并且学习过网上一些牛人的方法 xff0c 但是大部分时候说的很笼统 xff0c 自己把这些经验总结起来并都测试了一下 xff0c 感觉靠谱的结论列出如下 xff
  • TCP/UDP、封装与解封装

    目录 传输类型 网络里面三层架构 TCP IP模型 OSI模型 TCP IP模型 掌握 TCP IP模型当中重点 数据传递过程中的封装和解封装 封装 解封装 TCP UDP ICMP ICMP错误报告 ICMP重定向 典型应用 PING应用
  • 解决 ERROR: cannot launch node of type [xxx]: can‘t locate node [xxx] in package [xxx]

    背景 xff1a 从github下载的ros代码 xff0c 修改添加节点后 xff0c catkin make 编译通过 xff0c 但在运行launch文件时候报错 原因 xff1a 1 从github上下载的很多文件 xff0c 下载
  • stm32控制步进电机

    本文使用DM542c驱动器驱动 使用前注意根据实际情况调节拨码开关 本文不会提到GPIO使能 xff0c 请自行使能 一 PWM操作驱动器使步进电机一直转 使能定时器时钟 xff0c 并配置基本参数 下图以TIM3为例 配置输出比较PWM1
  • 树莓派GPIO

    命令行执行下行 xff0c 即可得树莓派管脚编码表 gpio readall 也可看下图 xff1a BOARD 编号参考 Raspberry Pi 主板上 P1 接线柱的针脚编号 使用该方式的优点是无需考虑主板的修订版本 xff0c 无需
  • python opencv滤波

    1 均值滤波 算法简单 xff0c 计算速度快 xff0c 在去噪的同时去除了很多细节部分 xff0c 将图像变得模糊 cv2 blur 2 高斯滤波 去除高斯噪声 cv2 GaussianBlur 3 中值滤波 去除椒盐噪声 cv2 me
  • opencv imwrite()保存指定路径

    cpp为例 include lt opencv2 opencv hpp gt include lt string gt include lt iostream gt using namespace cv using namespace st
  • python pip安装的包的路径

    以ubuntu为例 从一个店家那里拿到的一个ubuntu环境中 xff0c 同时安装了python3 6和python2 7 xff0c 又安装了ros xff0c 最后pip安装包的位置很混乱 xff0c 安装的包不知道安装在了哪里 使用
  • solidworks实体显示线框

    sw有段时间没使用 xff0c 今天打开突然发现打开的sw窗口数超过1 xff0c 那么从第二个窗口以后的模型都显示成以下样子 xff08 无论是之前的文件还是新建的都不行 xff09 如上是一个圆盘 xff0c 明明是实体 xff0c 却
  • vscode使用虚拟环境

    我的conda没有添加入path xff0c 每次打开总是报错 一 选择对应虚拟环境的解释器 1 点击vscode的右下角这里 2 点击后可能会在vscode上方出现下图样子 xff0c 如果出现下图 xff0c 则点击第二项Select
  • TabError: inconsistent use of tabs and spaces in indentation

    错误原因是tab制表符和空格混用了 从其他地方复制源码容易出现此错误 解决办法 xff1a 把处于同级缩进的所有缩进修改统一 比较流行的几个编辑器都能标识tab和空格 xff0c 比如我用的vscode 用鼠标框选 不知道是tab还是空格的
  • 关于深度学习的问题笔记

    感谢沐神教我深度学习 x1f64f 损失为什么要平均 xff1f 平均即除以batch size xff0c 若不除 xff0c 则批越大梯度越大 xff0c 梯度下降的步长就越大 除以batch size可使梯度与批大小无关 也可以不在损
  • 简单(炫酷)的单链表快速排序写法

    昨天在复习快排的时候 在B站看到一个小哥哥说某大厂的面试让写一个单链表的快速排序 我们见的最多的快排写法都是从两端向中间扫描 这种写法在单链表上不能实现 哥们分析道 快排的核心思想是每次扫描后 所有pivot左侧的元素都比pivot小 右侧