【算法系列篇】分冶-快排

在这里插入图片描述

文章目录

  • 前言
  • 什么是分冶
  • 1.颜色分类
    • 1.1 题目要求
    • 1.2 做题思路
    • 1.3 Java代码实现
  • 2. 排序数组
    • 2.1 题目要求
    • 2.2 做题思路
    • 2.3 Java代码实现
  • 3.数组中的第k个最大元素
    • 3.1 题目要求
    • 3.2 做题思路
    • 3.3 Java代码实现
  • 4. 最小的k个数
    • 4.1 题目要求
    • 4.2 做题思路
    • 4.3 Java代码实现
  • 总结

前言

我相信看到这里很多人都学过八大排序了吧,其中快速排序是一种非常高效的排序方式,那么今天我们将会使用快速排序的算法来解决实际生活中的某些问题。

什么是分冶

分治算法是一种算法设计策略,它将大问题分解成更小的子问题,并通过解决子问题来解决原始问题。分治算法的基本思想是将问题分解成若干个规模较小但结构与原问题相似的子问题,然后递归地解决这些子问题,最后再将子问题的解合并得到原问题的解。

一般而言,分治算法可以分为三个步骤:

  1. 分解(Divide):将原问题划分成若干个规模较小且相互独立的子问题,通常通过递归方式实现。

  2. 解决(Conquer):递归地解决子问题。如果子问题的规模足够小,无需继续分解,直接求解并返回结果。

  3. 合并(Merge):将子问题的解合并成原问题的解。这一步骤通常涉及对子问题解的操作,以得到原问题的解。

分治算法的典型应用包括排序算法(如快速排序和归并排序)、查找算法(如二分查找)、图算法(如最大子数组和、最短路径问题)等。

分治算法的优点在于它能够高效地解决某些复杂问题,尤其适用于可以被划分为多个子问题的情况。通过将问题分解为更小的子问题,分治算法可以减少问题的规模,简化问题的解决过程。

然而,需要注意的是,并非所有问题都适合采用分治算法。在使用分治算法时,需要保证子问题相对独立且可以有效地解决。此外,分治算法在涉及大量递归调用时可能会带来额外的开销,因此在设计算法时需要注意递归深度与性能之间的平衡。

我们今天使用的快速排序的算法则是很好的利用了分冶将大事化小的思想来解决问题的,将整个数组分为若干小区间来进行排序,最终得到我们想要的结果。

1.颜色分类

https://leetcode.cn/problems/sort-colors/

1.1 题目要求

给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

必须在不使用库内置的 sort 函数的情况下解决这个问题。

示例 1:

输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]

示例 2:

输入:nums = [2,0,1]
输出:[0,1,2]

提示:

  • n == nums.length
  • 1 <= n <= 300
  • nums[i] 为 0、1 或 2

进阶:

你能想出一个仅使用常数空间的一趟扫描算法吗?

class Solution {public void sortColors(int[] nums) {}
}

1.2 做题思路

前面学习的快速排序,每一趟排序过程会以一个数为基准,使最终结果这个基准值的左边小于等于这个基准值,右边部分都是大于这个基准值,所以这个题目我们同样可以使用这种快排的思想,以1为基准,然后用 i 来遍历数组,left 指针以及 left 指针左边都是0,right 指针以及 right 指针右边部分都是2。left 一开始的位置指向 -1,right 指针指向 n(数组大小),当 i 所指向的数据小于 1 的时候,就先将 left++ ,然后将left 所指的内容与 i 所指的内容交换位置,交换结束之后,i++;如果 i 所指向的内容等于 1 的之后,直接i++;如果 i 指向的内容大于 1 ,则先需要将 right–,然后交换right 与 i 所指向的内容,但是这里交换完成之后,i 不能++,因为与 right 指向的内容交换位置之后,i 所指向的内容是 i 没有遍历过的,如果 i++,那么这个数字将会被跳过。
在这里插入图片描述

1.3 Java代码实现

class Solution {private void swap(int[] nums, int i, int j) {int t = nums[i];nums[i] = nums[j];nums[j] = t;}public void sortColors(int[] nums) {int n = nums.length;int left = -1,right = n,i = 0;while(i < right) {if(nums[i] < 1) swap(nums,++left,i++);else if(nums[i] == 1) i++;else swap(nums,--right,i);}}
}

在这里插入图片描述

2. 排序数组

https://leetcode.cn/problems/sort-an-array/

2.1 题目要求

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

提示:

  • 1 <= nums.length <= 5 * 104
  • -5 * 104 <= nums[i] <= 5 * 104
class Solution {public int[] sortArray(int[] nums) {}
}

2.2 做题思路

这道题就很简单明了,直接将数组进行升序排序,我们可以使用分冶的思想,讲整个数组分为 n 个部分,然后在这 n 个小部分中使用快排的思想进行排序,需要注意的是,如果数组趋于有序的话,快速排序的时间复杂度会下降到 O(N^2) ,所以我们可以对快速排序进行优化,优化的方式有很多:三数取中等等,这里我们使用的方式是随机取基准值的方法吗,这样能使快排的时间复杂度基本趋于 O(N*logN)。

2.3 Java代码实现

class Solution {public int[] sortArray(int[] nums) {qsort(nums,0,nums.length-1);return nums;}private void qsort(int[] nums, int l, int r) {if(l >= r) return; //递归结束的条件int left = l-1,right = r + 1, i = l;//在[l,r]区间内,随机取一个数作为基准值int key = nums[new Random().nextInt(r - l + 1) + l];while(i < right) {if(nums[i] < key) swap(nums,++left,i++);else if(nums[i] == key) i++;else swap(nums,--right,i);}//当将基准值排序到最终位置之后,还需要将基准位置左右两边部分继续排序qsort(nums,l,left);qsort(nums,right,r);}private void swap(int[] nums, int i, int j) {int t = nums[i];nums[i] = nums[j];nums[j] = t;}
}

在这里插入图片描述

3.数组中的第k个最大元素

https://leetcode.cn/problems/kth-largest-element-in-an-array/

3.1 题目要求

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

输入: [3,2,1,5,6,4], k = 2
输出: 5

示例 2:

输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104
class Solution {public int findKthLargest(int[] nums, int k) {}
}

3.2 做题思路

要想找到数组中的第k个最大元素,我们能想到的还是将数组进行排序,然后从大到小找到第k个元素。这道题目可以使用堆排序,创建出大小为 k 的小根堆。但是我们不使用堆排序的方法,而是使用分冶-快排的方法来解决。如何使用快排的方式来解决呢?同样是先找一个元素作为基准值,进行快排,将数组分为 a——小于基准值的部分、b——等于基准值的部分和c——大于基准值的部分,因为要找到第 k 个最大的元素,所以首先我们需要在大于基准的部分中找这个元素是否存在,如果 c 部分的长度大于等于 k ,则说明这个部分中存在第 k 大的元素,然后我们在这个部分中继续寻找;如果 c 的长度小于 k ,并且 b + c 的长度大于等于 k,那么我们可以直接返回 b 部分的元素,因为 c 部分的长度小于 k ,所以这个第 k 大的元素存在于 b 部分,而 b 部分都是等于基准值的部分,可以直接返回;如果前面两种情况都不存在,那么这个第 k 大的元素就在 a 部分,我们需要在 a 部分中找到第 k - b - c 大的元素,这个操作跟前面的递归操作类似。

在这里插入图片描述

3.3 Java代码实现

class Solution {public int findKthLargest(int[] nums, int k) {return qsort(nums,0,nums.length-1,k);}private int qsort(int[] nums, int l, int r, int k) {int key = nums[new Random().nextInt(r - l + 1) + l];int left = l-1, right = r + 1, i = l;while(i < right) {if(nums[i] < key) swap(nums,++left,i++);else if(nums[i] == key) i++;else swap(nums,--right,i);}//c表示大于key的部分,b表示等于key的部分,剩下的部分就是小于key的部分int c = r - right + 1;int b = right - left - 1;if(c >= k) return qsort(nums,right,r,k);else if(b + c >= k) return key;else return qsort(nums,l,left,k - b - c);}private void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}
}

在这里插入图片描述

4. 最小的k个数

https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof/

4.1 题目要求

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:

  • 0 <= k <= arr.length <= 10000
  • 0 <= arr[i] <= 10000
class Solution {public int[] getLeastNumbers(int[] arr, int k) {}
}

4.2 做题思路

因为这道题目没有要求要按照元素的大小顺序返回,所以我们可以模仿上面的第 k 个最大元素的思路进行分冶-快排的算法,对数组进行简单的排序,并且将数组分为:a——小于基准值的部分,b——等于基准值的部分,c——大于基准值的部分。
如果a > k,则需要在 a 部分中继续递归,找到最小的 k 个数;如果a <= k,但是 a + b >= k ,因为 b 部分都是相等的数据,所以可以直接返回;如果前面两种情况都不符合的话,就还需要在 c 部分中继续进行排序,直到在 c 部分中找到 第k - a -b小的元素,然后该位置之前的部分就是我们需要的最小的 k 个数。

4.3 Java代码实现

class Solution {public int[] getLeastNumbers(int[] nums, int k) {qsort(nums,0,nums.length-1,k);int[] ret = new int[k];for(int i = 0; i < k; i++) ret[i] = nums[i];return ret;}private void qsort(int[] nums, int l, int r, int k) {int key = nums[new Random().nextInt(r - l + 1) + l];int left = l - 1, right = r + 1,i = l;while(i < right) {if(nums[i] < key) swap(nums,++left,i++);else if(nums[i] == key) i++;else swap(nums,--right,i);}int a = left - l + 1,b = right - left - 1;if(a > k) qsort(nums,l,left,k);else if(a + b >= k) return;else qsort(nums,right,r,k - a - b);}private void swap(int[] nums, int i, int j) {int t = nums[i];nums[i] = nums[j];nums[j] = t;}
}

在这里插入图片描述

总结

通过本篇博客,我们深入了解了分治算法以及其在快速排序算法中的应用。快速排序是一种高效的排序算法,它利用了分治策略,将大问题逐步分解为规模较小的子问题,并通过递归地解决和合并子问题来完成整个排序过程。

快速排序算法的核心思想是选择一个基准元素,将待排序数组分割成两个子数组,一个小于等于基准的子数组和一个大于基准的子数组。然后,递归地对两个子数组进行排序,最后合并得到最终的有序数组。

快速排序算法具有以下优点:

  1. 高效性:快速排序算法的平均时间复杂度为O(nlogn),在实际应用中表现出良好的性能。它通过不断地将数组划分为较小的子数组进行排序,从而减少了比较和交换的次数。

  2. 原地排序:快速排序算法可以在原数组上进行排序,不需要额外的辅助空间。这对于内存受限的环境来说具有重要意义。

然而,快速排序算法也存在一些注意事项和局限性:

  1. 对于初始数组的选择敏感:快速排序算法的性能高度依赖于选择的基准元素。最理想的情况是选择一个能够将数组划分成大小相似的子数组的基准元素,以避免出现最坏情况的时间复杂度。

  2. 递归深度:在快速排序算法中,递归调用的深度取决于划分操作的方式和基准元素的选择。当数组中存在大量重复元素时,可能会导致递归深度增加,影响算法的性能。

总结而言,快速排序算法是一种高效、原地排序的算法,通过分治策略实现了对待排序数组的快速排序。它在实践中被广泛使用,具有较好的性能。然而,需要根据具体问题选择合适的基准元素,并考虑递归深度对算法性能的影响。

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

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

相关文章

Python Tkinter Multiple Windows 教程

一、说明 在这个Python Tkinter教程中&#xff0c;我们将学习如何在Python Tkinter中创建多个窗口&#xff0c;我们还将介绍与多个窗口相关的不同示例。而且&#xff0c;我们将介绍这些主题。 Python Tkinter multiple windows使用多个窗口的 Python Tkinter 用户注册Python Tk…

【聚类】DBCAN聚类

OPTICS是基于DBSCAN改进的一种密度聚类算法&#xff0c;对参数不敏感。当需要用到基于密度的聚类算法时&#xff0c;可以作为DBSCAN的一种替代的优化方案&#xff0c;以实现更优的效果。 原理 基于密度的聚类算法&#xff08;1&#xff09;——DBSCAN详解_dbscan聚类_root-ca…

跨源资源共享(CORS)Access-Control-Allow-Origin

1、浏览器的同源安全策略 没错&#xff0c;就是这家伙干的&#xff0c;浏览器只允许请求当前域的资源&#xff0c;而对其他域的资源表示不信任。那怎么才算跨域呢&#xff1f; 请求协议http,https的不同域domain的不同端口port的不同 好好好&#xff0c;大概就是这么回事啦&…

【权限提升-Windows提权】-UAC提权之MSF模块和UACME项目-DLL劫持-不带引号服务路径-不安全的服务权限

权限提升基础信息 1、具体有哪些权限需要我们了解掌握的&#xff1f; 后台权限&#xff0c;网站权限&#xff0c;数据库权限&#xff0c;接口权限&#xff0c;系统权限&#xff0c;域控权限等 2、以上常见权限获取方法简要归类说明&#xff1f; 后台权限&#xff1a;SQL注入,数…

1780_添加鼠标右键空白打开命令窗功能

全部学习汇总&#xff1a; GitHub - GreyZhang/windows_skills: some skills when using windows system. 经常执行各种脚本&#xff0c;常常需要切换到命令窗口中输入相关的命令。从开始位置打开cmd然后切换目录是个很糟糕的选择&#xff0c;费时费力。其实Windows 7以及Windo…

经管博士科研基础【19】齐次线性方程组

1. 线性方程组 2. 非线性方程组 非线性方程,就是因变量与自变量之间的关系不是线性的关系,这类方程很多,例如平方关系、对数关系、指数关系、三角函数关系等等。求解此类方程往往很难得到精确解,经常需要求近似解问题。相应的求近似解的方法也逐渐得到大家的重视。 3. 线…

vue3 封装千分位分隔符自定义指令

toLocaleString作用&#xff1a;在没有指定区域的基本使用时&#xff0c;返回使用默认的语言环境和默认选项格式化的字符串。可点击进入MDN查看 // 千分位分隔符指令 import { Directive, DirectiveBinding } from vueconst thousandSeparator: Directive {mounted(el: any, …

Win10搭建VisualSvn Server

Win10搭建VisualSvn Server 目录 Win10搭建VisualSvn Server一、下载VisualSvn Server安装包二、安装VisualSvn Server三、配置和使用VisualSVN Server四、添加用户及权限设定方法五、创建目录及配置权限 1、服务端&#xff1a;有集成了Subversion和Apache、安装使用非常简单且…

Redis从基础到进阶篇(三)----架构原理与集群演变

目录 一、缓存淘汰策略 1.1 LRU原理 1.2 案例分析 1.3 Redis缓存淘汰策略 1.3.1 设置最⼤缓存 1.3.2 淘汰策略 二、Redis事务 2.1 Redis事务典型应⽤—Redis乐观锁 2.2 Redis事务介绍 2.3 事务命令 2.3.1 MULTI 2.3.2 EXEC 2.3.3 DISCARD 2.3.4 WATCH 2.3.5 UNW…

【GitLab私有仓库】在Linux上用Gitlab搭建自己的私有库并配置cpolar内网穿透

文章目录 前言1. 下载Gitlab2. 安装Gitlab3. 启动Gitlab4. 安装cpolar5. 创建隧道配置访问地址6. 固定GitLab访问地址6.1 保留二级子域名6.2 配置二级子域名 7. 测试访问二级子域名 前言 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xf…

视频汇聚/视频云存储/视频监控管理平台EasyCVR部署后无法正常启用是什么问题?该如何解决?

安防监控/视频监控/视频汇聚平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;视频云存储/安防监控汇聚平台EasyCVR支持多种播放协议&#xff0c;包括&#xff1a;HLS、HTTP-FLV、WebSoc…

MavenCentral库发布记录

最近发布了 Android 路由库 URouter&#xff0c;支持 AGP8、ActivityResult启动等特性。 把提交到 Maven Central 过程记录一下。 一、注册 Sonatype 账号&#xff0c;新建项目 注册 https://​​issues.sonatype.org 登录后&#xff0c;新建项目&#xff1a; 相关选项&…

Stable Diffusion 多视图实践

此教程是基于秋叶的webui启动器 1.Stable Diffsuion 使用多视图需要准备一个多角度open pose 图 我给大家提供一个可使用的。 2.需要添加图片到到controlnet当中,不要选择预处理器,选择模型为openpose的模型,然后需要点选同步图片尺寸。 3.然后填写关键字可以参照一下这个…

通过安装cpolar内网穿透在Kali上实现SSH远程连接的步骤指南

文章目录 1. 启动kali ssh 服务2. kali 安装cpolar 内网穿透3. 配置kali ssh公网地址4. 远程连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 简单几步通过cpolar 内网穿透软件实现ssh 远程连接kali! 1. 启动kali ssh 服务 默认新安装的kali系统会关闭ssh 连接服务,我们通…

智慧导览|智能导游系统|AR景区导览系统|景区电子导览

随着文旅市场的加快复苏&#xff0c;以及元宇宙、VR、AR、虚拟数字人等新兴技术的快速发展&#xff0c;文旅行业也正在加快数字化转型的步伐&#xff0c;向智慧景区建设迈进。为满足不同年龄段游客的游览需要&#xff0c;提升旅游服务体验&#xff0c;越来越多的旅游景区、博物…

UDP和TCP协议报文格式详解

在初识网络原理(初识网络原理_蜡笔小心眼子&#xff01;的博客-CSDN博客)这篇博客中,我们简单的了解了一下TCP/IP五层网络模型,这篇博客将详细的学习一下五层网络模型中传输层的两个著名协议:UDP和TCP 目录 一, 传输层的作用 二, UDP 1,UDP协议的特点 2,UDP报文格式 三, TC…

什么是websockret连接

什么是WebSocket WebSocket&#xff0c;是一种网络传输协议&#xff0c;位于 OSI 模型的应用层。可在单个 TCP 连接上进行全双工通信&#xff0c;能更好的节省服务器资源和带宽并达到实时通迅 客户端和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&am…

OpenCV(二十):图像卷积

1.图像卷积原理 图像卷积是一种在图像上应用卷积核的操作。卷积核是一个小的窗口矩阵&#xff0c;它通过在图像上滑动并与图像的像素进行逐元素相乘&#xff0c;然后求和来计算新图像中每个像素的值。通过滑动卷积核并在图像上进行逐像素运算&#xff0c;可以实现一系列图像处理…

(笔记七)利用opencv进行形态学操作

&#xff08;1&#xff09;程序清单 形态学操作是一种图像处理技术&#xff0c;它基于数学形态学理论&#xff0c;用于改变图像的形状和结构。它主要通过结构元素的腐蚀和膨胀操作来实现。 #!/usr/bin/env python # -*- coding:utf-8 -*- """ author: LIFEI t…

Vue生成多文件pdf准考证

这是渲染的数据 这是生成的pdf文件&#xff0c;直接可以打印 需要安装和npm依赖和引入封装的pdf.js文件 npm install --save html2canvas // 页面转图片 npm install jspdf --save // 图片转pdfpdf.js文件 import html2canvas from "html2canvas"; import jsPDF …