快速排序学习笔记

代码框架

    // 在数组nums将下标从left到right中进行从小到大排序// 原理是先将一个元素排好序,然后将其他的元素排好序void sort(int[] nums, int left, int right) {if (left >= right) {return;}// 对数组nums[left,right]进行切分,使得nums[left,p-1]<=nums[p]<=nums[p+1,right]int p = partition(nums, left, right);// 去左右数组进行切分sort(nums, left, p - 1);sort(nums, p - 1, right);}// 在数组中nums[left,right]中寻找到一个分界点pint partition(int[] nums, int left, int right) {// 将数组中最左边的元素放入正确的位置后,返回该位置int pivot = nums[left];// 最后数组被分为三个区间,[left,i)和i和(j,right]int i = left + 1, j = right;while (i <= j) {// i右移找大于pivot的数while (i < right && nums[i] <= pivot) {i++;}// j左移找到小于pivot的数while (j > left && nums[j] >= pivot) {j--;}// 判断此时的i和j是否越界if (i >= j) {break;}swap(nums, i, j);}// 最后将pivot和j进行交换swap(nums, left, j);return j;}// 将元素随机打乱void shuffle(int[] nums) {int len = nums.length;Random random = new Random();for (int i = 0; i < len; i++) {// 生成[i,len-1]之间的随机数int index = i + random.nextInt(len - i);swap(nums, i, index);}}void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}

为什么要用shuffle将数组进行乱序处理?

目的是消除对初始输入的依赖,使得算法更具有随机性。在快排算法中,选择分区点的方式可能会影响算法的性能。如果数组已经有序或者近似有序,选择第一个元素作为分区点可能导致算法性能下降,因为分区点选择的不好可能导致快速选择算法的退化为O(n^2)的时间复杂度

在快速排序中,`partition()` 会选择一个基准值(pivot),然后重新排列数组或列表的元素,使得小于基准值的元素都位于它的左侧,大于基准值的元素都位于它的右侧。通常,它返回一个索引值,表示基准值在排序后所在的位置,同时也将数组或列表划分成两个部分。

再这么看快排就很简单了,一直分割左右两块,直到所有都排序完为止。

注意base case是左应该<=右!

另外,其实对比可以发现出,快排和二叉树的前序遍历是很像的:

/* 二叉树遍历框架 */
void traverse(TreeNode root) {if (root == null) {return;}/****** 前序位置 ******/print(root.val);/*********************/traverse(root.left);traverse(root.right);
}

一句话总结

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

快速排序的核心无疑是 `partition` 函数, `partition` 函数的作用是在 `nums[lo..hi]` 中寻找一个切分点 `p`,通过交换元素使得 `nums[lo..p-1]` 都小于等于 `nums[p]`,且 `nums[p+1..hi]` 都大于 `nums[p]`:

一个元素左边的元素都比它小,右边的元素都比它大,不就是它自己已经被放到正确的位置上了吗?

所以 `partition` 函数干的事情,其实就是把 `nums[p]` 这个元素排好序了。然后呢?你再把剩下的元素排好序不就得了。

剩下的元素有哪些?左边一坨,右边一坨,去吧,对子数组进行递归,用 `partition` 函数把剩下的元素也排好序。

从二叉树的视角,我们可以把子数组 `nums[lo..hi]` 理解成二叉树节点上的值,`sort` 函数理解成二叉树的遍历函数。

排序数组

912. 排序数组 - 力扣(LeetCode)

class Solution {public int[] sortArray(int[] nums) {shuffle(nums);quickSort(nums, 0, nums.length - 1);return nums;}void shuffle(int[] nums) {Random random = new Random();for (int i = 0; i < nums.length; i++) {int p = i + random.nextInt(nums.length - i);swap(nums, i, p);}}void quickSort(int[] nums, int left, int right) {if (left < right) {int pivot = partition(nums, left, right);// 分治,分别对左右的数据开始递归quickSort(nums, left, pivot - 1);quickSort(nums, pivot + 1, right);}}int partition(int[] nums, int left, int right) {int pivot = nums[left];int i = left + 1;int j = right;while (i <= j) {while (i <= right && nums[i] <= pivot) {i++;}while (j >= left && nums[j] > pivot) {j--;}if (i <= j) {swap(nums, i, j);}}swap(nums, left, j);return j;}void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}

数组中的第k个最大元素

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

class Solution {public int findKthLargest(int[] nums, int k) {shuffle(nums);int left = 0, right = nums.length - 1;// 因为是找第 k 大的元素,而不是找第 k 小的元素,所以要从右边开始数 k 个k = nums.length - k;while (left <= right) {int p = partition(nums, left, right);// 缩小查找范围if (p < k) {// 说明第k大的元素在分区右边left = p + 1;} else if (p > k) {// 说明第k大的元素在分区左边right = p - 1;} else {return nums[p];}}// 未找到return -1;}void shuffle(int[] nums) {Random random = new Random();int n = nums.length;for (int i = 0; i < n; i++) {int r = i + random.nextInt(n-i);swap(nums, i, r);}}int partition(int[] nums, int left, int right) {int p = nums[left], i = left + 1, j = right;while (i <= j) {// `i` 向右移动,找到第一个大于 `p` 的元素while (i <= right && nums[i] <= p) i++;// `j` 向左移动,找到第一个小于等于 `p` 的元素while (j >= left && nums[j] > p) j--;if (i >= j) break;// 如果左区间有比 p 大的数,右区间有比 p 小的数,且下标左小于右,交换i与jswap(nums, i, j);}// 最后,将 `nums[left]`(即分区点原始位置)与 `nums[j]` 交换,将分区点放到正确的位置。swap(nums, left, j);// 返回分区点索引return j;}void swap(int[] nums, int i, int j) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}
}

补充:

优先级队列做法

class Solution {public int findKthLargest(int[] nums, int k) {// 利用优先级队列,自动是小根堆PriorityQueue<Integer> queue = new PriorityQueue<>();for (int i = 0; i < k; i++) {queue.add(nums[i]);}for (int i = k; i < nums.length; i++) {if (nums[i] > queue.peek()) {queue.poll();queue.add(nums[i]);}}return queue.peek();}
}

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

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

相关文章

软件测试|QtDesigner配置以及使用

简介 上一篇文章我们介绍了PyQt5环境的安装和配置&#xff0c;并且安装了Qt tools工具&#xff0c;本文我们将介绍如何使用Qt tools的QtDesigner如何使用。 QtDesigner 的启动和入门 打开我们的项目从顶部菜单栏选择&#xff1a;Tools -> ExternalTools -> QtDesigner…

基于TCP的半双工网络编程实践

首先我们先了解一下什么是半双工通信&#xff1f; 半双工数据传输允许数据在两个方向上传输&#xff0c;但是在某一时刻&#xff0c;只允许数据在一个方向上传输&#xff0c;它实际上是一种切换方向的单工通信。 TCP服务端代码&#xff1a; #include <stdio.h> #inclu…

Blob 和 File 的区别,以及 Blob、File、Base64 三种类型的相互转换

一、 Blob 和 File 的区别 Blob 和 File 都是 JavaScript 中用于处理二进制数据的对象&#xff0c;但它们有一些区别。 Blob 定义&#xff1a; Blob 表示不可变的、原始数据的类文件对象。它通常用于存储大块的二进制数据&#xff0c;比如图像、音频或视频文件等。 特点&…

如何在免费云Colab上使用扩散模型生成图片?

前言 在人工智能技术的迅猛发展下&#xff0c;内容生成领域也迎来了一系列创新的突破。其中&#xff0c;使用扩散模型&#xff08;如Stable Diffusion&#xff09;从文字生成图片的AI技术备受瞩目。这一技术的出现&#xff0c;为我们创造栩栩如生的图像提供了全新的可能性。本…

MongoDB - 库、集合、文档(操作 + 演示 + 注意事项)

目录 一、MongoDB 1.1、简介 a&#xff09;MongoDB 是什么&#xff1f;为什么要使用 MongoDB&#xff1f; b&#xff09;应用场景 c&#xff09;MongoDB 这么强大&#xff0c;是不是可以直接代替 MySQL &#xff1f; d&#xff09;MongoDB 中的一些概念 e&#xff09;Do…

k8s--动态pvc和pv

前情回顾 存储卷&#xff1a; emptyDir 容器内部&#xff0c;随着pod销毁&#xff0c;emptyDir也会消失 不能做数据持久化 hostPath&#xff1a;持久化存储数据 可以和节点上目录做挂载。pod被销毁了数据还在 NFS&#xff1a;一台机器&#xff0c;提供pod内容器所有的挂载点…

算法训练day11Leetcode20有效的括号1047删除字符串中所有相邻重复项150逆波兰表达式求值

今日学习的文章和视频链接 https://leetcode.cn/problems/valid-parentheses/description/ https://programmercarl.com/0020.%E6%9C%89%E6%95%88%E7%9A%84%E6%8B%AC%E5%8F%B7.html 20 有效的括号 题目描述 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#…

使用emu8086实现——显示程序设计实验

一、实验目的 1. 掌握DOS系统功能调用的编程方法 2. 掌握汇编语言程序设计方法&#xff0c;自己编写程序&#xff0c;并调试运行验证结果。 二、实验内容 1.在显示器上的输出窗口显示A-Z共26个大写英文字母。 代码&#xff1a; datas segmentzimu db "A B C D E F G H …

git入门之本地操作

1、启动git命令输入 在想要建立仓库&#xff0c;建议的方式是在文件夹中右键单击&#xff0c;选择git bash here 2、初始化仓库命令&#xff1a;git init 3、查看仓库状态&#xff1a;git status 4、更新仓库特定文件&#xff1a;git add xxx 5、更新仓库所有文件&#xff1…

LLVM代码内容

1. LLVM简介 LLVM库包含所有LLVM顶层项目&#xff0c;可以分为以下几类&#xff1a; • LLVM核心库和附加内容 • 编译器和工具 • 运行时库 LLVM是一个编译器框架。LLVM作为编译器框架&#xff0c;是需要各种功能模块支撑起来的。可以将clang和lld都看做是LLVM的组成部分。…

老卫带你学---leetcode刷题(50. Pow(x, n))

50. Pow(x, n) 问题 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000 示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出…

RandLA-Net导出onnx模型并使用onnxruntime推理

首先下载RandLA-Net工程&#xff1a;https://github.com/tsunghan-wu/RandLA-Net-pytorch 导出onnx模型 import torch from utils.config import ConfigSemanticKITTI as cfg from network.RandLANet import Networkmodel Network(cfg) checkpoint torch.load("./pret…

Pandas实战100例 | 案例 13: 数据分类 - 使用 `cut` 对数值进行分箱

案例 13: 数据分类 - 使用 cut 对数值进行分箱 知识点讲解 在数据分析中&#xff0c;将连续的数值数据分类成不同的区间&#xff08;或“分箱”&#xff09;是一种常见的做法。Pandas 提供了 cut 函数&#xff0c;它可以根据你指定的分箱边界将数值数据分配到不同的类别中。 …

力扣(leetcode)第709题转成小写字母(Python)

709.转成小写字母 题目链接&#xff1a;709.转成小写字母 给你一个字符串 s &#xff0c;将该字符串中的大写字母转换成相同的小写字母&#xff0c;返回新的字符串。 示例 1&#xff1a; 输入&#xff1a;s “Hello” 输出&#xff1a;“hello” 示例 2&#xff1a; 输入&am…

浏览器进程模型和JS的事件循环

一、浏览器的进程模型 1、什么是进程&#xff1f; 程序运行所需要的专属内存空间 2、什么是线程&#xff1f; ​​​​​运行​代码的称为线程&#xff08;同一个进程中的线程共享进程的资源&#xff09; ⼀个进程⾄少有⼀个线程&#xff0c;所以在进程开启后会⾃动创建⼀个线…

ubuntu在使用su - root时提示认证失败

原因&#xff1a; 在ubuntu中&#xff0c;默认情况下&#xff0c;是没有开启root账户的&#xff0c;因此在输入密码的时候会显示认证失败。 解决方法&#xff1a; 输入sudo passwd root设置密码来激活root权限 参考链接&#xff1a;ubuntu出现su:Authentication failure解决方…

《剑指 Offer》专项突破版 - 面试题 7 : 数组中和为 0 的 3 个数字(C++ 实现)

题目链接&#xff1a;15. 三数之和 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 输入一个数组&#xff0c;如何找出数组中所有和为 0 的 3 个数字的三元组&#xff1f;需要注意的是&#xff0c;返回值中不得包含重复的三元组。例如&#xff0c;在数组 [-1, 0, …

【数值分析】数值微分

1. 基于Taylor公式的数值微分公式 f ′ ( x ) ≈ f ( x h ) − f ( x ) h , 截断误差 − f ′ ′ ( ξ ) 2 h f(x)\approx \frac{f(xh)-f(x)}{h}\,\,,\,\, 截断误差 \,\,\, - \frac{f(\xi)}{2}h f′(x)≈hf(xh)−f(x)​,截断误差−2f′′(ξ)​h f ′ ( x ) ≈ f ( x ) − f …

【JUC进阶】14. TransmittableThreadLocal

目录 1、前言 2、TransmittableThreadLocal 2.1、使用场景 2.2、基本使用 3、实现原理 4、小结 1、前言 书接上回《【JUC进阶】13. InheritableThreadLocal》&#xff0c;提到了InheritableThreadLocal虽然能进行父子线程的值传递&#xff0c;但是如果在线程池中&#x…

spring-mvc(1):Hello World

虽然目前大多数都是使用springboot来开发java程序&#xff0c;或者使用其来为其他端提供接口&#xff0c;而为其他端提供接口&#xff0c;这些功能都是依靠springmvc实现的&#xff0c;所以有必要学习一下spring-mvc&#xff0c;这样才能更好的学习springboot。 一&#xff0c…