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

【排序算法】—— 快速排序

        快速排序的原理是交换排序,其中qsort函数用的排序原理就是快速排序,它是一种效率较高的不稳定函数,时间复杂度为O(N*longN),接下来就来学习一下快速排序。

一、快速排序思路

1.整体思路

以升序排序为例:

        (1)、首先随便找一个数(一般取首元素)作为排序对象(记为key),先将这个数排到准确的位置,即把小于等于key的全部放在key左边,大于key的全部放在key右边。这是排一趟需要做的事。

        (2)、排完一趟后被排的那个数key此时它在的位置是准确的,接下来只需要用同样的方法分别对它的左数组和右数组排序,这就有点类似于二叉树的前序遍历

如下:

void QuickSort(int* arr, int left, int right)
{if (left >= right)//如果左区间>=右区间就不用再排,直接返回。return;int key=_Sort3(arr, left, right);//该函数用来排单趟QuickSort(arr, left, key - 1);QuickSort(arr, key + 1, right);
}

        至于排单趟的算法一般有三种分别是:霍尔法,挖坑法,前后指针法。在下面会细讲。 

2.单趟排序

2.1.霍尔法

        把第一个元素当排序对象key(首元素的下标),用L储存左下标,R储存右下标。先让R从右往左走找到比key小的元素时停下,然后让L从左往右走找到比key大的元素停下。然后把位于L位置和位于R位置的的值进行交换,最后重复上面的操作直到L>=R为止。再把key的位置与L位置的值交换。至此一趟排序就结束了。

代码示例:

int _Sort1(int* arr, int begin, int end)//霍尔法
{int key = begin;while (begin < end){while (begin < end && arr[end] > arr[key])end--;while (begin < end && arr[begin] <= arr[key])begin++;Swap(&arr[begin], &arr[end]);}Swap(&arr[key], &arr[begin]);key = begin;return key;
}

2.1.挖坑法

          把首元素当做排序对象赋值给key,然后把第一个下标L当做坑位,让R从右边出发找到比key小的数放在坑位,然后R的位置变成坑位,让L从左往右找到比key大的数放在坑位,L位置变成坑位,再次让R走,循环往复直到L与R相遇。最后把key放在坑位,至此一趟排序结束。

 代码示例:

int _Sort2(int* arr, int begin, int end)
{int key = arr[begin];int kw = begin;//坑位下标while (begin < end){while (begin < end && arr[begin] < key)begin++;arr[kw] = arr[begin];kw = begin;while (begin < end && arr[end] >= key)end--;arr[kw] = arr[end];kw = end;}arr[kw] = key;return kw;
}

2.3.前后指针法

         把第一个元素当排序对象key(首元素的下标),prev指向首元素下标,cur指向首元素下一个元素下标。

        如果cur指向的元素小于key的话prev向右走,并且如果prev不等于cur,那么把prev与cur互换。最后cur都需要无条件向右走一步。

代码示例:

int _Sort3(int* arr, int begin, int end)//前后指针法
{int perv = begin, cur = begin + 1;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[perv], &arr[cur]);cur++;}Swap(&arr[perv], &arr[begin]);return perv;
}

二、提供效率的方法

1.三数取中(key值的选取)

        快速排序的"三数取中"(Median of Three)是一种优化技术,它相当于选排序对象key。其具体做法如下:

        (1).选择三个元素:从待排序数组中选择三个关键元素,通常是第一个元素、中间元素和最后一个元素。
        (2).比较并选择中位数:将这三个元素进行比较,选择值不是最大也不是最小的数。比如,如果三个元素分别是arr[left]、arr[mid]、arr[right],则中位数是介于这三个值之间的那个数。
        (3).交换为第一个元素:将选定的中位数元素与数组的第一个元素交换位置,以便在后续的快速排序中使用。

        通过使用"三数取中"的方法,可以有效地减少快速排序中最坏情况的发生概率,因为选取的主元更有可能接近数组的中值,而不是极端值。这样可以更均匀地划分数组,减少排序的平均时间复杂度,提高算法的整体性能。

2.小区间优化

        小区间优化指的是在快速排序算法中针对较小规模的子数组(或子问题)采用其他排序方法,而不是继续使用快速排序本身。这种优化技术的目的是避免在小规模问题上快速排序的递归调用带来的额外开销,提高算法的整体效率。
        具体来说,当快速排序的递归过程划分的子数组规模减小到一定程度时,可以选择使用插入排序或其他适合小规模数组的排序方法来处理。这样可以避免递归深度过深,减少递归调用和分区操作的开销,从而提高整体排序算法的性能。
具体的优化策略包括:

        (1).设定阈值:在实现快速排序时设定一个阈值,当子数组的大小小于这个阈值时,转而使用插入排序或者直接选择排序等方法进行排序。常见的阈值一般设定在5到10之间,具体取决于具体实现和排序数据的特性。
        (2).切换到其他排序算法:在快速排序的递归过程中,当子数组大小小于设定的阈值时,停止快速排序的递归,转而使用其他排序算法完成剩余的排序工作。

        小区间优化技术能有效降低快速排序在小规模数据上的不必要开销,提高整体排序算法的效率和性能。

三、非递归实现

        把递归该为非递归毋庸置疑首先考虑使用栈结构,这里我们需要抓住重点,考虑需要用栈结构储存什么数据,先从函数参数上看左右区间的参数是一直在变化的,那么只需要把区间储存到栈中,每次从栈中就可以取到需要排序的区间,对这个区间进行一趟排序后再分为两个小区间存入栈中,重复以上操作直到栈为空。

四、源码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include"Stack.h"
#define size 100
void Swap(int* a, int* b)
{int c = *a;*a = *b;*b = c;
}
void InsertSort(int* arr, int sz)//插入排序(小区间优化)
{for (int i = 0; i < sz - 1; i++){int j = i;int tmp = arr[j + 1];while (j >= 0 && tmp < arr[j]){arr[j + 1] = arr[j--];}arr[j + 1] = tmp;}
}
int Sk(int* arr, int left, int right)//三数取中
{int v = (left + right) / 2;if (arr[left] > arr[right]){if (arr[right] > arr[v])return right;else{if (arr[left] < arr[v])return left;elsereturn v;}}else{if (arr[left] > arr[v])return left;else{if (arr[right] > arr[v])return right;elsereturn v;}}
}
int _Sort1(int* arr, int begin, int end)//霍尔法
{int key = begin;while (begin < end){while (begin < end && arr[end] > arr[key])end--;while (begin < end && arr[begin] <= arr[key])begin++;Swap(&arr[begin], &arr[end]);}Swap(&arr[key], &arr[begin]);key = begin;return key;
}
int _Sort2(int* arr, int begin, int end)//挖坑法
{int val = arr[begin];int kw = begin;//坑位while (begin < end){while (begin < end && arr[begin] < val)begin++;arr[kw] = arr[begin];kw = begin;while (begin < end && arr[end] >= val)end--;arr[kw] = arr[end];kw = end;}arr[kw] = val;return kw;
}
int _Sort3(int* arr, int begin, int end)//前后指针法
{int perv = begin, cur = begin + 1;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[perv], &arr[cur]);cur++;}Swap(&arr[perv], &arr[begin]);return perv;
}
void QuickSort(int* arr, int left, int right)//快速排序(递归实现)
{if (left >= right)return;if (right - left <= 10){InsertSort(arr + left, right - left + 1);return;}int key=_Sort1(arr, left, right);QuickSort(arr, left, key - 1);QuickSort(arr, key + 1, right);
}
void QuickSortNoR(int* arr, int left, int right)//快速排序(非递归)
{Stack st;StackInit(&st);//关于栈结构的函数实现在这里并未展示,已在Stack.h中声明StackPush(&st, right);StackPush(&st, left);while (!StackEmpty(&st)){int begin = StackTop(&st);StackPop(&st);int perv = begin, cur = begin + 1;int end = StackTop(&st);StackPop(&st);if (begin >= end)continue;while (cur <= end){if (arr[cur] < arr[begin] && ++perv != cur)Swap(&arr[cur], &arr[perv]);cur++;}Swap(&arr[begin], &arr[perv]);StackPush(&st, end), StackPush(&st, perv + 1);StackPush(&st, perv - 1), StackPush(&st, begin);}StackDestroy(&st);
}
int main()
{int arr[size] = { 0 };srand((unsigned int)time(NULL));//随机数种子for (int i = 0; i < size; i++){arr[i] = rand() % 1000 + 1;//生成随机数赋值给arr[i]}QuickSort(arr, 0, size - 1);for (int i = 0; i < size; i++){printf("%-3d ", arr[i]);if ((i + 1) % 20 == 0)printf("\n");}return 0;
}

相关文章:

【排序算法】—— 快速排序

快速排序的原理是交换排序&#xff0c;其中qsort函数用的排序原理就是快速排序&#xff0c;它是一种效率较高的不稳定函数&#xff0c;时间复杂度为O(N*longN)&#xff0c;接下来就来学习一下快速排序。 一、快速排序思路 1.整体思路 以升序排序为例&#xff1a; (1)、首先随…...

前端JS特效第22波:jQuery滑动手风琴内容切换特效

jQuery滑动手风琴内容切换特效&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xm…...

redis的数据类型对应的使用场景

Redis提供了多种数据类型&#xff0c;每种数据类型都有其特定的适用场景。以下是Redis主要数据类型及其典型应用场景&#xff1a;1. 字符串(String) 应用场景&#xff1a;适用于存储简单的键值对数据&#xff0c;如用户基本信息、计数器&#xff08;如网页访问次数&…...

ctfshow-web入门-命令执行(web118详解)Linux 内置变量与Bash切片

输入数字和小写字母&#xff0c;回显 evil input 查看源码&#xff0c;发现这里会将提交的参数 code 传给 system 函数 使用 burpsuite 抓包进行单个字符的模糊测试 fuzz&#xff1a; 发现过滤掉了数字和小写字母以及一些符号&#xff0c;下面框起来的部分是可用的 结合题目提…...

C语言 指针和数组——指针和二维数组之间的关系

目录 换个角度看二维数组 指向二维数组的行指针 按行指针访问二维数组元素 再换一个角度看二维数组 按列指针访问二维数组元素 二维数组作函数参数 指向二维数组的行指针作函数参数 指向二维数组的列指针作函数参数​编辑 用const保护你传给函数的数据 小结 换个角度看…...

问题集锦1

01.inner中使用JwtTokenUtil.getUserCode() 前端调用上传&#xff08;java&#xff09;&#xff0c;上传使用加购 Overridepublic Boolean insertShoppingCart(InsertShoppingCartParamsDto dto) {// 通过userCode,itemCode和supplierCode来判断当前加购人添加到购物车的商品是…...

浅析MySQL-索引篇01

什么是索引&#xff1f; 索引是帮助存储引擎快速获取数据的一种数据结构&#xff0c;类似于数据的目录。 索引的分类 按数据结构分类&#xff1a; MySQL 常见索引有 BTree 索引、HASH 索引、Full-Text 索引。 Innodb是MySQL5.5之后的默认存储引擎&#xff0c;BTree索引类型也…...

2028年企业云存储支出翻倍,达到1280亿美元

根据Omdia的研究&#xff0c;到2028年&#xff0c;企业云存储支出将从去年的570亿美元翻一番以上&#xff0c;达到1280亿美元。该研究分析了基础设施即服务&#xff08;IaaS&#xff09;和平台即服务&#xff08;PaaS&#xff09;数据中心的收入&#xff0c;作为年度存储数据服…...

ActiViz中的颜色映射表vtkLookupTable

文章目录 一、简介二、VtkLookupTable的创建与初始化三、设置数据范围四、颜色映射设置五、不透明度设置六、自定义颜色映射七、 不连续性颜色映射八、 预设颜色映射方案九、可视化效果优化十、与其他VTK组件的整合十一、 动态调整映射表十二、保存和加载颜色映射表一、简介 V…...

【Spring AOP 源码解析前篇】什么是 AOP | 通知类型 | 切点表达式| AOP 如何使用

前言&#xff08;关于源码航行&#xff09; 在准备面试和学习的过程中&#xff0c;我阅读了还算多的源码&#xff0c;比如 JUC、Spring、MyBatis&#xff0c;收获了很多代码的设计思想&#xff0c;也对平时调用的 API 有了更深入的理解&#xff1b;但过多散乱的笔记给我的整理…...

Laravel HTTP客户端:网络请求的瑞士军刀

标题&#xff1a;Laravel HTTP客户端&#xff1a;网络请求的瑞士军刀 Laravel的HTTP客户端是一个功能强大的工具&#xff0c;它提供了一种简洁、直观的方式来发送HTTP请求。无论是与外部API集成&#xff0c;还是进行网络数据抓取&#xff0c;Laravel的HTTP客户端都能满足你的需…...

7月07日,每日信息差

第一、6 月份&#xff0c;北京、上海、广州和深圳的新建商品住宅成交量分别环比增加 21%、66%、48% 和 38%&#xff0c;均创年内新高 第二、2024 年世界人工智能大会上&#xff0c;上海向四家企业发放了首批无驾驶人智能网联汽车示范应用许可&#xff0c;这些企业可以在浦东部…...

ubuntu 网络常用命令

在Ubuntu中&#xff0c;有许多网络相关的常用命令。以下是一些主要命令及其用途&#xff1a; ifconfig&#xff1a;此命令用于显示和配置网络接口信息。你可以使用它来查看IP地址、子网掩码、广播地址等。 例如&#xff1a;ifconfig 注意&#xff1a;在新版本的Linux发行版中…...

Python28-7.4 独立成分分析ICA分离混合音频

独立成分分析&#xff08;Independent Component Analysis&#xff0c;ICA&#xff09;是一种统计与计算技术&#xff0c;主要用于信号分离&#xff0c;即从多种混合信号中提取出独立的信号源。ICA在处理盲源分离&#xff08;Blind Source Separation&#xff0c;BSS&#xff0…...

Spring Boot与Okta的集成

Spring Boot与Okta的集成 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何在Spring Boot应用中集成Okta&#xff0c;实现身份认证和授权的功能…...

MVC(Model-View-Controller)模式

MVC&#xff08;Model-View-Controller&#xff09;模式三个主要组件&#xff1a;模型&#xff08;Model&#xff09;&#xff0c;视图&#xff08;View&#xff09;&#xff0c;和控制器&#xff08;Controller&#xff09;&#xff1a; 模型&#xff08;Model&#xff09;&a…...

MuLan:模仿人类画家的多对象图像生成

在图像生成领域&#xff0c;处理包含多个对象及其空间关系、相对大小、重叠和属性绑定的复杂提示时&#xff0c;现有的文本到图像模型仍面临挑战&#xff1a;当文本提示中包含多个对象&#xff0c;并且这些对象之间存在特定的空间关系时&#xff0c;现有模型往往难以准确地捕捉…...

如何在Android中实现网络通信,如HttpURLConnection和HttpClient。

在Android开发中&#xff0c;网络通信是一个不可或缺的功能&#xff0c;它允许应用与服务器交换数据&#xff0c;实现丰富的功能。在实现网络通信时&#xff0c;HttpURLConnection和HttpClient是两种常用的方式。下面将从技术难点、面试官关注点、回答吸引力以及代码举例四个方…...

评价ChatGPT与强人工智能的未来

在人工智能领域&#xff0c;ChatGPT的出现无疑是一个里程碑事件。它不仅展示了自然语言处理技术的巨大进步&#xff0c;也引发了人们对于强人工智能&#xff08;AGI&#xff09;的无限遐想。本文将从多个角度评价ChatGPT&#xff0c;并探讨强人工智能距离我们还有多远。 ChatGP…...

【web前端HTML+CSS+JS】--- CSS学习笔记02

一、CSS&#xff08;层叠样式表&#xff09;介绍 1.优势 2.定义解释 如果有多个选择器共同作用的话&#xff0c;只有优先级最高那层样式决定最终的效果 二、无语义化标签 div和span&#xff1a;只起到描述的作用&#xff0c;不带任何样式 三、标签选择器 1.标签/元素选择器…...

linux 安装 ImageMagick 及 php imagick扩展

安装imagick扩展前必须安装ImageMagick 一、安装ImageMagick wget http://www.imagemagick.org/download/ImageMagick.tar.gz 上面如果报错&#xff08;cannot verify download.imagemagick.org’s certificate&#xff09;执行 sudo yum install -y ca-certificates tar zxv…...

秋招突击——7/5——复习{}——新作{跳跃游戏II、划分字母区间、数组中的第K个大的元素(模板题,重要)、前K个高频元素}

文章目录 引言正文贪心——45 跳跃游戏II个人实现参考实现 划分字母区间个人实现参考实现 数组中的第K个最大元素个人实现参考做法 前K个高频元素个人实现参考实现 总结 引言 今天就开始的蛮早的&#xff0c;现在是九点多&#xff0c;刚好开始做算法&#xff0c;今天有希望能够…...

【Linux】信号的处理

你很自由 充满了无限可能 这是很棒的事 我衷心祈祷你可以相信自己 无悔地燃烧自己的人生 -- 东野圭吾 《解忧杂货店》 信号的处理 1 信号的处理2 内核态 VS 用户态3 键盘输入数据的过程4 如何理解OS如何正常的运行5 如何进行信号捕捉信号处理的总结6 可重入函数volatile关…...

Python数据分析的数据导入和导出

在Python数据分析中&#xff0c;数据的导入和导出是非常关键的步骤。这些步骤通常涉及到将数据从外部文件&#xff08;如CSV、Excel、数据库等&#xff09;读入到Python程序中&#xff0c;以及将处理后的数据导出回外部文件或数据库。以下是一些常用的库和方法来实现这些操作。…...

【JAVA多线程】线程池概论

目录 1.概述 2.ThreadPoolExector 2.1.参数 2.2.新任务提交流程 2.3.拒绝策略 2.4.代码示例 1.概述 线程池的核心&#xff1a; 线程池的实现原理是个标准的生产消费者模型&#xff0c;调用方不停向线程池中写数据&#xff0c;线程池中的线程组不停从队列中取任务。 实现…...

java双亲委派机制

Java中的双亲委派机制&#xff08;Parent Delegation Model&#xff09;是一种类加载机制&#xff0c;它确保了类加载的安全性和一致性。该机制规定了类加载器在加载类时的顺序和方式&#xff0c;从而避免了重复加载和类冲突问题。 以下是一个简单的自定义类加载器的示例&#…...

记录第一次使用air热更新golang项目

下载 go install github.com/cosmtrek/airlatest 下载时提示&#xff1a; module declares its path as: github.com/air-verse/air but was required as: github.com/cosmtrek/air 此时&#xff0c;需要在go.mod中加上这么一句&#xff1a; replace github.com/cosmtrek/air &…...

Leetcode 3213. Construct String with Minimum Cost

Leetcode 3213. Construct String with Minimum Cost 1. 解题思路2. 代码实现 题目链接&#xff1a;3213. Construct String with Minimum Cost 1. 解题思路 这一题的话思路上还是比较直接的&#xff0c;就是一个trie树加一个动态规划&#xff0c;通过trie树来快速寻找每一个…...

python操作SQLite3数据库进行增删改查

python操作SQLite3数据库进行增删改查 1、创建SQLite3数据库 可以通过Navicat图形化软件来创建: 2、创建表 利用Navicat图形化软件来创建: 存储在 SQLite 数据库中的每个值(或是由数据库引擎所操作的值)都有一个以下的存储类型: NULL. 值是空值。 INTEGER. 值是有符…...

【电控笔记6.7】非最小相位系统

全通滤波器 [...