leetcode刷题记录:归并排序和快速排序

1. 快速排序

https://labuladong.github.io/algo/di-yi-zhan-da78c/shou-ba-sh-66994/kuai-su-pa-39aa2/

1.1 快排基础

先看核心代码

def sort(nums, lo, hi):if (lo >= hi):returnp = partition(nums, lo, hi)sort(nums, lo, p-1)sort(nums, p+1, hi)

一句话总结快排:先将一个元素排好序(nums[p]),再将剩下的元素排好序

快排的核心是partition函数,其作用是在nums[lo, …, hi]中寻找一个切分点索引p, 让nums[lo,...,p] <= nums[p] < nums[p+1, ..., hi]注意这里的边界条件,是小于等于和大于。即把nums[p]放到正确的位置上;再用递归把左边和右边的部分都排好序即可,其实就是一个二叉树的前序遍历。

partition函数的结果类似一个二叉搜索树nums[p]是根节点,左边是左子树,右边是右子树。或者说,快排就是一个构造二叉搜索树的过程

但是有可能会碰到极端不平衡的情况,比如每次选出的p都在两端,导致左子树或者右子树为空,这样时间复杂度会大幅度上升,因此需要增加数组的随机性。

class Solution(object):def quickSort(self, nums, lo, hi):if (lo >= hi):returnp = self.partition(nums, lo, hi)self.quickSort(nums, lo, p-1)self.quickSort(nums, p+1, hi)def partition(self, nums, lo, hi):import randompivot_idx = random.randint(lo, hi)   self.swap(nums, lo, pivot_idx)                # 随机选择pivotpivot = nums[lo]i, j = lo+1, hiwhile i <= j:            while i < hi and nums[i] <= pivot:i += 1while j > lo and nums[j] > pivot:j -= 1                if (i >= j):break                self.swap(nums, i, j)self.swap(nums, lo, j)return jdef swap(self, nums, i, j):nums[i], nums[j] = nums[j], nums[i]def sortArray(self, nums):""":type nums: List[int]:rtype: List[int]"""self.quickSort(nums, 0, len(nums)-1)return nums

1.2 快排变体:三路快排解决相同元素的case

以上方法是传统的两路快排,缺点是leetcode上有个[2,2,2,2…]的相同元素case过不了(官方的c++题解都过不了)。这里可以用以下三路排序的方式来解决,把整个数组分成<pivot, =pivot 和 >pivot三段来解决。
代码如下,详细思路可以参考: https://www.runoob.com/data-structures/3way-qiuck-sort.html

class Solution(object):def quickSort(self, nums, l, r):import randomif (l >= r):returnself.swap(nums, l, random.randint(l, r))pivot = nums[l]# indexlt, gt = l, ri = l + 1while (i <= gt):if (nums[i] < pivot):self.swap(nums, i, lt+1)i += 1lt += 1                elif (nums[i] > pivot):self.swap(nums, i, gt)gt -= 1else:i += 1self.swap(nums, l, lt)self.quickSort(nums, l, lt-1)self.quickSort(nums, gt+1, r)def swap(self, nums, i, j):nums[i], nums[j] = nums[j], nums[i]def sortArray(self, nums):""":type nums: List[int]:rtype: List[int]"""self.quickSort(nums, 0, len(nums)-1)return nums

时间复杂度
主要的时间复杂度在partition循环里。第一次partition函数的执行时间最长,是数组总的元素数,所以partition的时间复杂度是O(N).
假设切分点每次都落在中间,类比一个二叉树,数的深度是logN,一共要执行logN次partion函数。所以理想的时间复杂度是O(NlogN)

空间复杂度:空间复杂度就是递归栈的深度,即O(logN)

假设数组的分布不均匀,每次partition的长度从N开始递减,时间复杂度是
N + N-1 + N-2 + … + N-(N-1) = N^2 - (N-1)N/2 = N^2/2+N/2 => O(N^2)
这个时候树的深度是N,因此空间复杂度是O(N)

1.3 快排衍生:快速选择 Quick Select

快速选择是快排的变体,例子是leetcode215题:数组中的第k个最大元素 https://leetcode.cn/problems/kth-largest-element-in-an-array/

解法一:二叉堆(优先队列)

一些基本的定义:

  • 堆:一颗用数组表示的特殊的树。我们主要讨论二叉堆,即对应的是二叉树。这颗树需要满足以下条件
    • 完全二叉树:除了最后一层,其他层的节点个数都是满的,最后一层的节点都集中在左部连续位置
    • 堆中每一个节点的值都必须大于等于(或小于等于)其左右子节点的值 。前者叫大顶堆,后者叫小顶堆
  • 大顶堆:即任何一个父节点的值,都 大于等于 它左右孩子节点的值。
  • 小顶堆:即任何一个父节点的值,都 小于等于 它左右孩子节点的值。
  • 堆的建立:这里先跳过
  • 堆的操作:删除、添加
  • 优先队列:优先队列也是一种队列,只不过其入队和出队顺序是按照优先级来的;支持插入和删除最小值操作(返回并删除最小元素)或删除最大值操作(返回并删除最大元素);对应的数据结构就是小顶堆和大顶堆
    本题用二叉堆的解法:

解法二:快速选择

先抄一个解法,有个case会超时,明天再看下。再不休息就要死了啊啊啊啊

class Solution(object):def findKthLargest(self, nums, k):""":type nums: List[int]:type k: int:rtype: int"""def quickSelect(nums, lo, hi, k):lt, gt = partition(nums, lo, hi)if lt > k:return quickSelect(nums, lo, lt-1, k)elif gt < k:return quickSelect(nums, gt+1, hi, k)else:return nums[lt]def partition(nums, lo, hi):import randomp = random.randint(lo, hi)pivot = nums[p]swap(nums, lo, p)lt, gt, i = lo, hi+1, lo+1# print("before:", nums, lt, gt, i)while i < gt:if nums[i] < pivot:swap(nums, i, lt+1)i += 1lt += 1elif nums[i] > pivot:gt -= 1swap(nums, i, gt)else:i += 1swap(nums, lo, lt)# print("after:", nums, lt, gt, i)return lt, gt-1def swap(nums, i, j):nums[i], nums[j] = nums[j], nums[i]# print("nums:", nums)if not nums:return k = len(nums) - kreturn quickSelect(nums, 0, len(nums)-1, k)

2. 归并排序

归并排序采用分而治之(divide-and-conquer)的思想。

  • 分:将问题分解成一个个小的子问题然后递归求解
  • 治:将解决好的问题merge在一起。
    即先让每个子序列有序,再让子序列之间有序,即可完成数组的排序。
    图片示例:https://blog.csdn.net/python_tian/article/details/122086920

归并排序的思路和代码框架

归并排序就是先把左半边数组排好序,再把右半边数组排好序,然后把两半数组合并。

def sort(nums, lo, hi):if lo == hi:return numsmid = lo + (hi-lo)//2sort(nums, lo, mid)sort(nums, mid+1, hi)# 后序位置merge(nums, lo, mid, hi)
def merge(nums, lo, mid, hi):pass

在二叉树的纲领篇(记得放link)我们说过,二叉树问题有两种解法,一是遍历一遍二叉树,而是分解问题。从上面这段代码可以看出,归并排序就是分解二叉树的解法。
归并排序的逻辑可以抽象成一个二叉树的后序遍历。树上每个结点的值是nums[lo:hi], 其中左子树是nums[lo:mid-1], 右子树是nums[mid+1:hi],根节点是nums[mid].
然后在每个节点的后序位置执行merge函数,合并两个子节点上的子数组。
时间复杂度O(nlogn), 空间O(n)

class Solution(object):def sortArray(self, nums):""":type nums: List[int]:rtype: List[int]"""        n = len(nums)if n <= 1:return numsself.temp = [0] * nself.mergeSort(nums, 0, n-1)return numsdef mergeSort(self, nums, lo, hi):if lo >= hi:returnmid = lo + (hi-lo)//2self.mergeSort(nums, lo, mid)self.mergeSort(nums, mid+1, hi)self.merge(nums, lo, mid, hi)def merge(self, nums, lo, mid, hi):# 合并两个有序数组.for i in range(lo, hi+1):self.temp[i] = nums[i]i, j = lo, mid+1for p in range(lo, hi+1):if i == mid+1:# 左半边数组已经全部被合并nums[p] = self.temp[j]j += 1elif j == hi+1:# 右半边数组已经全部被合并nums[p] = self.temp[i]i += 1elif self.temp[i] > self.temp[j]:nums[p] = self.temp[j]j += 1else:nums[p] = self.temp[i]i += 1

归并排序的应用:计算右侧小于当前元素的个数

leetcode315 https://leetcode.cn/problems/count-of-smaller-numbers-after-self/description/
构造一个pair class,key是元素值,value是index
merge函数中arr[i] = temp[p1]时更新count

class Solution(object):def countSmaller(self, nums):""":type nums: List[int]:rtype: List[int]"""class Pair(object):def __init__(self, key, val):self.key = keyself.val = valdef mergeSort(arr, lo, hi):if lo >= hi:returnmid = lo  + (hi-lo)//2mergeSort(arr, lo, mid)mergeSort(arr, mid+1, hi)merge(arr, lo, mid, hi)def merge(arr, lo, mid, hi):idx = lop1, p2 = lo, mid+1for i in range(lo, hi+1):temp[i] = arr[i]for i in range(lo, hi+1):if p1 == mid+1:arr[i] = temp[p2]p2 += 1elif p2 == hi+1:arr[i] = temp[p1]count[temp[p1].val] += p2-mid-1# print(temp[p1].val, count)p1 += 1 elif temp[p1].key <= temp[p2].key:arr[i] = temp[p1]count[temp[p1].val] += p2-mid-1p1 += 1                    else:arr[i] = temp[p2]p2 += 1            arr = [Pair(nums[i], i) for i in range(len(nums))]temp = [Pair(0, i) for i in range(len(nums))]count = [0 for _ in range(len(nums))]mergeSort(arr, 0, len(arr)-1)return count

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

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

相关文章

小米消金深耕老年服务,不断强化消费者权益保护新举措

随着互联网金融的蓬勃发展&#xff0c;网上银行、手机银行及非现金支付等新型服务形式日益普及&#xff0c;金融产品与服务层出不穷。然而&#xff0c;对于老年人群体而言&#xff0c;他们在面对现代科技和网络时可能感到困惑&#xff0c;容易对复杂的金融产品产生误解。鉴于此…

一文带你了解5款高效率软件,建议收藏

​ 人类与99%的动物之间最大差别在于是否会运用工具&#xff0c;借助好的工具&#xff0c;能提升几倍的工作效率。 1. 高速文件复制——TeraCopy ​ TeraCopy是一款高效的文件复制工具&#xff0c;可以大幅度提高文件复制和移动的速度。它支持多线程复制、错误恢复、校验和等…

React的路由

1. 什么是前端路由 一个路径 path 对应一个组件 component 当我们在浏览器中访问一个 path 的时候&#xff0c;path 对应的组件会在页面中进行渲染 2. 创建路由开发环境 # 使用CRA创建项目 npm create-react-app react-router-pro# 安装最新的ReactRouter包 npm i react-ro…

罗宾斯《管理学》第13版/教材讲解/考研真题视频课程/网课

本课程是罗宾斯《管理学》&#xff08;第13版&#xff09;精讲班&#xff0c;为了帮助参加研究生招生考试指定考研参考书目为罗宾斯《管理学》&#xff08;第13版&#xff09;的考生复习专业课&#xff0c;我们根据教材和名校考研真题的命题规律精心讲解教材章节内容。 序号名…

如何使用PHP进行图片处理?

如何使用PHP进行图片处理&#xff1f; 使用PHP进行图片处理是一项强大的功能&#xff0c;它可以让你在服务器端对图像进行各种操作&#xff0c;如裁剪、缩放、添加水印、调整颜色等。这通常通过使用GD库或Imagick扩展来实现。下面将详细介绍如何使用PHP和这两个工具进行图片处…

WinForms 应用程序中使用 SignalR 连接到服务器

安装 dotnet Install Microsoft.AspNetCore.SignalR.ClientWinForms 应用程序中使用 SignalR 连接到服务器时 安装 SignalR 客户端库&#xff1a;使用 NuGet 包管理器安装 SignalR 客户端库。创建 SignalR 连接&#xff1a;在代码中创建 SignalR 连接&#xff0c;并指定服务器…

前端工程化Vue使用Node.js永久设置国内高速npm镜像源

前端工程化Vue使用Node.js永久设置国内高速npm镜像源 接续上篇错误收录&#xff0c;此篇通过简单配置永久设置国内高速npm镜像源方法 1.更换新版镜像 清空npm缓存 npm cache clean --force修改回原版镜像源或直接删除配置过的镜像源 npm config set registry https://registr…

Linux挂载硬盘

1、查看硬盘数量 fdisk -l # 可以看到三个磁盘 # /dev/vda 50G # /dev/vdb 100G 新增 # /dev/vdc 100G 新增2、查看当前挂载情况 df -h # 可以看到50G的已经挂载3、格式化待挂载盘 # 对新的数据盘进行挂载前要进行格式化&#xff0c;只有格式化后才可以挂载 mkfs.ext4 /dev/…

电商技术揭秘三十五:智能风控功能架构浅析

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…

【c++】优先级队列与仿函数:C++编程的强大组合

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来讲解优先级队列priority_queue 目录 1.priority_queue的介绍和使用函数使用仿函数的使用与介绍greater和less 2.priority_queue的模拟实现基本框架…

网络安全SQL注入

HTML & Vue.js 结合使用 HTML 是网页的基础结构语言&#xff0c;而 Vue.js 是一个用于构建用户界面的渐进式JavaScript框架。结合两者&#xff0c;你可以创建动态、响应式的Web应用程序。Vue.js通过指令&#xff08;如v-bind, v-if, v-for&#xff09;无缝集成到HTML中&am…

统一化数据库:为大语言模型垂域应用奠定基础

编者按&#xff1a;检索增强生成&#xff08;RAG&#xff09;技术因在减少生成幻觉和虚构信息方面的显著效果&#xff0c;以及对知识及时更新能力的改善&#xff0c;正逐渐成为大语言模型系统的主流架构之一。随着 RAG 技术的广泛应用&#xff0c;其核心组件——向量数据库&…

2022 csp-j 答案(精选题)

主题目 16 - 21题 22 - 27题 28 - 34题 35 -39题 40 - 44题 精选题目 答案及解析 28题 答案&#xff1a;T 解析&#xff1a;考查时间复杂度评估。 31题 答案&#xff1a;A 解析&#xff1a;模拟即可。 32题 答案&#xff1a;B 解析&#xff1a;模拟即可。 34题 答…

Android 学习 鸿蒙HarmonyOS 4.0 第一天

1.1系统定位 1.1.1系统定位 HarmonyOS 是一款面向万物互联时代的&#xff0c;全新的分布式操作系统。 在传统的单设备系统能力基础上&#xff0c;HarmonyOS 提出了基于同一套系统能力&#xff0c;适配多种终端形态的分布式理念&#xff0c;能支持手机&#xff0c;平板&#xf…

线上线下包搭建小程序/公众号/H5 支持二开!

网上交友有以下三个积极影响&#xff1a; 1. 扩展社交圈和增加社交机会&#xff1a;网上交友可以让人们接触到不同地区、不同背景、不同文化的人&#xff0c;拓展人们的社交圈并且增加交友机会。这些新的社交联系对于个人的成长和发展有积极的影响&#xff0c;可以让人们学习新…

线阵相机和面阵相机简介

线阵相机 线阵相机&#xff0c;顾名思义就是所探测的物体要在一个很长的界面上。线阵相机的传感器只有一行感光像素&#xff0c;所以线阵相机一般具有非常高的扫描频率和分辨率。 线阵相机特点 线阵相机使用的线扫描传感器通常只有一行感光单元&#xff08;少数彩色线阵使用…

国内各种免费AI聊天机器人(ChatGPT)推荐(上)

作者主页&#xff1a;点击&#xff01; 国内免费AI推荐专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月27日11点25分 欢迎来到AI聊天机器人推荐系列的第一篇文章&#xff01; 在这个系列中&#xff0c;我将引领您探索国内各种AI聊天机器人的精彩世界。 从…

k8s-身份认证与权限

认证概述 Kubernetes作为一个分布式集群的管理工具&#xff0c;保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。 在Kubernetes集群中&#xff0c;客户端通常有两类&#xff1a; User Account&#xff1a;一般是独…

动态规划:0/1背包问题

01背包问题是一个经典的动态规划问题&#xff0c;它询问在给定的物品和背包容量下&#xff0c;如何选择物品使得背包中的物品总价值最大&#xff0c;同时保证不超过背包的容量限制。物品不能分割&#xff0c;每个物品只能选择放入或不放入背包。 问题定义 输入&#xff1a; 物…

基于FPGA的数字信号处理(4)--浮点数的定点化

写在前面 首先要说明的是&#xff0c;题目《浮点数的定点化》中所谓的 浮点数 并不是指 IEEE754 规定的 单精度浮点数 或者 双精度浮点数 等格式&#xff0c;而是指10进制小数。所以说白了&#xff0c;这篇文章要讲的就是如何将10进制小数采用定点数的形式表示。 为什么2进制无…