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

刷题 排序算法

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) 复杂度的算法都会超过时间限制&#xff0c;只有 O(nlogn) 的可以通过 快速排序空间复杂度为 O(logn)是由于递归的栈的调用归并排序空间复杂度为 O(n) 是由于需要一个临时数组 (当然也需要栈的调用&#xff0c;但是 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 研究背景 如今互联网高速发展&#xff0c;网络遍布全球&#xff0c;通过互联网发布的消息能快而方便的传播到世界每个角落&#xff0c;并且互联网上能传播的信息也很广&#xff0c;比如文字、图片、声音、视频等。从而&#xff0c;这种种好处使得互联网成了信息传…...

docker tar包安装 docker-26.1.4.tgz

一、docker安装 1.先将docker安装包&#xff08;docker-26.1.4.tgz&#xff09;拷贝到DM系统中。 下载地址 Index of linux/static/stable/x86_64/ 1.先将docker安装包&#xff08;docker-26.1.4.tgz&#xff09;拷贝到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 开发环境 背景 如题&#xff0c;想要接近 UE 的开发流程 正文 大的流程就是 xmake 生成 vs 的 sln&#xff0c;用 Rider 进行开发 intellisense&#xff0c;断点调试 加了个脚本手动刷新 sln xmake project -k vsxmake -m "debug;release" -…...

控制台java原生工具打包jar文件

1、进入java源代码所在路径&#xff0c;或者包起始文件的所在路径 2、编译为class文件 我没配全局变量&#xff0c;这里使用jdk的完整路径来调用 3、jar命令进行打包 -cfe后面: svnHook.jar 指定jar包文件名 Request 包名入口类名&#xff0c;如果有包含包的话&#xff0c;应…...

MySQL主从同步

MySQL主从同步 作用 减少单台服务器的压力&#xff0c;防止单点故障 部署 主库 编辑/etc/mysql/mysql.conf.d/mysqld.cnf log_binmysql-bin server-id1 #服务器的id,再主从数据库里不能重复重启MySQL服务器 systemctl restart mysql连接mysql&#xff0c;并创建用于主从…...

ansible 学习之变量

参考文档&#xff1a; http://www.ansible.com.cn/docs/playbooks_variables.html#variables 合法的变量 ansible变量是有数字&#xff0c;字母&#xff0c;下划线组成并且变量始终应该以字母开头。 “foo_port”是个合法的变量名.”foo5”也是. “foo-port”, “foo port”, …...

【知识科普】Markdown语法内容看这一篇就够了

文章目录 1. 标题2. 段落3. 字体4. 分隔线5. 删除线6. 列表7. 区块引用8. 代码11. HTML元素12. 特殊字符13. 数学公式14. 其他高级技巧 Markdown是一种轻量级标记语言&#xff0c;其排版语法简洁&#xff0c;让人们能更多地关注内容本身而非排版。以下是对Markdown语法的详细解…...

什么是智能合约?

什么是智能合约&#xff1f; 智能合约&#xff0c;就是一段写在区块链上的代码&#xff0c;一旦某个事件触发合约中的条款&#xff0c;代码即自动执行。也就是说&#xff0c;满足条件就执行&#xff0c;不需要人为操控、不需要第三方信任。区块链的安全性和不可篡改性&#xf…...

Oracle低代码平台apex介绍

Oracle APEX&#xff08;Application Express&#xff09;是一个强大的低代码开发平台&#xff0c;它允许开发者快速构建企业级Web应用程序。该平台基于Oracle数据库&#xff0c;并充分利用了数据库的功能来提供安全、可扩展且易于维护的应用程序。 什么是Oracle APEX&#xf…...

【读书笔记·VLSI电路设计方法解密】问题12:制造MOSFET晶体管的主要工艺步骤是什么

VLSI芯片是在半导体材料上制造的,这种材料的导电性介于绝缘体和导体之间。通过一种称为掺杂的工艺引入杂质,可以改变半导体的电气特性。能够在半导体材料的细小且定义明确的区域内控制导电性,促使了半导体器件的发展。结合更简单的无源元件(电阻、电容和电感),这些器件被…...

内存分析工具的使用——AddressSanitizer

一、c/c中的内存问题 memory corruption&#xff0c;内存崩溃或者说内存损坏。在c/c程序中&#xff0c;有相当一部分的Bug是由内存引起的&#xff0c;也就是刚刚提到的内存崩溃。说得再通俗一些&#xff0c;往往和内存的非法访问有关。内存问题&#xff0c;轻则导致程序失能&a…...

linux使用nmcli 管理wifi的命令

在 Linux 系统中&#xff0c;nmcli 是 NetworkManager 的命令行工具&#xff0c;常用于管理网络连接&#xff0c;包括 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 注意安装过程略 小白参考 &#xff1a; Centos 7.9 安装 图解版 小白必看 最新_centos7.9-CSDN博客文章浏览阅读2.4k次&#xff0c;点赞34次&#xff0c…...

使用PL/SQL Deverloper过程遇见的问题

目录 背景: ORA-01031权限问题&#xff1a; PL/SQL Deverloper显示Oravle中存在的所有表&#xff1a; PL/SQL Deverloper优点: 背景: PL/SQL Developer是由Allround Automations公司开发的一款集成开发环境(IDE),它专门面向Oracle数据库存储的程序单元的开发。随着越来越多…...

pikachu靶场总结(三)

五、RCE 1.RCE(remote command/code execute)概述 RCE漏洞&#xff0c;可以让攻击者直接向后台服务器远程注入操作系统命令或者代码&#xff0c;从而控制后台系统。 远程系统命令执行 一般出现这种漏洞&#xff0c;是因为应用系统从设计上需要给用户提供指定的远程命令操作的…...

onvif相关的http api有哪些功能点

ONVIF 提供了一系列 HTTP API&#xff0c;用于访问和控制支持 ONVIF 的设备。这些 API 基于 SOAP 协议&#xff0c;通过 HTTP 协议传输。主要的 API 分为几个关键的服务类别&#xff0c;每个类别都有特定的操作。以下是 ONVIF 相关的 HTTP API 概述&#xff1a; 1. 设备管理服…...

3分钟上手PCL2-CE:打造专属Minecraft启动环境的完整指南

3分钟上手PCL2-CE&#xff1a;打造专属Minecraft启动环境的完整指南 PCL2-CE社区版是一款开源游戏配置工具&#xff0c;致力于为Minecraft玩家提供高效、灵活的游戏环境管理方案。通过智能化配置和模块化设计&#xff0c;让玩家告别繁琐设置&#xff0c;轻松掌控游戏入口&…...

最近在折腾语音端点检测的时候发现个有意思的方法——频带方差检测。这玩意儿特别适合对付环境噪声,原理简单粗暴但有效。今天咱们就手撕代码看看它怎么玩转语音段定位

基于matlab的频带方差端点检测&#xff0c;噪声频谱中&#xff0c;各频带之间变化很平缓&#xff0c;语音各频带之间变化较激烈。 据此特征&#xff0c;语音和噪声就极易区分。 计算短时频带方差&#xff0c;实质就是计算某一帧信号的各频带能量之间的方差。 这种以短时频带方差…...

OpenClaw技能开发入门:千问3.5-9B定制天气查询

OpenClaw技能开发入门&#xff1a;千问3.5-9B定制天气查询 1. 为什么需要自定义技能&#xff1f; 去年冬天&#xff0c;我经常需要同时查看多个城市的天气情况来安排出差行程。每次手动打开天气网站、输入城市名、截图保存的操作让我不胜其烦。直到发现OpenClaw支持自定义技能…...

高速ADC采样时钟不准?手把手教你理解时钟占空比校正(DCC)电路的核心原理

高速ADC采样时钟不准&#xff1f;手把手教你理解时钟占空比校正&#xff08;DCC&#xff09;电路的核心原理 当你在调试一块高速ADC板卡时&#xff0c;发现ENOB&#xff08;有效位数&#xff09;始终比规格书低2-3位&#xff0c;频谱分析显示谐波失真异常。这种困扰可能来自一…...

FGA智能自动化:重新定义Fate/Grand Order效率提升新范式

FGA智能自动化&#xff1a;重新定义Fate/Grand Order效率提升新范式 【免费下载链接】FGA Auto-battle app for F/GO Android 项目地址: https://gitcode.com/gh_mirrors/fg/FGA 在Fate/Grand Order的游戏世界中&#xff0c;90%的玩家每天都在重复着机械的刷本操作&…...

效率倍增:用快马生成openclaw在ubuntu的一键部署与docker化脚本

最近在折腾一个开源项目openclaw的部署&#xff0c;发现每次在Ubuntu服务器上手动安装配置特别费时间。作为一个懒人程序员&#xff0c;我决定研究下怎么把整个流程自动化&#xff0c;结果发现用InsCode(快马)平台可以轻松搞定这件事&#xff0c;效率直接翻倍。 传统部署方式的…...

Docker测试学习思路

Docker 核心概念学习与实战指南本文系统梳理 Docker 学习的核心思路与方法&#xff0c;用通俗类比帮助理解 Docker 的本质&#xff0c;涵盖镜像构建、容器运行、网络通信、数据持久化、资源限制五大核心能力&#xff0c;适合初学者建立清晰的 Docker 知识框架。一、Docker 到底…...

AQ智商测试

AQ逆商测试结果分析&#xff08;PSYTOPIC版&#xff09; Psytopic分析&#xff1a;您的AQ得分是 168 &#xff0c;在人群中属较高水平 。 以下是PSYTOPIC为您提供的分析参考&#xff1a; 你能面对现实&#xff0c;对来自工作和生活中的困难应对自如&#xff0c;并敢于迎接逆境…...

手把手改造Ruoyi-vue-plus权限体系:给多租户增加动态数据权限控制

深度定制Ruoyi-vue-plus多租户数据权限&#xff1a;从架构设计到前端适配全解析 在当今企业级应用开发中&#xff0c;多租户系统已成为SaaS服务的标配&#xff0c;而数据权限控制则是确保租户间数据隔离的核心机制。Ruoyi-vue-plus作为国内流行的快速开发框架&#xff0c;其原生…...

告别黑屏和错位!Uniapp视频轮播最佳实践:巧用v-if与swiper事件实现无缝切换

Uniapp视频轮播组件深度优化&#xff1a;从黑屏错位到无缝体验的全链路解决方案 在移动应用开发中&#xff0c;视频轮播组件已经成为提升用户参与度的关键元素。然而&#xff0c;当Uniapp开发者尝试在swiper组件中嵌入视频时&#xff0c;常常会遇到视频位置偏移、黑屏闪现、自动…...