【算法】随机快速排序和随机选择算法

文章目录

    • 1、随机快速排序
      • 1.1 什么是随机快排
      • 1.2 随机快排的好处
    • 2、随机选择算法

前言: 快速排序就是每次划分前,通过一种方法将一个基准值的位置确定好,再进入不同的部分重复相同的工作以此确定好每个值的位置以达到有序。如果你之前并不了解快速排序请看快速排序。

1、随机快速排序

1.1 什么是随机快排

随机快速排序顾名思义也是一种快速排序,它和普通的快排不同在有以下的优化。

  1. 通过随机选取一个基准值,保证应对快排在有序情况下的最坏效率问题。
  2. 通过三路划分的方式,假设基准值为x,每一趟划分都会确保区间为<x ==x >x。

随机快排的思路:

  1. 通过数组下标控制范围,在这个范围内通过随机函数选择一个随机数。
  2. 选好随机数后以此数为基准值,通过三路划分的方式划分区间为<x ==x >x。
  3. 通过下标控制边界,通过函数递归重复1和2。


第一步:选随机数。 由于采用的是C++语言,用的是rand函数,下面先看用法再看快排相关的。

#include <ctime>
#include <cstdlib>
srand(time(0)); //确保每次运行rand的随机序列不同
int num = rand(); // 会返回0到32767之间的整数
//如果生成[a,b)区间的话,而[a,b]就在后面加1
int num = a + rand() % (b-a) // a + rand() % (b-a+1)////那么快排中选取一个随机数就是(当然这里也可以用下标)
int x = nums[l + rand() % (r - l + 1)];


第二步:确定好基准值后进行以基准值分割区域

int first = 0, last = 0;
void partition(vector<int>& nums, int l, int r, int x){first = l, last = r;int i = l;while (i <= last){if (nums[i] < x) swap(nums[i++], nums[first++]);else if (nums[i] > x) swap(nums[i], nums[last--]);else i++;}}

选定全局first(下图F)和last(下图L)作为等于x区域的左边界和右边界,用i来遍历数组,x代表所选随机数。其中F左边的区域代表小于x的,L右边的区域代表大于x的。F和i开始一起。

  1. 当nums[i] < x时,交换i和F的数,并且i和F向右走,使得F左边都是小于x的数。
  2. 当nums[i] > x时,交换i和L的数,这时只有L向左走(因为交换后i位置可能还是大于x的数),保证L右边都是大于x的数。
  3. 当nums[i] == x时,i直接向右走,此时F左边依旧是小于x的数,L右边依旧是大于x的数。

第三步:通过下标控制边界,通过函数递归重复1和2

void RandomQuickSort(vector<int>& nums, int l, int r)
{if (l >= r) return;int x = nums[l + rand() % (r - l + 1)];partition(nums, l, r, x);RandomQuickSort(nums, l, first - 1);RandomQuickSort(nums, last + 1, r);
}

下面通过一道题进行随机快排的测试。

排序数组

完整代码

class Solution {
public:int first = 0, last = 0;void partition(vector<int>& nums, int l, int r, int x){first = l, last = r;int i = l;while (i <= last){if (nums[i] < x) swap(nums[i++], nums[first++]);else if (nums[i] > x) swap(nums[i], nums[last--]);else i++;}}void RandomQuickSort(vector<int>& nums, int l, int r){if (l >= r) return;int x = nums[l + rand() % (r - l + 1)];partition(nums, l, r, x);RandomQuickSort(nums, l, first - 1);RandomQuickSort(nums, last + 1, r);}vector<int> sortArray(vector<int>& nums) {RandomQuickSort(nums, 0, nums.size() - 1);return nums;}
};

随机快排的时间复杂度为O(N*LogN),空间复杂度为O(LogN)。

1.2 随机快排的好处

接下来我们看看为什么随机快排需要挑选随机数以及三路划分

我们先来看看什么是快排效率最差的情况

  • 最差情况:当对一个有序数组进行快排,比如[1 2 3 4 5 6 7],如果依据上面partition的做法,普通的按照最后一个数为基准值,那么每次都得拿最后一个数和前面每一个数进行比较(比如第一趟7和前面每一个数都比了一遍),那么时间复杂度就是O(N^2)。如果通过随机选取一个基准值,那么就可以极大概率降低每次都抽到最后一个数的情况。

为什么要用三路划分?

  • 如果给定一组数3 3 3 4 3 3 3,如果按照之前快排的挖坑法,第一次排序后还是3 3 3 4 3 3 3,因为其采用的是划分为>=x 和 <=x的区域,而通过三路划分则可以划分为3 3 3 3 3 3 4。(这两个方法都可以实现有序,只是在一些场景下我们要求严格的<x =x >x进行划分,通过三路划分就更好。比如下面随机选择算法的这道题)

2、随机选择算法

随机选择算法主要用于在无序数组中快速定位第k大的元素。其通过分区策略和随机化选择,最终时间复杂度能达到O(N)。
它的原理如下:

  1. 通过确定随机数后进行三路划分,得到<x ==x >x三个范围,并且这个x所在位置和有序的情况是一样的(因为快排所做的就是让每个基准值放在对应有序的位置)。
  2. 因为第k大的元素,反过来就是len-k小的元素,在有序情况下这个元素就是对应所在下标位置。
  3. 因此,假设对应k位置的值为val,那么val大于x,就说明还需要道x的右边区域查找(也就是再细分区域),如果val小于x,就说明要道x的左边区域继续查找。如果等于x就说明第k大的元素就是x。

数组中的第K个最大元素

完整代码

class Solution {
public:int first = 0, last = 0;int findKthLargest(vector<int>& nums, int k) {//找第k大的,就是找nums.size-k小的return findKthSmall(nums, nums.size() - k);}int findKthSmall(vector<int>& nums, int i){int ans = 0;int l = 0, r = nums.size() - 1;while (l <= r){int x = nums[l + rand() % (r - l + 1)];//将x对应的有序位置进行确定partition(nums, l, r, x);//看nums[i]和x的大小关系,确定nums[i]的位置if (nums[i] > x) l = last + 1;else if (nums[i] < x) r = first - 1;else{ans = nums[i];break;}}return ans;}//三路划分, 一定要注意划分范围是 >x ==x >x 不然不行void partition(vector<int>& nums, int l, int r, int x){first = l, last = r;int i = l;while (i <= last){if (nums[i] < x) swap(nums[i++], nums[first++]);else if (nums[i] > x) swap(nums[i], nums[last--]);else ++i;}}
};

为什么只能用三路划分?partition用挖坑法行吗?
如果是3 3 3 4 3 3 3这样一组数找第一大的,如果采用挖坑法只能划分<=x >=x两个范围,对于随机到3而言,第一趟划分就是不变,就无法通过比较对应i位置的大小确定第一大的数的位置。因此一定得保证划分范围是 >x ==x >x 。

算法中有很多精妙又美丽的思想传统,请务必坚持下去!!

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

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

相关文章

网络技术基础,NAT,桥接,交换机,路由器

什么是NAT Network Address Translation&#xff08;网络地址转换&#xff09;&#xff0c;它负责将目标IP或源IP进行了改变&#xff0c;相当于一个中间代理&#xff0c;我们家庭常用的路由器就是一个NAT设备&#xff0c;NAT是为了解决IPv4的IP地址快要耗尽的问题&#xff0c;…

DVWA靶场保姆级通关教程--03CSRF跨站请求伪造

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 文章目录 前言 一、low级别的源码分析 二、medium级别源码分析 安全性分析 增加了一层 Referer 验证&#xff1a; 关键点是&#xff1a;在真实的网络环境中&a…

【Ansible自动化运维实战:从Playbook到负载均衡指南】

本文是「VagrantVirtualBox虚拟化环境搭建」的续篇&#xff0c;深入探索Ansible在自动化运维中的核心应用&#xff1a; ✅ Ansible核心技能&#xff1a;Playbook编写、角色&#xff08;Roles&#xff09;模块化、标签&#xff08;Tags&#xff09;精准控制 ✅ 实战场景覆盖&a…

基于STM32、HAL库的STC31-C-R3气体传感器驱动程序设计

一、简介: STC31-C-R3是Sensirion公司推出的一款基于CMOSens技术的CO2传感器,具有以下特点: 测量范围:0-100%体积浓度 I2C数字接口 低功耗设计 高精度和长期稳定性 小尺寸封装(5mm x 5mm) 二、硬件接口: STC31-C-R3 STM32L4xx ---------------------------- VDD (P…

Nginx篇之限制公网IP访问特定接口url实操

一、nginx配置限制IP访问 要在 Nginx 配置中添加 IP 限制&#xff0c;阻止来自指定公网 IP 地址段的访问&#xff0c;并且只对特定路径进行限制&#xff0c;可以在 location 配置中使用 deny 和 allow 指令来控制访问。 二、案例 1. 需求 对来自特定公网的地址段&#xff0…

算法研习:无重复字符的最长子串问题剖析

算法研习:无重复字符的最长子串问题剖析 一、引言 在算法的广袤天地中,字符串相关问题一直是备受关注的焦点。“无重复字符的最长子串”这一问题,不仅在面试中频繁出现,更是对算法思维和编程技巧的一次深度考验。它要求我们从给定字符串中找出不含有重复字符的最长子串的长…

Spring Cloud Gateway路由+断言+过滤

目录 介绍核心功能三大核心Route以服务名动态获取URLPredicate常用断言Path Route PredicateAfter Route PredicateBefore Route PredicateBetween Route PredicateCookie Route PredicateHeader Route PredicateHost Route PredicateQuery Route PredicateRemoteAddr Route Pr…

springboot集成langchain4j记忆对话

流式输出 LLM 一次生成一个标记&#xff08;token&#xff09;&#xff0c;因此许多 LLM 提供商提供了一种方式&#xff0c;可以逐个标记地流式传输响应&#xff0c;而不是等待整个文本生成完毕。 这显著改善了用户体验&#xff0c;因为用户不需要等待未知的时间&#xff0c;几…

【SpringCloud GateWay】Connection prematurely closed BEFORE response 报错分析与解决方案

一、背景 今天业务方调用我们的网关服务报错: Connection prematurely closed BEFORE response二、原因分析 三、解决方案 第一步: 增加 SCG 服务的JVM启动参数,调整连接获取策略。 将连接池获取策略由默认的 FIFO&#xff08;先进先出&#xff09;变更为 LIFO&#xff08…

使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第十一讲)

这一期讲解lvgl中下拉框的基础使用&#xff0c;下拉列表允许用户从选项列表中选择一个值&#xff0c;下拉列表的选项表默认是关闭的&#xff0c;其中的选项可以是单个值或预定义文本。 当单击下拉列表后&#xff0c;其将创建一个列表&#xff0c;用户可以从中选择一个选项。 当…

【神经网络与深度学习】VAE 在解码前进行重参数化

在 VAE 中&#xff0c;解码之前进行重参数化主要有以下几个重要原因&#xff1a; 可微分性 在深度学习里&#xff0c;模型是通过反向传播算法来学习的&#xff0c;而这需要计算梯度。若直接从潜在变量的分布 (q_{\theta}(z|x))&#xff08;由编码器输出的均值 (\mu) 和方差 (…

BBDM学习笔记

1. configs 1.1 LBBDM: Latent BBDM [readme]

mysql主从复制搭建,并基于‌Keepalived + VIP实现高可用

以下是基于 ‌Keepalived VIP‌ 实现 MySQL 主从复制高可用的详细步骤&#xff0c;涵盖主从复制搭建与故障自动切换&#xff1a; 一、MySQL 主从复制搭建&#xff08;基础步骤回顾&#xff09; 1. ‌主库&#xff08;Master&#xff09;配置‌ 修改配置文件‌ /etc/my.cnf&…

CD36.【C++ Dev】STL库的string的使用 (下)

目录 1.reserve函数(不是reverse) 代码示例 2.resize 代码示例 3.reserve和resize的区别 4.shrink_to_fit 代码示例 5.与C语言的配合的接口函数: c_str 代码示例 6.rfind 知识回顾:find函数 rfind 代码示例 练习题: 字符串最后一个单词的长度 代码 提交结果 ​…

STM32的网络天气时钟项目

一、项目概述与硬件架构 1.1 核心功能 本智能天气时钟系统集成了实时天气获取、网络时间同步、环境监测和低功耗管理四大核心功能&#xff1a; 网络数据获取&#xff1a; 通过ESP8266 WiFi模块连接心知天气API&#xff08;每小时更新&#xff09;获取北京标准时间服务器的时…

FPGA DDR4多通道管理控制器设计

DDR4控制器一般采用自带的MIG控制器&#xff0c;用户控制主要是基于MIG IP核进行设计 实际工程项目中可能只挂载了一组DDR&#xff0c;但是用户数据可能有很多种&#xff0c;用户通过给每种数据划分特定地址进行存储&#xff0c;如何实现灵活管理成为设计的关键 为了方便后端数…

低代码 x AI,解锁数智化应用的创新引擎

AI 智能体开发指南 随着全球信息化浪潮的持续推进&#xff0c;数字化、智能化转型已成为企业发展的必经之路。在这个变革的时代&#xff0c;企业面临着前所未有的挑战与机遇。一方面&#xff0c;市场环境瞬息万变&#xff0c;企业需要快速响应并调整业务模式&#xff1b;另一方…

【Spring Boot 注解】@Configuration与@AutoConfiguration

文章目录 Configuration与AutoConfiguration一、Configuration二、AutoConfiguration Configuration与AutoConfiguration 一、Configuration 这是最常用的 Spring 注解之一&#xff0c;表示当前类是一个 配置类&#xff0c;可以定义 Bean 方法&#xff0c;等效于传统的 XML 配…

arXiv论文 MALOnt: An Ontology for Malware Threat Intelligence

文章讲恶意软件威胁情报本体。 作者信息 作者是老美的&#xff0c;单位是伦斯勒理工学院&#xff0c;文章是2020年的预印本&#xff0c;不知道后来发表在哪里&#xff08;没搜到&#xff0c;或许作者懒得投稿&#xff0c;也可能是改了标题&#xff09;。 中心思想 介绍开源…

【存储管理—动态不等长存储资源分配算法】

文章目录 一、实验目的二、实验内容与设计思想实验内容设计思路 三、实验代码实现四、总结 一、实验目的 理解动态异长存储分区资源管理&#xff0c;掌握所需数据结构和管理程序&#xff0c;了解各种存储分配算法的优点和缺点。 二、实验内容与设计思想 实验内容 1.分析uni…