C语言实现快速排序算法

news/2024/12/2 14:05:32/

1. 什么是快速排序算法

快速排序的核心思想是通过分治法(Divide and Conquer)来实现排序。

算法的基本步骤是:

1. 选择一个基准值(通常是数组中的某个元素),将数组分成两部分,使得左边的部分所有元素都小于基准值,右边的部分所有元素都大于基准值。

2. 对这两部分分别进行递归排序,直到整个数组有序。

那么,该算法为什么叫做快速排序算法呢?

快速排序算法之所以被称为“快速”,是因为它在大多数情况下都能够快速地完成排序。在平均情况下,其时间复杂度为O(nlogn),其中n为数组的大小。

此外,快速排序还具有原地排序的特点,即不需要额外的辅助空间,只需对原始数组进行原地操作。这些优点使得快速排序成为了排序问题中的一种首选算法。


2. 单趟排序

单趟排序指的就是将数组分为两部分的算法。

对于实现这一步,我们有三种思路。

2.1 霍尔法

霍尔并无什么特殊含义,只是因为最早发现快速排序算法的人叫霍尔,这是他的实现方法。

思路:

1. 先选定一个基准值key(一般选择首元素)。

2. 定义两个指针left和right,分别指向数组的左右两端。

3. left从左往右遍历,寻找大于基准值的元素,right从右往左遍历,寻找小于基准值的元素。

4. 如果left与right未相遇,那么就交换两指针指向的元素。

5. 如果left与right相遇,那么就让key指向的元素与right指向的元素交换。

代码

//霍尔法
int ParSort_1(int* arr, int left, int right)
{int key = left;while (left < right){while (left < right && arr[right] >= arr[key]) { right--; }while (left < right && arr[left] <= arr[key]) { left++; }if (left < right)swap(&arr[left], &arr[right]);}swap(&arr[key], &arr[left]);return left;
}

注意事项(如何保证right和left共同指向的元素与key指向的元素交换是合理的)

1. 如果选取的基准值为首元素,那么在外层循环的一次循环中,一定要让right先进行移动,这样可以确保共同指向的元素是小于或等于基准值的。

2. 如果选取的基准值为最后一个元素,则与上面相反。

2.2 挖坑法

核心思想与霍尔法相同,但是表现形式有所差异。

思路

顾名思义,我们将基准值单独用变量进行存放,而将基准值原本所在的位置空出来成为一个坑(我们将其称作hole)。

1. right先进行遍历,如果发现有小于基准值的元素,则将该元素填入hole中,然后right指向的位置成为新的hole。(假设F小于key)

2. 然后left再进行遍历,如果发现有大于基准值的元素,则将该元素填入hole中,然后left指向的位置成为新的hole。(假设B大于key)

 3. 以此类推,当left与right相遇时,将key填入hole中即可。

代码

//挖坑法
int ParSort_2(int* arr, int left, int right)
{int key = arr[left];int hole = left;while (left < right){while (left < right && arr[right] >= key) { right--; }arr[hole] = arr[right];hole = right;while (left < right && arr[left] <= key) { left++; }arr[hole] = arr[left];hole = left;}arr[hole] = key;return hole;
}

注意事项

对于这个方法来说,洞在谁那边,谁就先开始遍历。

2.3 前后指针法

思路

1. 定义两个指针,prev和cur,prev所指向的以及之前的元素就是小于基准值的元素,cur用于遍历数组。

2. 如果cur找到小于基准值的元素,让prev++,然后交换prev指向的元素和cur指向的元素。

3. 让基准值与prev指向的元素进行交换。

代码

//前后指针
int ParSort_3(int* arr, int left, int right)
{int key = left;int prev = left;int cur = left + 1;while (cur <= right){//arr[cur]小于基准值就交换if (arr[cur] <= arr[key] && ++prev != cur){swap(&arr[prev], &arr[cur]);}cur++;}swap(&arr[key], &arr[prev]);return prev;
}

注意事项

如果选取的基准值为首元素,则在最后让prev指向的元素与基准值交换;如果选区的基准值为最后一个元素,则在最后让prev指向的下一个元素与基准值交换。

3. 快速排序算法的递归实现

按照第一部分的介绍,我们很容易的到下面的代码。

void QuickSort(int* arr, int begin, int end)
{if (begin >= end)return;int key = ParSort_3(arr, begin, end);QuickSort(arr, key + 1, end);//排右边QuickSort(arr, begin, key - 1);//排左边
}

4. 快速排序算法的优化

当数据量十分巨大时,使用递归实现的快速排序算法会由于调用函数次数过多而导致程序效率下降。

由于递归的逻辑结构像是一个树状图,所以在递归的层次较深时,每次进到下一层都会调用上一层两倍数量的函数。

众所周知,2^{n}是一个极其可怕的东西,那么我们能不能在层次较深,所需排序的数组长度较短时,转而使用其他经典的排序算法呢?

于是我们做出了如下的优化:

void QuickSort(int* arr, int begin, int end)
{if (begin >= end)return;if (end - begin <= 8){InsertSort(arr + begin, end - begin + 1);return;}int key = ParSort_3(arr, begin, end);QuickSort(arr, key + 1, end);//排右边QuickSort(arr, begin, key - 1);//排左边
}

当数组长度小于等于9时,我们采用插入排序来进行排序(不止插入排序,其他排序也可)。

//插入排序
void InsertSort(int* arr, int len)
{for (int i = 1; i < len; i++){int j = i;while (j > 0 && arr[j] < arr[j - 1]){swap(&arr[j], &arr[j - 1]);j--;}}
}

5. 结语

快速排序的非递归算法是用栈模拟递归实现的,由于太麻烦我就没写,感兴趣可以自己尝试写写。

单趟排序算法用哪个,快速排序递归还是非递归,层次较深时换用什么排序算法,这些都是可以自由组合的。


http://www.ppmy.cn/news/1408955.html

相关文章

Go语言和Java编程语言的主要区别

目录 1.设计理念&#xff1a; 2.语法&#xff1a; 3.性能&#xff1a; 4.并发性&#xff1a; 5.内存管理&#xff1a; 6.标准库&#xff1a; 7.社区和支持&#xff1a; 8.应用领域&#xff1a; Go&#xff08;也称为Golang&#xff09;和Java是两种不同的编程语言&…

Python+Yolov8框选位置目标识别人数统计计数

程序示例精选 PythonYolov8框选位置目标识别人数统计计数 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《PythonYolov8框选位置目标识别人数统计计数》编写代码&#xff0c;代码整洁&#…

IDEA2023创建SpringMVC项目

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 开发环境篇 ✨特色专栏&#xff1a; M…

2012年认证杯SPSSPRO杯数学建模C题(第二阶段)碎片化趋势下的奥运会商业模式全过程文档及程序

2012年认证杯SPSSPRO杯数学建模 C题 碎片化趋势下的奥运会商业模式 原题再现&#xff1a; 从 1984 年的美国洛杉矶奥运会开始&#xff0c;奥运会就不在成为一个“非卖品”&#xff0c;它在向观众诠释更高更快更强的体育精神的同时&#xff0c;也在攫取着巨大的商业价值&#…

OpenHarmony开发-系统烧录

本文详细介绍了烧录OpenHarmony系统到开发板的操作流程。从基础的硬件准备和软件环境设置入手&#xff0c;详细说明了如何配置开发环境、构建系统镜像等过程&#xff0c;详细描述了烧录过程中的关键步骤&#xff0c;以及如何使用专用工具将OpenHarmony系统镜像传输到开发板。同…

线性表之——顺序表

哈喽小伙伴们大家好&#xff0c;这篇博客呢&#xff0c;鱼头会和大家分享一下我最近学习的数据结构中的顺序表&#xff0c;希望能对在读的各位提供帮助&#xff0c;还望多多支持&#xff01; 目录 1.顺序表 1.1线性表 1.2顺序表的分类 1.2.1静态顺序表 1.2.2动态顺序表 …

基础算法精讲 07|环形链表II,快慢指针

876. 链表的中间结点 - 力扣&#xff08;LeetCode&#xff09; Time: O(n) Space:O(1) /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNod…

python爬虫获取豆瓣前top250的标题(简单)

今天是简略的一篇&#xff0c;简单小实验 import requests from bs4 import BeautifulSoup# 模拟浏览器的构成&#xff08;请求头&#xff09; headers {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Ch…