description
- 找一个基准点,每次把小于这个基准点的放在一边(根据是要降序还是升序来定),大于这个基准点的放在另一边来处理。对分出来的两个部分再次递归使用这个函数,直到每个部分只剩下一个数
code
- 这里做的是升序排序,把第一个数字作为基准点(单独拿出来),从另一边也就是末尾开始运行,如果小于基准点的话直接把值赋给刚才访问的元素(这里第一步就是头,也刚好是基准点),这样交替达到调换值得效果
<双路快排>
- cpp代码
// Quick_Sort.cpp : Defines the entry point for the application.
// 快速排序算法
#include <iostream>
using namespace std;
//快速排序算法(从小到大)
// arr:需要排序的数组,begin:需要排序的区间左边界,end:需要排序的区间的右边界
void quickSort(int* arr, int begin, int end) {
//如果区间不只一个数
if(begin < end) {
int pivot = arr[begin]; //将区间的第一个数作为基准数
int i = begin; //从左到右进行查找时的“指针”,指示当前左位置
int j = end; //从右到左进行查找时的“指针”,指示当前右位置
//不重复遍历
while(i < j) {
//当右边的数大于基准数时,略过,继续向左查找
//不满足条件时跳出循环,此时的j对应的元素是小于基准元素的
while(i < j && arr[j] >= pivot)
j--;
//将右边小于等于基准元素的数填入右边相应位置
arr[i] = arr[j];
//当左边的数小于等于基准数时,略过,继续向右查找
//(重复的基准元素集合到左区间)
//不满足条件时跳出循环,此时的i对应的元素是大于等于基准元素的
while(i < j && arr[i] <= pivot)
i++;
//将左边大于基准元素的数填入左边相应位置
arr[j] = arr[i];
}
//将基准元素填入相应位置
arr[i] = pivot;
//此时的i即为基准元素的位置
//对基准元素的左边子区间进行相似的快速排序
quickSort(arr, begin, i - 1);
//对基准元素的右边子区间进行相似的快速排序
quickSort(arr, i + 1, end);
}
//如果区间只有一个数,则返回
}
int main() {
int num[12] = {23, 45, 17, 11, 13, 89, 72, 26, 3, 17, 11, 13};
int n = 12;
quickSort(num, 0, n - 1);
cout << "排序后的数组为:" << endl;
for(int i = 0; i < n; i++)
cout << num[i] << ' ';
cout << endl;
return 0;
}
- 输出
排序后的数组为:
3 11 11 13 13 17 17 23 26 45 72 89
最坏的时候可能会每次分成1和n-1的情况
改进:
进行pivot随机化处理
i = Random(begin,end);
swap(num[i],num[begin]);
QuickSort(begin,end);
补充
<三路快排>
- 一路的比较简单,就是像一个方向的,我没有写
- 三路快排的提出是为了解决数据中有大量重复元素,在双路中我们把重复的当做某一边的,这样会造成重复操作
- 这篇文章对原理进行了很好的讲解,我的代码和注释都是来自于这里https://www.cnblogs.com/deng-tao/p/6536302.html
代码:
/******************************************
* @Author : 鱼香肉丝没有鱼
* @Date : 2021-09-22 10:01:37
* @LastEditors : 鱼香肉丝没有鱼
* @LastEditTime : 2021-11-29 20:27:04
******************************************/
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
void Swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
void QuickSort3(int* arr, int left, int right)
{
if(left < right) {
//基准随机化
int mid = rand() % (right - left + 1) + left; //随机化处理,生成一个[left,right]的数字
Swap(arr[left], arr[mid]);
int v = arr[left]; //基准
int lt = left; // less than// 将<v的分界线的索引值lt初始化为第一个元素的位置(也就是<v部分的最后一个元素所在位置的后一个位置)
int gt = right + 1; // great than将>v的分界线的索引值gt初始化为最后一个元素right的后一个元素所在位置(也就是>v部分的第一个元素所在位置)
int i = left + 1; //等于部分的指针,将遍历序列的索引值i初始化为 left+1
while(i < gt) { // 循环继续的条件
if(arr[i] < v) { // 如果当前位置元素<v,则将当前位置元素与=v部分的第一个元素交换位置
Swap(arr[i], arr[lt + 1]);
i++; // i++ 考虑下一个元素
lt++; // lt++ 表示<v部分多了一个元素
}
else if(arr[i] > v) { // 如果当前位置元素>v,则将当前位置元素与>v部分的第一个元素的前一个元素交换位置
Swap(arr[i], arr[gt - 1]); // 此时i不用动,因为交换过来的元素还没有考虑他的大小
gt--; // gt-- 表示>v部分多了一个元素
}
else
i++; // 如果当前位置元素=v , 则只需要将i++即可,表示=v部分多了一个元素
}
Swap(arr[left], arr[lt]); // 上面的遍历完成之后,将整个序列的第一个元素(也就是"基准"元素)放置到合适的位置
// 也就是将它放置在=v部分即可
QuickSort3(arr, left, lt - 1); // 对<v部分递归调用_
QuickSort3(arr, gt, right); // 对>v部分递归调用
}
}
int main()
{
int arr[15] = {23, 45, 17, 11, 13, 89, 72, 26, 3, 17, 11, 13, 13, 17, 13};
int n = 15;
srand(time(NULL)); //使用随机种子
QuickSort3(arr, 0, n - 1);
cout << "排序后的数组为:" << endl;
for(int i = 0; i < n; i++)
cout << arr[i] << ' ';
cout << endl;
return 0;
}
summary
- 快速排序算法得时间复杂度是不确定的,可能是 O ( n log n ) {\rm{O}}(n\log n) O(nlogn) , 也可能是 O ( n 2 ) {\rm{O}}({n^2}) O(n2).
- 属于分治法的范畴,分解时间长,不需要合
- 当给定的数组有序时为最坏情况,这个时候的复杂度为O(n2),为了避免这个情况,我们可以进行随机化处理,随机选取一个数作为pivot,,注:c++中的qsort()就进行了随机化处理