快排的引入
快速排序被称为世界十大算法之一,就像他的名字一样,速度快,效率高,是一种优秀的排序算法。今天我们来介绍几种快速排序的实现方法;以及快速排序的思想。
算法思想##
快速排序采用了分治的算法思想,先找出一个基准元素,然后定义两个指针分别指向拍序序列的头和尾(不包含基准元素),left负责找大于基准元素的数,right负责找小于基准元素的数,一趟交换直到left和right交换为止,最后把left和right指向的元素和key所指向的元素进行交换。这样做是为了保证一趟排序过后key右边的元素都大于key左边的元素,随后在根据key的位置来分割成两个子序列,在对子序列进行排序,使子序列有序,子序列有序后则排序序列有序。
快速排序交换法
在算法思想中介绍的快速排序方法称为交换法,它是通过left和right的交换从而使最后分割序列时分割点之前的元素都小于分割点之后的元素,便于后面子序列有序后合并出来的序列已经是排好序的序列了。
不过交换法需要对一种情况作出检测:
所以我们需要在交换key和left与right相遇的元素时需要进行判断断,如果key小于这个元素则进行交换否则不进行交换。
void QuickSort(int *a, int begin, int end)
{
assert(a);
int div = PortSort(a, begin, end);
if (div - 1> begin)
{
QuickSort(a, begin, div - 1);
}
if (div + 1< end)
{
QuickSort(a, div + 1, end);
}
}
size_t PortSort(int* a, int begin, int end)
{
size_t left = begin;
size_t right = end - 1;
int key = a[end];
while (left < right)
{
while (left<right && a[left] <= key)
{
//left找比key大的
left++;
}
while (left < right && a[right] >= key)
{
//right为找a中小于key的下标
right--;
}
if (left < right) //left < right 交换
{
swap(a[left], a[right]);
}
}
//最后判断key和相遇元素的交换条件
if (key <= a[left])
{
swap(a[end], a[left]);
return left;
}
else
return end;
}
快速排序挖坑法
挖坑法的基本思想与交换法的思想相同,但是它避免了交换法所出现的错误情况。
int PortPothSort(int* a, int begin, int end)
{
int left = begin;
int right = end - 1;
int key = a[end];
int index = end; //初始的坑的位置
while (left < right)
{
while (left < right&&a[left] <= key) //left找大于key的元素
{
left++;
}
if (left < right)
{
a[index] = a[left]; //填坑
index = left; //left所指向的位置现在变为坑
}
while (left < right&&a[right] >= key)
{
right--;
}
if (left < right)
{
a[index] = a[right];
index = right;
}
}
a[index] = key; //一趟排序完成后把key填入坑中
return index; //返回最后一个坑的位置
}
void QuickPothSort(int* a, int begin, int end)
{
assert(a);
int div = PortPothSort(a, begin, end);
if (div - 1> begin)
{
QuickSort(a, begin, div - 1);
}
if (div + 1< end)
{
QuickSort(a, div + 1, end);
}
}
前后指针法
size_t PortSortOP(int* a, int begin, int end)
{
int cur = begin;
int prev = cur - 1;
int key = a[end];
while (cur <= end)
{
if (a[cur] < key && ++prev != cur) //cur找比key小的数
swap(a[cur], a[prev]); //cur遇到比key大的数,prev不移动
cur++; //cur再次遇到比key小的数时如果prev不紧跟cur 则进行交换
}
swap(a[++prev], a[end]);
return prev;
}
void QuickSortOP(int* a, int begin, int end)
{
assert(a);
int div = PortSortOP(a, begin, end);
if (div - 1 > begin)
QuickSortOP(a, begin, div - 1);
if (div + 1 < end)
QuickSortOP(a, div + 1, end);
}
快速排序分析
快速排序的时间主要耗费在划分操作上,对长度为k的区间进行划分,共需k-1次关键字的比较。
最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。时间复杂度为O(n*n)
在最好情况下,每次划分所取的基准都是当前无序区的”中值”记录,划分的结果是基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数:O(nlgn)
尽管快速排序的最坏时间为O(n2),但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快者,快速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。