刷题 排序算法
912. 排序数组
注意这道题目所有
O(n^2)复杂度的算法都会超过时间限制,只有O(nlogn)的可以通过

- 快速排序空间复杂度为
O(logn)是由于递归的栈的调用 - 归并排序空间复杂度为
O(n)是由于需要一个临时数组(当然也需要栈的调用,但是O(logn)<O(n)的忽略了)
基于插入的排序算法
直接插入排序
类似于打扑克牌的操作 直接插入排序(算法过程, 效率分析, 稳定性分析)
- 时间复杂度:最好情况
O(n), 最坏情况O(n^2) - 空间复杂度:
O(1) - 稳定性:是稳定的
class Solution {
public:vector<int> sortArray(vector<int>& nums) {// 插入排序for (int i = 1; i < nums.size(); ++i) {int cur_val = nums[i];int j = i - 1;while (j >= 0 && nums[j] > cur_val) { // 寻找插入位置nums[j + 1] = nums[j];--j;}nums[j + 1] = cur_val;}return nums;}
};
折半插入排序
直接插入排序是使用
顺序查找的方法,从后往前寻找插入的位置
同理我们也可以使用二分查找的方式来寻找插入的位置
折半查找减少了比较的次数,将比较操作的时间复杂度降低为O(logn),但没有减少移动的次数,整体时间复杂度还是O(n^2)
- 时间复杂度:最好情况
O(n), 最坏情况O(n^2) - 空间复杂度:
O(1) - 稳定性:是稳定的
class Solution {
public:int binarySearch(vector<int> nums, int right, int target) {// 找到第一个大于 target 的值int left = 0;// 使用左闭右闭区间while (left <= right) { // 区间不为空int mid = left + (right - left) / 2;// 循环不变量// nums[left - 1] <= target// nums[right + 1] > targetif (nums[mid] <= target) {left = mid + 1;} else if (nums[mid] > target) {right = mid - 1;}}return left;}vector<int> sortArray(vector<int>& nums) {// 折半插入排序for (int i = 1; i < nums.size(); ++i) {int cur_val = nums[i];int insert_pos = binarySearch(nums, i - 1, cur_val);for (int j = i - 1; j >= insert_pos; --j) {nums[j + 1] = nums[j]; }nums[insert_pos] = cur_val;}return nums;}
};
希尔排序 - 插入排序的改进 - 缩小增量排序
插入排序在序列基本有序时效率较高
基于这个特点,希尔排序就是对数组分组进行插入排序,分组的组数就是 d,也即增量,一种简单的增量序列就是从 num.size() / 2 开始,一直缩小到 1,当然也可以采用其他的增量序列
- 时间复杂度:最好情况
O(n), 最坏情况O(n^2),平均复杂度O(n^1.3)(了解即可) - 空间复杂度:
O(1) - 稳定性:不稳定的
class Solution {
public:vector<int> sortArray(vector<int>& nums) {for (int d = nums.size() / 2; d >= 1; --d) {// 分组插入排序for (int k = 0; k < d; ++k) {// 组内进行插入排序for (int i = k + d; i < nums.size(); i += d) {int cur_val = nums[i];int j = i - d;while (j >= 0 && nums[j] > cur_val) {nums[j + d] = nums[j];j -= d;}nums[j + d] = cur_val;}}}return nums;}
};
基于交换的排序算法
冒泡排序
- 时间复杂度:最好情况
O(n), 最坏情况O(n^2) - 空间复杂度:
O(1) - 稳定性:稳定
class Solution {
public:vector<int> sortArray(vector<int>& nums) {// 冒泡排序for (int i = nums.size() - 1; i >= 1; --i) {bool swapped = false;for (int j = 0; j < i; ++j) {if (nums[j] > nums[j + 1]) {swap(nums[j], nums[j + 1]);swapped = true;}}if (!swapped) { // 没有发生交换,说明代码已经有序break;}}return nums;}
};
快速排序 图解 - 分治法
步骤:
- 随机选取一个位置 nums[i] = x
- 将大于 x 的值都移到 nums[i] 的左边,小于 x 的值都移动到 nums[i] 的右边
- 对 nums[0 ~i -1] 和 nums[i + 1 ~ n -1] 分别进行快速排序
- …
步骤中的核心问题:如何 将大于 x 的值都移到 nums[i] 的左边,小于 x 的值都移动到 nums[i] 的右边?



- 时间复杂度:最好情况
O(n), 最坏情况O(n^2) - 空间复杂度:
O(1) - 稳定性:稳定
class Solution {
public:void quickSort(vector<int>& nums, int left, int right) {if (left >= right) return; // 递归终止条件int p = partition(nums, left, right);quickSort(nums, left, p - 1);quickSort(nums, p + 1, right);}int partition(vector<int>& nums, int left, int right) {int p = left + rand() % (right - left + 1); // 生成 [left ~ right] 区间内的随机数swap(nums[p], nums[right]); // 将 pivot 和末尾值交换int i = left;// 维护的区间: [left, i) 区间内的值小于等于 nums[right]// [j, right) 区间内的值大于 nums[right]for (int j = left; j < right; ++j) {if (nums[j] <= nums[right]) {// 此时不满足我们对区间的要求了// 调整区间使其满足要求// {nums[left] ... nums[i-1]} {[nums[i] ... nums[j]]}swap(nums[i], nums[j]);++i;// --> {nums[left] ... nums[i-1] nums[j]} { ... nums[i]]}}}swap(nums[i], nums[right]);return i;}vector<int> sortArray(vector<int>& nums) {srand(time(0)); // 以当前时间为随机数种子quickSort(nums, 0, nums.size() - 1);return nums;}
};
但是上面这段代码提交还是会超过时间限制,由于当前的快速排序在处理包含大量相同元素的数组时,表现不佳。快速排序在最坏情况下的时间复杂度是 O(n^2)
使用三向切分的快速排序
三向切分是对标准快速排序的一种改进,特别适用于处理大量重复元素的情况。它将数组分为三个部分:
- 小于基准的部分
- 等于基准的部分
- 大于基准的部分
通过三向切分,可以避免在处理大量重复元素时退化为 O(n²),使得时间复杂度保持在 O(n log n)。
class Solution {
public:void quickSort3Way(vector<int>& nums, int left, int right) {if (left >= right) return; // 递归终止条件int pivot = nums[left + rand() % (right - left + 1)]; // 选取随机基准int lt = left, i = left, gt = right; // 初始化 lt、i、gt 指针// [left ~ lt) 小于 pivot// [lt, gt] 等于 pivot// [gt + 1, right] 大于 pivot while (i <= gt) {if (nums[i] < pivot) {swap(nums[lt], nums[i]);++lt;++i;} else if (nums[i] > pivot) {swap(nums[i], nums[gt]);--gt; // 不能++i,因为换下来的这个数的值还没有跟 pivot 比较过} else {++i;}}// 递归处理小于和大于基准的部分quickSort3Way(nums, left, lt - 1);quickSort3Way(nums, gt + 1, right);}vector<int> sortArray(vector<int>& nums) {srand(time(0)); // 只需初始化一次随机数种子quickSort3Way(nums, 0, nums.size() - 1);return nums;}
};
选择排序
简单选择排序
- 时间复杂度:最好情况
O(n), 最坏情况O(n^2) - 空间复杂度:
O(1) - 稳定性:不稳定
不稳定性分析:
假设有一个数组 [4a, 2, 4b, 3],其中 4a 和 4b 是两个相同值的元素,但具有不同的初始顺序。
第一轮:选择 2 作为最小元素,然后与 4a 交换,数组变为 [
2,4a, 4b, 3]。第二轮:选择 3 作为最小元素,然后与 4a 交换,数组变为 [2,
3, 4b,4a]。 注意此时 4a 和 4b 的相对顺序已经被改变:原本 4a 在 4b 之前,现在 4a被排在了 4b 之后。因此,选择排序是不稳定的,因为它改变了相同值元素的初始顺序。
class Solution {
public:vector<int> sortArray(vector<int>& nums) {// 选择排序for (int i = 0; i < nums.size() - 1; ++i) {int min_idx = i;for (int j = i + 1; j < nums.size(); ++j) {if (nums[j] < nums[i]) {min_idx = j; // 最小值的索引}}swap(nums[i], nums[min_idx]); // 和最小值进行交换}return nums;}
};
堆排序 - 堆 - 完全二叉树 - 顺序存储


class Solution {
public:// 堆化函数:调整以 i 为根的子树,n 为堆的大小void heapify(vector<int>& nums, int n, int i) {int largest = i; // 初始化为根节点int left = 2 * i + 1; // 左孩子int right = 2 * i + 2; // 右孩子// 如果左孩子比根节点大if (left < n && nums[left] > nums[largest]) {largest = left;}// 如果右孩子比当前最大值还大if (right < n && nums[right] > nums[largest]) {largest = right;}// 如果最大值不是根节点,交换并继续堆化if (largest != i) {swap(nums[i], nums[largest]);// 递归对受影响的子树进行堆化heapify(nums, n, largest);}}vector<int> sortArray(vector<int>& nums) {int n = nums.size();// 从最后一个非叶子节点开始建堆,调整整个堆for (int i = n / 2 - 1; i >= 0; --i) {heapify(nums, n, i);}// 逐一将堆顶元素与末尾元素交换,并重新调整堆for (int i = n - 1; i > 0; --i) {// 将当前堆顶(最大值)与末尾元素交换swap(nums[0], nums[i]);// 重新对剩下的部分进行堆化heapify(nums, i, 0);}return nums;}
};
归并排序
可以将排序问题分解成 将左半边排序 + 将右半边排序 + 合并左右两侧
- 时间复杂度:
O(n log n) - 空间复杂度:
O(n) (源于临时数组) - 稳定性:稳定
class Solution {
public:void mergeArray(vector<int> &nums, vector<int>& tmp, int left, int right) {if (right == left) return; // 递归终止条件int mid = left + (right - left) / 2;mergeArray(nums, tmp, left, mid); // 对左半边进行排序mergeArray(nums, tmp, mid + 1, right); // 对右半边进行排序// 重要优化:如果左右两部分已经有序,可以跳过合并if (nums[mid] <= nums[mid + 1]) return;// 左右两侧均已完成排序,对二者进行合并int i = left, j = mid + 1, k = left;while (i <= mid && j <= right) {if (nums[i] <= nums[j]) {tmp[k++] = nums[i++]; } else {tmp[k++] = nums[j++];}}while (i <= mid) {tmp[k++] = nums[i++];}while (j <= right) {tmp[k++] = nums[j++];}copy(tmp.begin() + left, tmp.begin() + right + 1, nums.begin() + left);}vector<int> sortArray(vector<int>& nums) {vector<int> tmp(nums.size(), 0);mergeArray(nums, tmp, 0, nums.size() - 1);return nums;}
};
相关文章:
刷题 排序算法
912. 排序数组 注意这道题目所有 O(n^2) 复杂度的算法都会超过时间限制,只有 O(nlogn) 的可以通过 快速排序空间复杂度为 O(logn)是由于递归的栈的调用归并排序空间复杂度为 O(n) 是由于需要一个临时数组 (当然也需要栈的调用,但是 O(logn) < O(n) 的…...
【python3】tornado高性能编程
使用多进程充分利用cpu使用异步编程 asyncio import asyncio import time from abc import ABC from concurrent.futures import ProcessPoolExecutor from tornado import web, ioloop, genasync def async_task(name):print(f"start: {name}")st int(time.time()…...
构建高效购物推荐系统:SpringBoot实战
1系统概述 1.1 研究背景 如今互联网高速发展,网络遍布全球,通过互联网发布的消息能快而方便的传播到世界每个角落,并且互联网上能传播的信息也很广,比如文字、图片、声音、视频等。从而,这种种好处使得互联网成了信息传…...
docker tar包安装 docker-26.1.4.tgz
一、docker安装 1.先将docker安装包(docker-26.1.4.tgz)拷贝到DM系统中。 下载地址 Index of linux/static/stable/x86_64/ 1.先将docker安装包(docker-26.1.4.tgz)拷贝到DM系统中。 2.解压docker安装包 tar zxf docker-26.1.…...
Github 2024-10-12 Rust开源项目日报 Top10
根据Github Trendings的统计,今日(2024-10-12统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10JavaScript项目1Svelte项目1TypeScript项目1Rust: 构建可靠高效软件的开源项目 创建周期:5064 天开发语言:Rust协议类型:OtherSta…...
Spring Cloud 微服务架构及其应用:设计、实现与优化
引言 随着互联网技术的不断发展,传统的单体应用架构逐渐暴露出了一些问题,如扩展性差、维护复杂、部署不灵活等。为了解决这些问题,微服务架构应运而生。微服务是一种将应用程序分解为一组小的、自治的服务的架构模式,服务之间通过轻量级的通信协议(如HTTP)进行交互。Sp…...
Rider + xmake DX12 开发环境
Rider xmake DX12 开发环境 背景 如题,想要接近 UE 的开发流程 正文 大的流程就是 xmake 生成 vs 的 sln,用 Rider 进行开发 intellisense,断点调试 加了个脚本手动刷新 sln xmake project -k vsxmake -m "debug;release" -…...
控制台java原生工具打包jar文件
1、进入java源代码所在路径,或者包起始文件的所在路径 2、编译为class文件 我没配全局变量,这里使用jdk的完整路径来调用 3、jar命令进行打包 -cfe后面: svnHook.jar 指定jar包文件名 Request 包名入口类名,如果有包含包的话,应…...
MySQL主从同步
MySQL主从同步 作用 减少单台服务器的压力,防止单点故障 部署 主库 编辑/etc/mysql/mysql.conf.d/mysqld.cnf log_binmysql-bin server-id1 #服务器的id,再主从数据库里不能重复重启MySQL服务器 systemctl restart mysql连接mysql,并创建用于主从…...
ansible 学习之变量
参考文档: http://www.ansible.com.cn/docs/playbooks_variables.html#variables 合法的变量 ansible变量是有数字,字母,下划线组成并且变量始终应该以字母开头。 “foo_port”是个合法的变量名.”foo5”也是. “foo-port”, “foo port”, …...
【知识科普】Markdown语法内容看这一篇就够了
文章目录 1. 标题2. 段落3. 字体4. 分隔线5. 删除线6. 列表7. 区块引用8. 代码11. HTML元素12. 特殊字符13. 数学公式14. 其他高级技巧 Markdown是一种轻量级标记语言,其排版语法简洁,让人们能更多地关注内容本身而非排版。以下是对Markdown语法的详细解…...
什么是智能合约?
什么是智能合约? 智能合约,就是一段写在区块链上的代码,一旦某个事件触发合约中的条款,代码即自动执行。也就是说,满足条件就执行,不需要人为操控、不需要第三方信任。区块链的安全性和不可篡改性…...
Oracle低代码平台apex介绍
Oracle APEX(Application Express)是一个强大的低代码开发平台,它允许开发者快速构建企业级Web应用程序。该平台基于Oracle数据库,并充分利用了数据库的功能来提供安全、可扩展且易于维护的应用程序。 什么是Oracle APEX…...
【读书笔记·VLSI电路设计方法解密】问题12:制造MOSFET晶体管的主要工艺步骤是什么
VLSI芯片是在半导体材料上制造的,这种材料的导电性介于绝缘体和导体之间。通过一种称为掺杂的工艺引入杂质,可以改变半导体的电气特性。能够在半导体材料的细小且定义明确的区域内控制导电性,促使了半导体器件的发展。结合更简单的无源元件(电阻、电容和电感),这些器件被…...
内存分析工具的使用——AddressSanitizer
一、c/c中的内存问题 memory corruption,内存崩溃或者说内存损坏。在c/c程序中,有相当一部分的Bug是由内存引起的,也就是刚刚提到的内存崩溃。说得再通俗一些,往往和内存的非法访问有关。内存问题,轻则导致程序失能&a…...
linux使用nmcli 管理wifi的命令
在 Linux 系统中,nmcli 是 NetworkManager 的命令行工具,常用于管理网络连接,包括 WiFi。下面是一些常见的使用 nmcli 管理 WiFi 的命令。 1. 显示所有可用的 WiFi 网络 nmcli dev wifi list这个命令会列出当前可以扫描到的 WiFi 网络及其信…...
deepin20.9安装部署 |deepin20.9镜像下载 |基本命令 |手动分区 |开启远程ssh服务
下载deepin20.9 .iso 阿里云 https://mirrors.aliyun.com/deepin-cd/20.9/deepin-desktop-community-20.9-amd64.iso 注意安装过程略 小白参考 : Centos 7.9 安装 图解版 小白必看 最新_centos7.9-CSDN博客文章浏览阅读2.4k次,点赞34次,…...
使用PL/SQL Deverloper过程遇见的问题
目录 背景: ORA-01031权限问题: PL/SQL Deverloper显示Oravle中存在的所有表: PL/SQL Deverloper优点: 背景: PL/SQL Developer是由Allround Automations公司开发的一款集成开发环境(IDE),它专门面向Oracle数据库存储的程序单元的开发。随着越来越多…...
pikachu靶场总结(三)
五、RCE 1.RCE(remote command/code execute)概述 RCE漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。 远程系统命令执行 一般出现这种漏洞,是因为应用系统从设计上需要给用户提供指定的远程命令操作的…...
onvif相关的http api有哪些功能点
ONVIF 提供了一系列 HTTP API,用于访问和控制支持 ONVIF 的设备。这些 API 基于 SOAP 协议,通过 HTTP 协议传输。主要的 API 分为几个关键的服务类别,每个类别都有特定的操作。以下是 ONVIF 相关的 HTTP API 概述: 1. 设备管理服…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
webpack面试题
面试题:webpack介绍和简单使用 一、webpack(模块化打包工具)1. webpack是把项目当作一个整体,通过给定的一个主文件,webpack将从这个主文件开始找到你项目当中的所有依赖文件,使用loaders来处理它们&#x…...
链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...
云原生时代的系统设计:架构转型的战略支点
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、云原生的崛起:技术趋势与现实需求的交汇 随着企业业务的互联网化、全球化、智能化持续加深,传统的 I…...
