快速排序是基于分治策略的一种排序算法。基本思想是:对于给定序列按照以下三个步骤进行
- 分解序列:将数组的最右边的元素作为划分元,让比他小的都到他的左边,比他大的都放到他的右边
- 递归求解:通过递归调用快速排序,分别对划分出的左半端和有半段进行排序。
- 合并:当所有的分段都排好序之后进行合并,最后得到的数组就是排好序的数组。
基于以上思想,可以实现快速排序算法如下:
void QuickPass(int* ar, int left, int right)
{
if (left < right)
{
int pos = DetermineDivision(ar, left, right);//找到划分元位置
QuickPass(ar, left, pos - 1);//对划分元左端进行排序
QuickPass(ar, pos + 1, right);//对划分元右端进行排序
}
}
划分函数是三种排序的基础
int Partition(int* ar, int left, int right)
{
assert(ar != nullptr);
int tmp = ar[left];
while (left < right)
{
while (left < right && ar[right] >= tmp)
{
right--;
}
if (left < right)
{
ar[left] = ar[right];
}
while (left < right && ar[left] <= tmp)
{
left++;
}
if (left < right)
{
ar[right] = ar[left];
}
}
ar[left] = tmp;
return left;
}
快速排序可分为三种方法:传统的快速排序,随机数法快速排序以及中位数法快速排序。三种方法的主要区别在于划分元的选择不同,传统的快速排序不论所给的序列如何,都采用序列的端点元素作为划分元,这么做在序列对称的情况下可以取得比较好的效果,但是当对有序序列进行排序的时候效果就不是特别明显了;随机数法快速排序相较于传统的快速排序,在无序对称的情况下时间复杂度差不多,但是在有序的情况下效果较好,缺点就是随机数法有一定的随机性,在一定的概率下可能会起到适得其反的 作用;想要快速排序的时间复杂度在任何情况下都可以达到O(nlongn),那么最好的方法应该是中位数法快速排序,其采用三位取中法确定划分元,该算法对于有序或者无需的数组都可以起到较好的效果。
下面我们来看看三种算法的具体实现:
传统快速排序(不确定划分直接使用序列端点进行划分) :
#include <stdio.h>
#include<assert.h>
#include <stdlib.h>
#define N 2000
template <class Type>
//模板函数,增强函数的实用性
void Swap(Type& a, Type& b)
{
Type tmp = a;
a = b;
b = tmp;
}
//划分函数
int Partition(int* ar, int left, int right)
{
assert(ar != nullptr);
int tmp = ar[left];
while (left < right)
{
while (left < right && ar[right] >= tmp)
{
right--;
}
if (left < right)
{
ar[left] = ar[right];
}
while (left < right && ar[left] <= tmp)
{
left++;
}
if (left < right)
{
ar[right] = ar[left];
}
}
ar[left] = tmp;
return left;
}
//对划分的分段进行递归求解
void QuickPass(int* ar, int left, int right)
{
if (left < right)
{
int pos = Partition(ar, left, right);//找到划分元位置
QuickPass(ar, left, pos - 1);//对划分元左端进行排序
QuickPass(ar, pos + 1, right);//对划分元右端进行排序
}
}
void Quicksort(int* ar, int arsize)
{
assert(ar != nullptr);
QuickPass(ar, 0, arsize - 1);
}
void Print(int* ar, int n)
{
assert(ar != nullptr);
for (int i = 0; i < n; ++i)
{
printf("%5d", ar[i]);
}
printf("\n");
}
void Init_ar(int* ar, int n)
{
for (int i = 0; i < n; ++i)
{
ar[i] = rand() % n + 1;
}
}
int main()
{
int ar[N] = { 0 };
Init_ar2(ar, N);
Print(ar, N);
Quicksort(ar, N);
Print(ar, N);
return 0;
}
随机数法快速排序(在分段中取一个随机数作为划分元):
#include <stdio.h>
#include<assert.h>
#include <stdlib.h>
#include <iostream>
#include <Windows.h>
#define N 2000
template <class Type>
//模板函数,增强函数的实用性
void Swap(Type& a, Type& b)
{
Type tmp = a;
a = b;
b = tmp;
}
//划分函数
int Partition(int* ar, int left, int right)
{
assert(ar != nullptr);
int tmp = ar[left];
while (left < right)
{
while (left < right && ar[right] >= tmp)
{
right--;
}
if (left < right)
{
ar[left] = ar[right];
}
while (left < right && ar[left] <= tmp)
{
left++;
}
if (left < right)
{
ar[right] = ar[left];
}
}
ar[left] = tmp;
return left;
}
//随机化法划分函数
int Randpartition(int* ar, int left, int right)
{
//在所要划分的分段内找到一个随机位置,然后将这个位置的数据和left位置的数据进行交换
int index = rand() % (right - left) + left;
Swap(ar[left], ar[index]);
return Partition(ar, left, right);
}
void QuickPass(int* ar, int left, int right)
{
if (left < right)
{
int pos = Randpartition(ar, left, right);//找到划分元位置
QuickPass(ar, left, pos - 1);//对划分元左端进行排序
QuickPass(ar, pos + 1, right);//对划分元右端进行排序
}
}
void Quicksort(int* ar, int arsize)
{
assert(ar != nullptr);
QuickPass(ar, 0, arsize - 1);
}
void Print(int* ar, int n)
{
assert(ar != nullptr);
for (int i = 0; i < n; ++i)
{
printf("%5d", ar[i]);
}
printf("\n");
}
void Init_ar(int* ar, int n)
{
for (int i = 0; i < n; ++i)
{
ar[i] = rand() % n + 1;
}
}
int main()
{
int ar[N] = { 0 };
Init_ar2(ar, N);
Print(ar, N);
Quicksort(ar, N);
Print(ar, N);
return 0;
}
中位数快速排序(以三位取中法取得划分元对序列进行划分):
#include <stdio.h>
#include<assert.h>
#include <stdlib.h>
#define N 2000
template <class Type>
//模板函数,增强函数的实用性
void Swap(Type& a, Type& b)
{
Type tmp = a;
a = b;
b = tmp;
}
//划分函数
int Partition(int* ar, int left, int right)
{
assert(ar != nullptr);
int tmp = ar[left];
while (left < right)
{
while (left < right && ar[right] >= tmp)
{
right--;
}
if (left < right)
{
ar[left] = ar[right];
}
while (left < right && ar[left] <= tmp)
{
left++;
}
if (left < right)
{
ar[right] = ar[left];
}
}
ar[left] = tmp;
return left;
}
int GetMid(int* ar, int left, int right)
{
assert(ar != nullptr);
int mid = (right - left) / 2 + left;
if (ar[left] <= ar[right] && ar[right] <= ar[mid])
{
mid = right;
}
else if (ar[left] >= ar[right] && ar[left] <= ar[mid])
{
mid = left;
}
else
return mid;
}
//确定划分元,找到数组中的第(k+1)/2大的数,然后与第一位进行交换
int DetermineDivision(int* ar, int left, int right)
{
assert(ar != nullptr);
int pos=GetMid(ar, left, right);
Swap(ar[left], ar[pos]);
return Partition(ar, left, right);
}
void QuickPass(int* ar, int left, int right)
{
if (left < right)
{
int pos = DetermineDivision(ar, left, right);//找到划分元位置
QuickPass(ar, left, pos - 1);//对划分元左端进行排序
QuickPass(ar, pos + 1, right);//对划分元右端进行排序
}
}
void Quicksort(int* ar, int arsize)
{
assert(ar != nullptr);
QuickPass(ar, 0, arsize - 1);
}
void Print(int* ar, int n)
{
assert(ar != nullptr);
for (int i = 0; i < n; ++i)
{
printf("%5d", ar[i]);
}
printf("\n");
}
void Init_ar2(int* ar, int n)
{
for (int i = 0; i < n; ++i)
{
ar[i] = n - i;
}
}
int main()
{
int ar[N] = { 0 };
Init_ar2(ar, N);
Print(ar, N);
Quicksort(ar, N);
Print(ar, N);
return 0;
}