当前位置: 首页 > news >正文

算法系列--分治排序|再谈快速排序|快速排序的优化|快速选择算法

前言:本文就前期学习快速排序算法的一些疑惑点进行详细解答,并且给出基础快速排序算法的优化版本

一.再谈快速排序

快速排序算法的核心是分治思想,分治策略分为以下三步:

  1. 分解:将原问题分解为若干相似,规模较小的子问题
  2. 解决:如果子问题规模较小,直接解决;否则递归解决子问题
  3. 合并:原问题的解等于若干子问题解的合并

应用到快速排序算法:

  1. 分解:快速排序算法要实现的是对整个数组进行排序,但是规模较大,要想办法减少规模;他的解决策略是"选择一个基准元素,将数组划分为两部分,左边都是小于基准元素,右边都是大于基准元素",不断的重复上述过程,就能完成对整个数组的排序.对整个数组完成一次这样的操作后,再对左右两个区间分别执行上述过程
  2. 递归地对两个子数组进行快速排序,直到每个子数组的长度为0或1,此时数组已经有序。
  3. 由于在递归过程中子数组已经被分别排序,因此不需要再进行额外的合并步骤。

二.代码实现和细节讲解

快速排序的关键代码在于如何根据基准元素划分数组区间(parttion),分解的方法有很多,这里只提供一种方法挖坑法
代码:

class Solution {public int[] sortArray(int[] nums) {quick(nums, 0, nums.length - 1);return nums;}private void quick(int[] arr, int start, int end) {if(start >= end) return;// 递归结束条件int pivot = parttion(arr, start, end);// 递归解决子问题quick(arr, start, pivot - 1);quick(arr, pivot + 1, end);}// 挖坑法进行分解private int parttion(int[] arr, int left, int right) {int key = arr[left];while(left < right) {while(left < right && arr[right] >= key) right--;arr[left] = arr[right];while(left < right && arr[left] <= key) ++left;arr[right] = arr[left];}arr[left] = key;return left;}}

细节解答:
1.为什么start>=end是递归结束条件?

不断的分解子问题,最终子问题的规模大小是1,即只有一个元素,此时无需继续进行分解,start和end指针同时指向该元素

2.为什么要right先走?而不是left先走?

具体谁先走取决于基准元素的位置,在上述代码中,基准元素(key)是最左边的元素,如果先移动left,left先遇到一个比基准元素大的元素,此时执行arr[right] = arr[left],由于没有保存arr[right],这个元素就会丢失
如果先走right,right先遇到一个比基准元素小的元素,此时执行arr[left]=arr[right],因为此时left并没有移动,还是pivot,但是pivot已经被我们使用key进行保存了

3.为什么是arr[right]>=key?>不行吗

大于等于主要是为了处理重复元素问题
例如有数组[6,6,6,6,6]如果是>,right指针不会发生移动,left指针也不会发生移动,此时陷于死循环

4.为什么叫做挖坑法

当r指针遇到第一个<pivot的元素后停止,执行arr[r] = arr[l],此时l位置就空白出来,形成了一个坑


三.快速排序的优化

主要有两个优化方向:

  1. 基准值pivot的选取,可以证明的是当随机选取基准值时,快速排序的时间复杂度趋近于O(N*logN),即最好的时间复杂度
  2. 重复元素的处理:如果区间内部有大量的重复元素,上述版本的快速排序算法会对相同的元素重复执行多次;为了减少冗余的操作,使用数组分三块的思想解决,同时如果遇到特殊的测试用例(顺序数组或逆序数组)时间复杂度会退化到O(N^2)

先根据一道题目(颜色分类)了解什么是数组分三块
分析

在这里插入图片描述
代码:

class Solution {public void sortColors(int[] nums) {// 分治 --// 1.定义三指针int i = 0;// 遍历整个数组int l = -1, r = nums.length;while(i < r) {if(nums[i] == 0) swap(nums,++l,i++);else if(nums[i] == 1) i++;else swap(nums,--r,i);}return;}private void swap(int[] nums,int x,int y) {int tmp = nums[x]; nums[x] = nums[y]; nums[y] = tmp;}
}
  • 注意l,r的起始位置,第一个元素和最后一个元素在开始的时候属于未处理状态,所以`l,r不能指向这两个元素,必须在区间之外
  • 所谓的数组分三块,就是使用三个指针去分别维护四个区间,其中的一个区间是未处理区间,随着cur指针的不断移动,所有的区间都被处理,最终也就只有三个区间

将上述思路应用于快速排序的parttion中,最终的结果就是划分为三个区间
在这里插入图片描述
代码实现:

class Solution {// 快速排序优化版// 分解--解决--合并public int[] sortArray(int[] nums) {qsort(nums, 0, nums.length - 1);return nums;}private void qsort(int[] nums, int start, int end) {if(start >= end) return;// 递归结束条件// 分解int pivot = nums[start];int l = start - 1, r = end + 1, i = start;while(i < r) {int cur = nums[i];if(cur < pivot) swap(nums, ++l, i++);else if(cur == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r-1]  [r, end]// 递归解决qsort(nums, start, l);qsort(nums, r, end);}private void swap(int[] nums,int i, int j) {int tmp = nums[i];  nums[i] = nums[j]; nums[j] = tmp;}
}

在这里插入图片描述
2.随机选取基准值
采用随机数的方式随机选取基准值

        int pivot = nums[start + new Random().nextInt(end - start + 1)];//               起始位置      随机产生的偏移量

完整的改进代码:

class Solution {// 快速排序优化版// 分解--解决--合并public int[] sortArray(int[] nums) {qsort(nums, 0, nums.length - 1);return nums;}private void qsort(int[] nums, int start, int end) {if(start >= end) return;// 递归结束条件// 分解int pivot = nums[start + new Random().nextInt(end - start + 1)];int l = start - 1, r = end + 1, i = start;while(i < r) {int cur = nums[i];if(cur < pivot) swap(nums, ++l, i++);else if(cur == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r-1]  [r, end]// 递归解决qsort(nums, start, l);qsort(nums, r, end);}private void swap(int[] nums,int i, int j) {int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp;}
}

在这里插入图片描述

  • 效率明显提升

四.快速选择算法

快速选择算法是基于快速排序优化版本的一种时间复杂度可达到O(N)的选择算法,使用场景为第K大/前K大之类的选择问题

01.数组中的第K个最大的元素
链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/
分析

  • 暴力解法就是直接使用sort进行排序,然后返回第K大即可
  • 时间复杂度:O(N*logN)
  • 空间复杂度:O(logN)递归产生的栈调用

接下来采用快速选择算法,实现O(N)的时间复杂度
代码:

class Solution {public int findKthLargest(int[] nums, int k) {return qsort(nums, 0, nums.length - 1, k);}private int qsort(int[] nums, int start, int end, int k) {if(start >= end) return nums[start];int pivot = nums[start + new Random().nextInt(end - start + 1)];// 数组分三块  <pivot  ==pivot  >pivotint l = start - 1, r = end + 1, i = start;while(i < r) {if(nums[i] < pivot) swap(nums, ++l, i++);else if(nums[i] == pivot) ++i;else swap(nums, --r, i);}// [start, l]  [l+1, r - 1]  [r, end]int c = end - r + 1, b = r - 1 - (l + 1) + 1, a = l - start + 1;// 分情况讨论  进行选择if(c >= k) return qsort(nums, r, end, k);else if(b + c >= k) return pivot;else return qsort(nums, start, l, k - b - c);// 找较小区间的第(k-b-c)大}private void swap(int[] arr, int i, int j) {int tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp;}
}
  • 快速选择算法和快速排序的思想很像,不同点在于快速选择算法只对每次parttion结果的一部分区间进行递归,而不是像快速排序一样对整个区间进行递归,所以快速选择算法的时间复杂度降到了O(N)

相关文章:

算法系列--分治排序|再谈快速排序|快速排序的优化|快速选择算法

前言:本文就前期学习快速排序算法的一些疑惑点进行详细解答,并且给出基础快速排序算法的优化版本 一.再谈快速排序 快速排序算法的核心是分治思想,分治策略分为以下三步: 分解:将原问题分解为若干相似,规模较小的子问题解决:如果子问题规模较小,直接解决;否则递归解决子问题合…...

强化学习编程实战-1-一个及其简单的强化学习实例(多臂赌博机)

1.1 多臂赌博机 一台拥有K个臂的机器&#xff0c;玩家每次可以摇动K个臂中的一个&#xff0c;摇动后&#xff0c;会吐出数量不等的金币&#xff0c;吐出金币的数量服从一定的概率分布&#xff0c;而且不同臂的概率分布不同。 多臂赌博机的问题是&#xff1a;假设玩家共有N次摇地…...

Golang语法规范和风格指南(一)——简单指南

1. 前引 一个语言的规范的学习是重要的&#xff0c;直接关系到你的代码是否易于维护和理解&#xff0c;同时学习好对应的语言规范可以在前期学习阶段有效规避该语言语法和未知编程风格的冲突。 这里是 Google 提供的规范&#xff0c;有助于大家在开始学习阶段对 Golang 进行一…...

数据机构记录顺序表-笔记1

一、线性表的基本概念 数据元素&#xff1a;线性表中的基本单位&#xff0c;每个元素都是线性表的一部分。 数据项&#xff1a;数据元素的具体值。 存储位置&#xff1a;线性表中的元素在内存中的具体存储位置。 线性表按存储结构可以分为顺序表和链表两大类&#xff1a; 1.1…...

考研必备~总结严蔚敏教授《数据结构》课程的重要知识点及考点

作者主页&#xff1a;知孤云出岫 目录 1. 基本概念1.1 数据结构的定义1.2 抽象数据类型 (ADT) 2. 线性表2.1 顺序表2.2 链表 3. 栈和队列3.1 栈3.2 队列 4. 树和二叉树4.1 树的基本概念4.2 二叉树 5. 图5.1 图的基本概念5.2 图的遍历 6. 查找和排序6.1 查找6.2 排序 7. 重点考…...

【数据分享】国家级旅游休闲街区数据(Excel/Shp格式/免费获取)

之前我们分享过从我国文化和旅游部官网整理的2018-2023年我国50个重点旅游城市星级饭店季度经营状况数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01;文化和旅游部官网上也分享有很多与旅游相关的常用数据&#xff0c;我们基于官网发布的名单文件整理得到全国…...

Linux开发:进程间通过Unix Domain Socket传递数据

进程间传递数据的方式有很多种,Linux还提供一种特殊的Socket用于在多进程间传递数据,就是Unix Domain Socket(UDS)。 虽然通过普通的Socket也能做到在多进程间传递数据,不过这样需要通过协议栈层的打包与拆包,未免有些浪费效率,通过UDS,数据仅仅通过一个特殊的sock文件…...

Redis基础教程(九):redis有序集合

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…...

Servlet与Servlet容器

什么是Servlet? Servlet是Java EE&#xff08;现称Jakarta EE&#xff09;中的一个组件&#xff0c;通常用于创建动态Web内容。Servlet是运行在Web服务器上的Java程序&#xff0c;它处理客户端的请求并生成响应。Servlet的核心功能是处理HTTP请求和响应。下面是一个servlet例…...

腾讯centos mysql安装

腾讯centos mysql安装 腾讯云提供了一系列的云计算服务&#xff0c;包括操作系统、数据库、服务器等。在腾讯云上安装CentOS操作系统和MySQL数据库可以按照以下步骤进行&#xff1a; 登录腾讯云控制台&#xff08;登录 - 腾讯云&#xff09;。在控制台页面上方的搜索框中输入…...

c_各个unsigned int 和 int的取值范围

bool, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t 取值范围分别是什么&#xff1f; 定义形式&#xff1a; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long uint64_…...

C#/WPF 自制截图工具

在日常使用电脑办公时&#xff0c;我们经常遇到需要截图然后保存图片&#xff0c;我们往往需要借助安装截图工具才能实现&#xff0c;现在我们通过C#自制截图工具&#xff0c;也能够轻松进行截图。 我们可以通过C#调用WindousAPI来实现截图&#xff0c;实例代码如下&#xff1a…...

以腾讯为例,手把手教你搭建产品帮助中心

一个精心设计的产品帮助中心对于提高用户满意度和体验至关重要。腾讯&#xff0c;作为全球领先的互联网企业&#xff0c;通过其多样化的产品线&#xff08;包括微信、QQ、腾讯游戏、腾讯视频等&#xff09;吸引了亿万用户。下面将以腾讯为例&#xff0c;向您展示如何搭建一个高…...

计算机网络概述--自我学习用

计算网络体系概述 相关问题 计算机网络为什么要分层&#xff1f;计算机网络是怎么分层的&#xff1f;三种计算机网络模型的关系是什么&#xff1f;每一层分别包含哪些协议&#xff1f;计算机网络中&#xff0c;数据如何在各层中传播&#xff1f;数据在网络各层中的存在形式是…...

超级好用的java http请求工具

kong-http 基于okhttp封装的轻量级http客户端 使用方式 Maven <dependency><groupId>io.github.kongweiguang</groupId><artifactId>kong-http</artifactId><version>0.1</version> </dependency>Gradle implementation …...

在原有的iconfont.css文件中加入新的字体图标

前言&#xff1a;在阿里图标库中&#xff0c;如果你没有这个字体图标的线上项目&#xff0c;那么你怎么在本地项目中的原始图标文件中添加新的图标呢&#xff1f; 背景&#xff1a;现有一个vue项目&#xff0c;下面是这个前端项目的字体图标文件。现在需要新开发功能页&#x…...

使用 ESP32-WROOM + DHT11 做个无屏温湿度计

最近梅雨天&#xff0c;有个房间湿度很大&#xff0c;而我需要远程查看温湿度&#xff0c;所以无所谓有没有显示屏&#xff0c;某宝上的温湿度计都是带屏的&#xff0c;如果连WIFI查看温湿度操作也比较麻烦&#xff0c;还需要换电池&#xff0c;实在不能满足我的需求&#xff0…...

如何使用 SwiftUI 构建 visionOS 应用

文章目录 前言WindowsVolumes沉浸式空间结论 前言 Apple Vision Pro 即将推出&#xff0c;现在是看看 SwiftUI API 的完美时机&#xff0c;这使我们能够将我们的应用程序适应 visionOS 提供的沉浸式世界。苹果表示&#xff0c;构建应用程序的最佳方式是使用 Swift 和 SwiftUI。…...

InspireFace-商用级的跨平台开源人脸分析SDK

InspireFace-商用级的跨平台开源人脸分析SDK InspireFaceSDK是由insightface开发的⼀款⼈脸识别软件开发⼯具包&#xff08;SDK&#xff09;。它提供了⼀系列功能&#xff0c;可以满⾜各种应⽤场景下的⼈脸识别需求&#xff0c;包括但不限于闸机、⼈脸⻔禁、⼈脸验证等。 该S…...

华为HCIP Datacom H12-821 卷24

1.单选题 企业大楼有大量员工通常都在上班时在大厅开始接入到公司的WLAN网络,随着每位员工走到各自的工位过程中,每个人的移动端叶通过漫游的方式漫游到各自的网络覆盖区域。为了尽量保证每个终端的IP地址是固定的,建议的做法是? A、配置VLAN Pool并配置顺序算法 B、…...

告别单打独斗!Apipost 8协作版数据迁移保姆级教程(含团队项目处理)

Apipost 8协作版数据迁移实战&#xff1a;从个人到团队的无缝衔接 第一次打开Apipost 8协作版时&#xff0c;我盯着那个"迁入项目"按钮犹豫了整整十分钟——作为独立开发者&#xff0c;我的旧版本里积累了237个接口文档和56个测试集合&#xff0c;它们就像我精心搭建…...

基于MATLAB的平移线扫激光三维重建完整方案与代码实现

现整理了一套完整的&#xff0c;平移线扫重建 matlab代码和方案&#xff0c;包含相机标定、光平面标定与方案、移动装置标定与方案、激光线条中心线自适应提取、畸变矫正、三维重建、点云滤波等部分&#xff0c;代码按模块编写&#xff0c;注释完整&#xff0c;附带一份完整苹果…...

3步轻松上手BepInEx:Unity插件框架新手必备指南

3步轻松上手BepInEx&#xff1a;Unity插件框架新手必备指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx BepInEx是一款专为Unity游戏设计的插件框架&#xff0c;能帮助开发者轻…...

告别PCtoLCD2002!这款单片机调试助手如何用3步搞定OLED汉字显示?

3步解锁OLED汉字显示&#xff1a;新一代嵌入式开发神器实战指南 在嵌入式开发领域&#xff0c;OLED屏幕的汉字显示一直是让开发者头疼的难题。传统方案如PCtoLCD2002等取模软件不仅操作繁琐&#xff0c;生成的代码还需要大量手工调整。如今&#xff0c;一款名为单片机多功能调试…...

为什么你的Tinymce总是显示秘钥提示?深入解析富文本编辑器的授权机制

解密Tinymce授权机制&#xff1a;从技术原理到合规实践 每次启动项目时&#xff0c;那个突兀的"未授权"提示框是否让你感到困扰&#xff1f;作为前端开发领域的标配工具&#xff0c;Tinymce的授权机制远比表面看到的复杂。让我们拨开迷雾&#xff0c;从技术实现到商业…...

Leetcode 数据结构刷题 ->链表1

[27. 移除元素]移除等于所给值的元素&#xff0c;我们可以直接使用双指针&#xff0c;对着来的。关键就是把不等于x的值&#xff08;我改一下&#xff0c;没用val&#xff09;&#xff0c;放到后面去&#xff0c;这样前面就全部都是不等于x值&#xff0c;再计数即可。看代码就对…...

手把手教你用Google Cloud VPC流量监控:快速定位高费用虚拟机

谷歌云VPC流量监控实战&#xff1a;精准定位高成本虚拟机的5种方法 当凌晨三点的告警邮件突然弹出"本月云服务账单已超预算30%"时&#xff0c;作为运维负责人的你首先会检查哪个环节&#xff1f;根据2023年FinOps基金会调查报告&#xff0c;意外流量费用已成为云成本…...

OpenClaw自动化周报生成:Qwen3-32B私有镜像精准提取Git提交记录

OpenClaw自动化周报生成&#xff1a;Qwen3-32B私有镜像精准提取Git提交记录 1. 为什么需要自动化周报生成 每周五下午&#xff0c;我都会面临同样的困扰&#xff1a;需要从零散的Git提交记录中手动整理本周工作内容&#xff0c;再拼凑成一份结构化的周报。这个过程不仅耗时&a…...

OpCore-Simplify:让黑苹果配置从复杂到简单的智能化革命

OpCore-Simplify&#xff1a;让黑苹果配置从复杂到简单的智能化革命 【免费下载链接】OpCore-Simplify A tool designed to simplify the creation of OpenCore EFI 项目地址: https://gitcode.com/GitHub_Trending/op/OpCore-Simplify 你是否曾为黑苹果&#xff08;Hac…...

数字图书馆下载工具:高效获取策略与跨平台使用方案

数字图书馆下载工具&#xff1a;高效获取策略与跨平台使用方案 【免费下载链接】internet_archive_downloader A chrome/firefox extension that download books from Internet Archive(archive.org) and HathiTrust Digital Library (hathitrust.org) 项目地址: https://git…...