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

快速排序的深入优化探讨

快排性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选key基本⼆分居中,那么快排的递归树就是颗均匀的满⼆叉树,性能最佳。但是实践中虽然不可能每次都是⼆分居中,但是性能也还是可控的。但是如果出现每次选到最小值/最大值,划分为0个和N-1的子问题时,时间复杂度为O(N^2),数组序列有序时就会出现这样的问题,但是当数组中有大量重复数据时,之前的快速排序方法就会比较慢,因此我们需要更进算法。

三路排序

三路划分算法思想讲解:
当面对有大量跟key相同的值时,三路划分的核心思想有点类似hoare的左右指针和lomuto的前后指针的结合。核心思想是把数组中的数据分为三段 [比key小的值]、[跟key相等的值] 、[比key大的值],所以叫做三路划分算法。结合下图,理解⼀下实现思想:

  1. key默认取left位置的值。
  2. left指向区间最左边,right指向区间最右边,cur指向left+1位置。
  3. cur遇到比key小的值后跟left位置交换,换到左边,left++cur++
  4. cur遇到比key大的值后跟right位置交换,换到右边,right--
  5. cur遇到跟key相等的值后,cur++
  6. 直到cur>right结束

在这里插入图片描述

#include<stdio.h>
#include<time.h>
#include<stdlib.h>void swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}void Print(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ",a[i]);}printf("\n");
}void QuickSort(int* a, int left,int right)
{if (left >= right)return;//随机选keyint randi = left + (rand() % (right - left + 1));swap(&a[left], &a[randi]);int begin = left;int end = right;int key = left;int cur = left + 1;while (cur <= right){if (a[cur] < a[key]){swap(&a[cur],&a[left]);cur++;left++;}else if (a[cur] > a[key]){swap(&a[cur], &a[right]);right--;}else if (a[cur] == a[key]){cur++;}}QuickSort(a,begin,left-1);QuickSort(a, right + 1, end);
}int* sortArray(int* nums, int numsSize, int* returnSize) 
{srand((unsigned int)time(NULL));QuickSort(nums, 0, numsSize - 1);*returnSize = numsSize;return nums;
}int main()
{int arr[] = {2,5,7,6,1,4,3,9,8};int n = sizeof(arr) / sizeof(arr[0]);Print(arr,n);int* tmp=sortArray(arr, n,&n);Print(tmp, n);return 0;
}

在这里插入图片描述

自省排序( introsort)

自省排序的思路就是进行自我侦测和反省,快排递归深度太深(sgi stl中使用的是深度为2倍排序元素数量的对数值)那就说明在这种数据序列下,选key出现了问题,性能在快速退化,那么就不要再进行快排分割递归了,改换为堆排序进行排序。

#include<stdio.h>
#include<time.h>
#include<stdlib.h>void Print(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ",a[i]);}printf("\n");
}void Swap(int* x, int* y)
{int tmp = *x;*x = *y;*y = tmp;
}
//向下调整算法
void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){//选出左右孩⼦中⼤的那⼀个if (child + 1 < n && a[child + 1] > a[child]){++child;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}
//堆排序
void HeapSort(int* a, int n)
{//建堆--向下调整建堆-- O(N)for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[end], &a[0]);AdjustDown(a, end, 0);--end;}
}
//插入排序
void InsertSort(int* a, int n)
{for (int i = 1; i < n; i++){int end = i - 1;int tmp = a[i];//将tmp插⼊到[0, end]区间中,保持有序while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];--end;}else{break;}}a[end + 1] = tmp;}
}void IntroSort(int* a, int left, int right, int depth, int defaultDepth)
{if (left >= right)return;//数组⻓度⼩于16的小数组,换为插入排序,简单递归次数if (right - left + 1 < 16){InsertSort(a + left, right - left + 1);return;}//当深度超过2 * logN时改用堆排序if (depth > defaultDepth){HeapSort(a + left, right - left + 1);return;}depth++;int begin = left;int end = right;int randi = left + (rand() % (right - left + 1));Swap(&a[left], &a[randi]);int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[prev], &a[cur]);}++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;IntroSort(a, begin, keyi - 1, depth, defaultDepth);IntroSort(a, keyi + 1, end, depth, defaultDepth);
}void QuickSort(int* a, int left, int right)
{int logn = 0;int depth = 0;int N = right - left + 1;for (int i = 1; i < N; i *= 2){logn++;}IntroSort(a, left, right, depth, logn * 2);
}int* sortArray(int* nums, int numsSize, int* returnSize) 
{srand((unsigned int)time(NULL));QuickSort(nums, 0, numsSize - 1);*returnSize = numsSize;return nums;
}int main()
{int arr[] = {2,5,7,6,1,4,3,9,8};int n = sizeof(arr) / sizeof(arr[0]);Print(arr,n);int* tmp=sortArray(arr, n,&n);Print(tmp, n);return 0;
}

在这里插入图片描述

相关文章:

快速排序的深入优化探讨

快排性能的关键点分析 决定快排性能的关键点是每次单趟排序后&#xff0c;key对数组的分割&#xff0c;如果每次选key基本⼆分居中&#xff0c;那么快排的递归树就是颗均匀的满⼆叉树&#xff0c;性能最佳。但是实践中虽然不可能每次都是⼆分居中&#xff0c;但是性能也还是可…...

c语言杂谈系列:模拟虚函数

从整体来看&#xff0c;笔者的做法与之前的模拟多态十分相似&#xff0c;毕竟c多态的实现与虚函数密切相关 废话少说&#xff0c;see my code&#xff1a; kernel.c#include "kernel.h" #include <stdio.h>void shape_draw(struct shape_t* obj) {/* Call dr…...

短视频推广App不再难!Xinstall来帮忙

在短视频风靡的今天&#xff0c;如何利用这一热门媒介有效推广App&#xff0c;成为了许多推广者关注的焦点。而Xinstall&#xff0c;作为国内专业的App全渠道统计服务商&#xff0c;正是你解决这一难题的得力助手。 首先&#xff0c;Xinstall在数据维度上的优势无可比拟。它能…...

打靶记录13——doubletrouble

靶机&#xff1a; https://www.vulnhub.com/entry/doubletrouble-1,743/ 难度&#xff1a; 中 目标&#xff1a; 取得两台靶机 root 权限 涉及攻击方法&#xff1a; 主机发现端口扫描Web信息收集开源CMS漏洞利用隐写术密码爆破GTFObins提权SQL盲注脏牛提权 学习记录&am…...

awk文本处理工具

awk 是一个强大的文本处理工具&#xff0c;在Shell编程中常用于处理和分析文本数据。它可以按列处理数据&#xff0c;进行模式匹配&#xff0c;生成报告&#xff0c;执行计算等。以下是一些 awk 的主要功能和使用场景&#xff1a; 期待您的关注 美好的观念较美人尤为可爱 目录 …...

计算机毕业设计选题推荐-学院网站系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...

Spring模块详解Ⅰ

目录 SpringSpring框架的主要功能模块1. Core Container&#xff08;核心容器&#xff09;2. Data Access/Integration&#xff08;数据访问与集成&#xff09;3. Web4. AOP (Aspect-Oriented Programming&#xff0c;面向切面编程)5. Instrumentation&#xff08;工具集&#…...

C语言程序设计-练习篇

山海自有归期&#xff0c;风雨自有相逢。 一 下面代码的结果是什么&#xff1f; int main() { int i 0; for (i 0; i < 10; i) { if (i 5) //此处为赋值&#xff0c;i 5表达式结果为5 printf("%d ", i); //表达式为真&a…...

【Oracle篇】统计信息和动态采样的深度剖析(第一篇,总共六篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…...

无源互调自动化测试软件应用案例分享:S参数和互调的高效测试

随着产品种类的丰富和市场需求的变化&#xff0c;合肥某电子技术公司意识到&#xff0c;传统的手工测试已无法满足公司持续发展的需要。于是&#xff0c;一场自动化测试转型悄然展开。 一、背景介绍 合肥某电子技术公司成立于2009年&#xff0c;专注于功分器、耦合器、负载器、…...

【6大设计原则】精通设计模式之里氏代换原则:从理论到实践,掌握代码演化的黄金法则

一、引言 1.1 设计模式的必要性 在软件开发的复杂性面前&#xff0c;设计模式提供了一套成熟的解决方案&#xff0c;它们是经过多年实践总结出来的&#xff0c;能够帮助我们应对各种编程难题。设计模式不仅仅是一种编程技巧&#xff0c;更是一种编程哲学&#xff0c;它能够提…...

国内服务器安装Docker提示Failed to connect to download.docker.com port 443的解决方案

解决方案 换国内镜像源。我用的是清华的。https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ 自己找自己对应的版本。 例如你的Ubuntu系统。就用下列命令 sudo curl -fsSL https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu/gpg -o /etc/apt/keyrings/do…...

前端开发攻略---彻底弄懂跨域解决方案

目录 1、浏览器的同源策略 1.1 源 1.2 同源与非同源 1.3 同源请求与非同源请求 2、跨域受到的限制 3、注意点 4、CORS解决Ajax跨域问题 4.1 CORS概述 4.2 CORS解决简单请求跨域 4.3 简单请求与复杂请求 4.4 CORS解决复杂请求跨域 4.5 借助CORS库快速完成配置 5、JS…...

【HeadFirst 设计模式】装饰者模式的C++实现

一、案例背景 Starbuzz是以扩张速度最快而闻名的咖啡连锁店。如果你在街角看到它的店&#xff0c;在对面街上肯定还会看到另一家。因为扩张速度实在太快了&#xff0c;他们准备更新订单系统&#xff0c;以合乎他们的饮料供应要求。他们原先的类设计是这样的…… 购买咖啡时&am…...

大白话解释TCP的三次握手和四次挥手

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏和关注。个人知乎 TCP的三次握手是浏览器与服务器建立连接的过程&#xff0c;而四次挥手&#xff0c;是两者断开连接的过程。今天把客户端和服务端当做两个人&#xff0c;通过打电话的方式解释连接建立和断开的过程。 TCP…...

asyncua模块实现OPC UA通讯

asyncua是OPCUA的python实现&#xff0c;使用起来非常方便&#xff0c;其github地址是https://github.com/FreeOpcUa/opcua-asyncio UaExpert是OPC UA Client的GUI工具&#xff0c;当编写好server代码后并运行&#xff0c;我们可以使用UaExpert去和server进行通信。UaExpert使…...

RabbitMQ的核心概念

RabbitMQ是一个消息中间件&#xff0c;也是一个生产者消费者模型&#xff0c;负责接收&#xff0c;存储和转发消息。 核心概念 Producer 生产者&#xff0c;是RabbitMQ Server的客户端&#xff0c;向RabbitMQ发送消息。 Consumer 消费者&#xff0c;是RabbitMQ Server的客…...

【vSphere 7/8】深入浅出 vSphere 证书 Ⅰ—— 初识和了解 vSphere证书

目录 摘要1. vSphere 安全证书1.1 vSphere 安全证书的类型和有效期 2. 在 vSphere Client 中初识 vSphere 证书2.1 vCenter 8.0.3 的 vSphere Client 界面2.2 vCenter Server 7.0 Update2 到 vCenter Server 8.0 Update 2 的 vSphere Client 界面2.3 vCenter Server 7.0 到 vCe…...

【云备份】服务端模块-热点管理

文章目录 0.回顾extern1.介绍2.实现思想3.代码测试代码 热点管理总结 0.回顾extern extern cloudBackup::DataManager *_dataManager extern 关键字用于声明一个全局变量或对象&#xff0c;而不定义它。这意味着 _dataManager 是一个指向 cloudBackup::DataManager 类型的指针…...

call apply bind特性及手动实现

call // 原生的call var foo { value: 1 };function bar(...args) {console.log("this", this.value, args); }bar.call(foo)// call 改变了bar的this指向 // bar函数执行了 // 等价于 // var foo { // name: "tengzhu", // sex: "man", …...

pygame开发课程系列(5): 游戏逻辑

第五章 游戏逻辑 在本章中&#xff0c;我们将探讨游戏开发中的核心逻辑&#xff0c;包括碰撞检测、分数系统和游戏状态管理。这些元素不仅是游戏功能的关键&#xff0c;还能显著提升游戏的趣味性和挑战性。 5.1 碰撞检测 碰撞检测是游戏开发中的一个重要方面&#xff0c;它用…...

嵌入式系统实时任务调度算法优化与实现

嵌入式系统实时任务调度算法优化与实现 目录 嵌入式系统实时任务调度算法优化与实现 引言 1.1 嵌入式系统的重要性 1.2 实时任务调度的重要性 实时任务的定义与分类 2.1 实时任务的定义 2.2 实时任务的分类 2.3 实时任务的其他分类方法 硬实时与软实时系统 3.1 硬实…...

Java:枚举转换

在Java中&#xff0c;你可以使用Enum.valueOf()方法将字符串转换为枚举常量。但是&#xff0c;如果你想要将枚举转换为其他类型&#xff0c;你需要自定义转换方法。以下是一个简单的例子&#xff0c;演示如何将枚举转换为整数&#xff1a; public enum Color {RED(1), GREEN(2…...

Vue、react父子组件生命周期

Vue 的父子组件生命周期 以下分为三部分&#xff0c;加载渲染阶段——更新阶段——销毁阶段&#xff0c;我们来一一介绍&#xff1a; 1、加载渲染阶段 在加载渲染阶段&#xff0c;一定得等子组件挂载完毕后&#xff0c;父组件才能挂载完毕&#xff0c;所以父组件的 mounted 在…...

HTML 基础要素解析

目录 HTML 初步认识 纯文本文件介绍 纯文本文件与其它文件的区别 Html介绍 HTML 骨架 文档类型&#xff08;!DOCTYPE&#xff09;声明 介绍 常用的 DOCTYPE 声明 meta标签 字符集 关键字和页面描述 HTML 初步认识 纯文本文件介绍 纯文本文件指的是仅包含文本内容&am…...

开源的向量数据库Milvus

Milvus是一款开源的向量数据库&#xff0c;专为处理向量搜索任务而设计&#xff0c;尤其擅长处理大规模向量数据的相似度检索。 官网地址&#xff1a;https://milvus.io/ 以下是关于Milvus的详细介绍&#xff1a; 一、基本概念 向量数据库&#xff1a;Milvus是一款云原生向量…...

设计模式-工厂方法

“对象创建”模式 通过“对象创建”模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&#xff0c;从而支持对象创建的稳定。它是接口抽象之后的第一步工作。典型模式 Factory MethodAbstract Factory…...

Flask SQLALchemy 的使用

Flask SQLALchemy 的使用 安装 Flask-SQLAlchemy配置 Flask-SQLAlchemy定义模型创建数据库和表插入和查询数据更新和删除数据迁移数据库总结Flask-SQLAlchemy 是一个 Flask 扩展,它简化了 Flask 应用中 SQLAlchemy 的使用。SQLAlchemy 是一个强大的 SQL 工具包和对象关系映射(…...

Metasploit漏洞利用系列(一):MSF完美升级及目录结构深度解读

在信息安全领域&#xff0c;MetasploitFramework&#xff08;MSF&#xff09;是一个无处不在的工具&#xff0c;它集合了大量的渗透测试和漏洞利用模块&#xff0c;帮助安全专家识别和利用系统中的弱点。本文将深入探讨如何对Metasploit进行完美升级&#xff0c;以及对其核心目…...

C/C++|经典代码题(动态资源的双重释放与「按值传递、按引用传递、智能指针的使用」)

以下代码中你能看出其存在什么问题&#xff1f;如何修复&#xff0c;能给出几种方法&#xff1f;分别在什么场景下用哪种方法。 #include <iostream>class Buffer {public:Buffer() { std::cout << "Buffer created" << std::endl; }~Buffer() { s…...