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

        快速排序的原理是交换排序,其中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;
}

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

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

相关文章

web缓存代理服务器

一、web缓存代理 web代理的工作机制 代理服务器是一个位于客户端和原始&#xff08;资源&#xff09;服务器之间的服务器&#xff0c;为了从原始服务器取得内容&#xff0c;客户端向代理服务器发送一个请求&#xff0c;并指定目标原始服务器&#xff0c;然后代理服务器向原始…

2-27 基于matlab的一种混凝土骨料三维随机投放模型

基于matlab的一种混凝土骨料三维随机投放模型&#xff0c;为混凝土细观力学研究提供一种快捷的三维建模源代码。可设置骨料数量&#xff0c;边界距离、骨料大小等参数。程序已调通&#xff0c;可直接运行。 2-27 matlab 混凝土骨料三维随机投放模型 - 小红书 (xiaohongshu.com)…

CDNOW_master.txt数据分析实战

一、数据详情 该数据集是常见的销售数据集&#xff0c;数据展示的是美国1997后的商品销售数据。包含四个字段&#xff0c;分别是用户id,购买时间&#xff0c;销售量&#xff0c;与销售金额。 二、数据读取与数据清洗 导入必要的包 \s代表的许多空格作为分割&#xff0c;names重…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【明文导入密钥(C/C++)】

明文导入密钥(C/C) 以明文导入ECC密钥为例。具体的场景介绍及支持的算法规格 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 指定密钥别名keyAlias。 密钥别名的最大长度为64字节。 封装密钥属性集和密钥材料。通过[OH_Huks_I…

Word文档中公式的常用操作

一、参考资料 二、常用操作 插入公式 Alt 多行公式 Shift Enter 多行公式对齐 WORD Tips: 多行公式编辑及对齐 word自带公式等号对齐&#xff08;可任意符号处对齐&#xff09; 多行公式按照 为基准对齐。 拖动鼠标选中整个公式点击右键&#xff0c;选择【对齐点(…

计算机系统简述

目标 计算机世界并非如此神秘。相反&#xff0c;计算机是非常“确定”的一个系统&#xff0c;即在任何时候&#xff0c;在相同的方法、相同的状态下&#xff08;当然还包括相同的起始条件&#xff09;&#xff0c;同样的问题必然获得相同的结果。其实&#xff0c;计算机并不是…

数据库的学习(4)

一、题目 1、创建数据表qrade: CREATE TABLE grade(id INT NOT NULL,sex CHAR(1),firstname VARCHAR(20)NOT NULL,lastname VARCHAR(20)NOT NULL,english FLOAT,math FLOAT,chinese FLOAT ); 2、向数据表grade中插入几条数据: (3,mAllenwiiliam,88.0,92.0 95.0), (4,m,George&…

【数据结构与算法】快速排序双指针法

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​

【js基础巩固】深入理解作用域与作用域链

作用域链 先看一段代码&#xff0c;下面代码输出的结果是什么&#xff1f; function bar() {console.log(myName) } function foo() {var myName "极客邦"bar() } var myName "极客时间" foo()当执行到 console.log(myName) 这句代码的时候&#xff0c…

nullptr和NULL

nullptr 既不是整型类型&#xff0c;也不是指针类型&#xff0c;nullptr 的类型是 std::nullptr_t&#xff08;空指针类型&#xff09;&#xff0c;能转换成任意的指针类型。 NULL是被定义为0的常量&#xff0c;当遇到函数重载时&#xff0c;就会出现问题。避免歧义 函数重载…

数据结构--二叉树和堆

目录 1.基本概念 2.树的遍历方法 3.满二叉树&&完全二叉树 4.逻辑结构&&物理结构 5.推理公式 6.二叉树应用--堆 7.简单实现堆 1.基本概念 &#xff08;1&#xff09;这个里面的概念还是比较多的&#xff0c;但是大部分我们只需要了解即可&#xff0c;因为…

Ubuntu TensorRT安装

什么是TensorRT 一般的深度学习项目&#xff0c;训练时为了加快速度&#xff0c;会使用多 GPU 分布式训练。但在部署推理时&#xff0c;为了降低成本&#xff0c;往往使用单个 GPU 机器甚至嵌入式平台&#xff08;比如 NVIDIA Jetson&#xff09;进行部署&#xff0c;部署端也…

VSCode使用ipynb文件高效地进行功能测试

一、ipynb是什么文件 .ipynb文件是Jupyter Notebook的专用格式&#xff0c;它允许用户在一个网页应用中混合编写Markdown文本、执行代码、查看输出结果及图表。Jupyter Notebook的本质是一个Web应用程序&#xff0c;支持运行40多种编程语言&#xff0c;包括Python。它的主要用…

java反射介绍

Java反射API允许你在运行时检查和修改程序的行为。这意味着你可以动态地创建对象、查看类的字段、方法和构造函数&#xff0c;甚至调用它们。这是一个强大的特性&#xff0c;但也应该谨慎使用&#xff0c;因为它可以破坏封装性。 以下是使用Java反射的一些常见用途&#xff1a;…

【鸿蒙学习笔记】MVVM模式

官方文档&#xff1a;MVVM模式 [Q&A] 什么是MVVM ArkUI采取MVVM Model View ViewModel模式。 Model层&#xff1a;存储数据和相关逻辑的模型。View层&#xff1a;在ArkUI中通常是Component装饰组件渲染的UI。ViewModel层&#xff1a;在ArkUI中&#xff0c;ViewModel是…

[AHK V2]获取本地IP地址

问题&#xff1a;如何用AutoHotkey v2 获取本地IP地址。 解答&#xff1a;AutoHotkey v2 源代码如下 #Requires AutoHotkey v2; MsgBox GetLocalIPByAdapter(Ethernet) ; <— specify the adapter name you are interested in ; MsgBox GetLocalIPByAdapter(以太网) ; <…

《算法笔记》总结No.4——散列

散列的英文名是hash&#xff0c;即我们常说的哈希~该知识点在王道408考研的教材里面属于查找的范围。即便各位并无深入了解过&#xff0c;也听说过散列是一种更高效的查找方法。 一.引例 先来考虑如下一个假设&#xff1a;设有数组M和N分别如下&#xff1a; M[10][1,2,3,4,5,6…

java IO流(1)

一. 文件类 java中提供了一个File类来表示一个文件或目录(文件夹),并提供了一些方法可以操作该文件 1. 文件类的常用方法 File(String pathname)构造方法,里面传一个路径名,用来表示一个文件boolean canRead()判断文件是否是可读文件boolean canWrite()判断文件是否是可写文…

现场Live震撼!OmAgent框架强势开源!行业应用已全面开花

第一个提出自动驾驶并进行研发的公司是Google&#xff0c;巧的是&#xff0c;它发布的Transformer模型也为今天的大模型发展奠定了基础。 自动驾驶已经完成从概念到现实的华丽转变&#xff0c;彻底重塑了传统驾车方式&#xff0c;而大模型行业正在经历的&#xff0c;恰如自动驾…

基于S32K144驱动NSD8381

文章目录 1.前言2.芯片介绍2.1 芯片简介2.2 硬件特性2.3 软件特性 3.测试环境3.1 工具3.2 架构 4.软件驱动4.1 SPI4.2 CTRL引脚4.3 寄存器4.4 双极性步进电机驱动流程 5.测试情况6.参考资料 1.前言 最近有些做电磁阀和调光大灯的客户需要寻找国产的双极性步进电机驱动&#xf…