从二叉树的角度看快速排序

news2025/9/19 15:01:32

快速排序本质上可以看作二叉树的前序遍历

快速排序是先将一个元素排好序,然后再将剩下的元素排好序

核心思路依然是分治

快排整体思路

准确的可以说是治分 =>  先治 得到分界点后 再分

治:双指针技巧(左右指针或者快慢指针,我会将两种都展示)将一个元素放到正确的位置

分:根据上面“治”返回的分界点进行拆分 => 得到新个较小规模问题后再治理

 你甚至可以这样理解:快速排序的过程是一个构造二叉搜索树的过程

但谈到二叉搜索树的构造,那就不得不说二叉搜索树不平衡的极端情况,极端情况下二叉搜索树会退化成一个链表,导致操作效率大幅降低

=> 为了避免这种问题 => 我们将引入洗牌算法 (别害怕就是随机数问题,没难度)

void shuffle(vector<int>& nums){
        int n = nums.size();
        srand((unsigned)time(NULL));
        for(int i=0;i<n;i++){
            int r = i + rand()%(n-i);
            swap(nums[i],nums[r]);
        }
    }

partition写法

其中最难的就是partition的写法 => 也是本文的核心内容

=> 我将从多角度分析双指针技巧

 好啦,下面就来介绍partition写法

首先你要确定以谁为基准(也就是pivot或者说分界点),这很重要 => 直接影响到下一步选择

原则:升序情况下,以最右边为基准时,最后拿较大的和基准元素交换

注意我下面所说的较大较小都是和基本比较而言的哦!

左右指针

升序:左指针找较大的数字,右指针找较小的数字,然后交换彼此

降序:左指针找较小的数字,右指针找较大的数字,然后交换彼此

升序降序分别两种写法

 辅助理解

这里是升序情况,right拿到的是较小的 => 适合基准在最左边

int Partition(vector<int>& nums, int lo, int hi) {
    int pivot = nums[lo];
    int left = lo + 1, right = hi;
    while(left <= right) {
        while(left < hi && nums[left] <= pivot) left++;
        while(right > lo && nums[right] > pivot) right--;
        if(left >= right) break;
        swap(nums[left], nums[right]);
    }
    swap(nums[lo], nums[right]);
    return right;
}

还有一种升序情况演示:

int Partition(vector<int>& nums, int lo, int hi) {
    int pivot = nums[hi];
    int left = lo, right = hi - 1;
    while(left <= right) {
        while(left < hi && nums[left] <= pivot) left++;
        while(right > lo && nums[right] > pivot) right--;
        if(left >= right) break;
        swap(nums[left], nums[right]);
    }
    swap(nums[hi], nums[left]);
    return left;
}

这里是降序情况,left拿到的是较大的 => 适合基准在最左边 (注意降序要倒一下)

int Partition(vector<int>& nums, int lo, int hi) {
    int pivot = nums[hi];
    int left = lo, right = hi - 1;
    while(left < right) {
        while(left < hi && nums[left] > pivot) left++;
        while(right > lo && nums[right] <= pivot) right--;
        if(left >= right) break;
        swap(nums[left], nums[right]);
    }
    swap(nums[hi], nums[left]);
    return left;
}

快慢指针

(这里只考虑升序情况)

不同于上面左右指针两个都可以和基准做交换,

这里fast是探路指针,只有slow可以和基准做交换

但是方向不同,在升序情况下,亦有两种写法

辅助理解 

探路fast指针遇到较大的才交换时:
        => 将所有较大的数值放到走过的背后
        => slow占的位置是较小的数字  => 适合基准在最左边

int partition(vector<int>& nums,int lo,int hi){
    int pivot = nums[lo];
    int slow = hi, fast =hi;
    while(fast > lo){
//        探路fast指针遇到较大的才交换
//        => 将所有大的数值放到走过的背后
//        => slow占的位置是较小的数字
        if(nums[fast] > pivot){
            swap(nums[slow],nums[fast]);
            slow--;
        }
        fast--;
    }
    swap(nums[slow],nums[lo]);
    return slow;
}

探路fast指针遇到较小的才交换时:
        => 将所有较小的数值放到走过的背后
        => slow占的位置是较大的数字 => 适合基准在最右边

int partition(vector<int>& nums, int lo, int hi) {
        int pivot = nums[hi];
        int fast = lo, slow = lo;
        while(fast < hi) {
            if(nums[fast] < pivot) swap(nums[slow++], nums[fast]);
            fast++;
        }
        swap(nums[hi], nums[slow]);
        return slow;
    }

最后给出完整代码(以类形式给出)

class Quick {
public:
    void sort(vector<int>& nums) {
//        为了避免极端情况,先打乱
        shuffle(nums);
        sort(nums, 0, nums.size() - 1);
    }

private:
    void sort(vector<int>& nums, int lo, int hi) {
        if(lo >= hi) return;
        int p = partition(nums, lo, hi);
        sort(nums, lo, p - 1);
        sort(nums, p + 1, hi);
    }

    int partition(vector<int>& nums, int lo, int hi) {
        int pivot = nums[hi];
        int fast = lo, slow = lo;
        while(fast < hi) {
            if(nums[fast] < pivot) swap(nums[slow++], nums[fast]);
            fast++;
        }
        swap(nums[hi], nums[slow]);
        return slow;
    }

    void shuffle(vector<int>& nums) {
        int n = nums.size();
        srand((unsigned )time(NULL));
        for(int i = 0; i < n; i++) {
            int r = i + rand() % (n - i);
            swap(nums[i], nums[r]);
        }
    }
};

快排变体 => 快速选择算法

215. 数组中的第K个最大元素 - 力扣(LeetCode)


 

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        shuffle(nums);
        int n = nums.size();
        // 将第k大数字 转化为升序下的第k个数字
        k = n - k;
        int left = 0, right = n - 1;
        while(left <= right) {
            int p = partition(nums, left, right);
            // p排位计较小 => 到分界点右边去找找第k个
            if(k > p) left = p + 1;
            // p排位计较大 => 到分界点左边去找找第k个
            else if(k < p) right = p - 1;
            else return nums[p];
        }
        return -1;
    }
private:
    void shuffle(vector<int>& nums) {
        int n = nums.size();
        srand((unsigned)time(NULL));
        for(int i = 0; i < n; i++) {
            int r = i + rand() % (n - i);
            swap(nums[i], nums[r]);
        }
    }
    int partition(vector<int>& nums, int lo, int hi) {
        int pivot = nums[hi];
        int fast = lo, slow = lo;
        while(fast < hi) {
            if(nums[fast] < pivot) swap(nums[slow++], nums[fast]);
            fast++;
        }
        swap(nums[hi], nums[slow]);
        return slow;
    }
};

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

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

相关文章

【Docker】11、IDEA集成Docker插件实现一键部署SpringBoot项目

日常开发项目的过程中&#xff0c;我们每次需要部署线上的时候&#xff0c;都需要安装一大堆的运行环境&#xff0c;例如&#xff1a;JDK、MySQL、Redis 等&#xff0c;非常花费时间、我们可以使用 Docker 的容器技术&#xff0c;方便快捷地搭建项目启动所需要的运行环境&#…

【微服务笔记15】微服务组件之Config配置中心实现用户认证、配置属性加解密

这篇文章&#xff0c;主要介绍微服务组件之Config配置中心实现用户认证、配置属性加解密。 目录 一、用户认证 1.1、引入security依赖 1.2、服务端ConfigServer添加访问配置 1.3、客户端ConfigClient添加访问配置 二、配置属性加解密 2.1、对称加密 &#xff08;1&#…

逍遥自在学C语言 | 位运算符^的高级用法

前言 在上一篇文章中&#xff0c;我们介绍了|运算符的高级用法&#xff0c;本篇文章&#xff0c;我们将介绍^ 运算符的一些高级用法。 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— …

windows系统管理_文件系统授权规则

NTFS 权限规则 NFTS 权限有一些隐含规则&#xff0c;用户最终有效权限受这些规则的影响&#xff0c;了解并运用这些规则&#xff0c;才能 灵活的分配权限&#xff0c;符合实际需求。 1 权限的累加 如果在某个文件或文件夹的访问控制列表中为某个用户分配了操作权限&#xff…

猿辅导学员入选国家队,竞赛老师成为“最强辅助”

3月31日&#xff0c;国际数学奥林匹克竞赛&#xff08;IMO&#xff09;国家队名单正式出炉&#xff0c;猿辅导学员王淳稷、孙启傲分别以第一名和第二名的成绩位列其中&#xff0c;今年7月&#xff0c;他们将出征日本&#xff0c;代表中国参赛&#xff0c;为国争光。 自2020年以…

理解 与 计算 物联网产品的电池使用寿命

本文带你理解电池的容量以及教会你如何计算使用电池的产品的工作时长前言 在物联网领域&#xff0c;在保证产品性能的前提下&#xff0c;产品的功耗是做得越来越低&#xff0c;针对物联网领域的低功耗无线芯片的功耗也是越来越低。 作为研发人员&#xff0c;除了能够设计出满…

巧用千寻位置GNSS软件|点测量采集技巧

点测量是测量中重要的节点&#xff0c;在测量工作的信息处理分析中发挥着重要作用。本期将给各位带来使用千寻位置GNSS软件采集地形点、控制点、快速点、连续点、房角点和倾斜点的操作技巧。地形点地形点的设置如图 5.1-9所 示&#xff0c;每次采集一个点&#xff0c;该点需要满…

Docker通过Nginx容器代理部署Vue项目

一、打包构建dist 在vue.config.js 添加入口等配置&#xff1a; pages: {index: {// 入口entry: src/main.js}}, lintOnSave: false, publicPath: ./ 在package.json文件中编写build构建&#xff1a; 然后运行: npm run build 在项目根目录下就有构建好的dist包&#xff0…

物流路由线路配载前端算法逻辑实现方案

作者&#xff1a;京东物流 柳宏 1.前置知识 1.1 基本概念 1.1.1 配载 配载代表着某条线路是否具有发往某个方向&#xff08;区域、省市县、分拣等&#xff09;的能力&#xff0c;也可以说是网点&#xff08;分拣中心&#xff09;是否具有承载配载所指方向货物的能力。一般网…

Go 构建基础的事件调度器

&#x1f447;我在这儿 当我们需要在一段时间后的特定时间或间隔运行任务时&#xff0c;我们需要使用任务调度系统来运行任务&#xff1a;例如发送电子邮件、推送通知、午夜关闭账户、清空表格等。在本文中&#xff0c;我们将构建一个基本的事件调度程序&#xff0c;使用数据库…

基于springboot和ajax的简单项目 02.一直会出现的页面的上一页,下一页,分页与总页数 (下)

在各种功能中会一直出现页面分页的问题。 对此&#xff0c;可以使用pojo对象&#xff0c;来一直管理页面分页的功能。 01.创建相关的pojo对象。 由于属性是来辅助sql语句的&#xff0c;这个pojo对象。 Setter Getter ToString NoArgsConstructorpublic class PageObject<T&…

MySQL数据库实现主从同步

安装MySQL数据库8.0.32 前言 今天来学习数据库主从同步的原理及过程&#xff0c;数据库主要是用来存储WEB数据&#xff0c;在企业当中是极为重要的&#xff0c;下面一起来看下。 1.1 数据库做主从的目的 MySQL主从复制在中小企业&#xff0c;大型企业中广泛使用&#xff0c…

【ROS2指南-2】入门 turtlesim 和 rqt

目标&#xff1a;安装并使用 turtlesim 包和 rqt 工具为即将到来的教程做准备。 教程级别&#xff1a;初学者 时间&#xff1a; 15分钟 内容 背景 先决条件 任务 1 安装turtlesim 2 启动turtlesim 3 使用turtlesim 4 安装rqt 5 使用 rqt 6 重新映射 7 关闭turtlesim …

算法 || DFS(深度优先搜索) BFS(广度优先搜索)

&#xff11;、基本概念 dfs全称为Depth First Search,即深度优先搜索。它的思想是沿着每一条可能的路径一个节点一个节点地往下搜索,搜到了路径的到终点再回溯,一直到所有路径搜索完为止。 bfsbfs全称为Breath First Search,即广度(宽度)优先搜索。它的思想是将每一层的结搜…

【SQL】数据库的创建,表的创建、更新、删除

本文内容参考书籍《SQL基础教程》&#xff0c;初学者&#xff0c;请多指教。 一、数据库的创建 1、创建数据库语句 CREATE DATABASE <数据库名称>; 2、示例 CREATE DATABAST shop&#xff1b; 二、表的创建 创建表之前&#xff0c;必须先创建用于存储表的数据库。 1、…

如何找出消耗CPU最多的线程?

如何找出消耗CPU最多的线程&#xff1f; 1.使用 top -c 找出所有当前进程的运行列表 top -c 2.按P(Shiftp)对所有进程按CPU使用率进行排序&#xff0c;找出消耗最高的线程PID ​​​ 显示Java进程 PID 为 136 的java进程消耗最 3.使用 top -Hp PID&#xff0c;查出里面消…

JavaEE简单示例——基于XML配置文件的SSM整合

SSM整合 在本节中&#xff0c;我们会将之前我们学习过的三个框架结合起来&#xff0c;让他们可以融合起来&#xff0c;搭建成一个完整的贯穿整个三层架构的整体框架。 三层框架与对应的框架功能 我们首先回顾一下我们编写软件的三层框架以及对应使用的框架都分别是什么&…

代码随想录算法训练营第五十九天| 503. 下一个更大元素 II、42. 接雨水。

503. 下一个更大元素 II 题目链接&#xff1a;力扣 题目要求&#xff1a; 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。数字 x 的 下一个更大的元素 是按数组遍历顺…

( “树” 之 DFS) 110. 平衡二叉树 ——【Leetcode每日一题】

110. 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] …

智慧钢铁厂人员定位系统解决方案,助力钢铁企业提升安全生产管理水平

作为国民经济的基础原材料产业&#xff0c;钢铁工业在经济发展中具有重要地位。中国钢铁工业不仅为我国国民经济的快速发展做出了重大贡献&#xff0c;也为世界经济的繁荣和世界钢铁工业的发展起到了积极的促进作用&#xff0c;但钢铁行业在快速发展的同时也存在一些安全管理问…