快速排序
算法思想:基于分治的思想,是冒泡排序的改进型。同冒泡排序一样,快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。
不同的是,冒泡排序在每一轮只把一个元素冒泡到数列的一端,而快速排序在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成了两个部分。
每次把数列分成两部分,究竟有什么好处呢?
假如给定8个元素的数列,一般情况下冒泡排序需要比较8轮,每轮把一个元素移动到数列一端,时间复杂度是O(n^2)。
Hoare法:
代码
int HoareMethod(int *a, int beain, int end)
{
int key = a[end];
int keyindex = end;
while (beain < end)
{
while (beain < end&&a[beain] <= key)
beain++;
while (beain < end&&a[end] >= key)
end--;
swap(&a[beain], &a[end]);
}
swap(&a[beain], &a[keyindex]);
return beain;
}
挖坑法:
挖坑法顾名思义,首先我们从右边先取走一个基准值用于比较的数key,取走后这个数所在的位置就成为了一个坑,然后与Hoare法类似,从左边开始找到一个比key大的数,然后将这个数填到刚在取走的数的位置,现在刚才取走的数的位置又成了一个坑,以此类推
给定原始数列如下,要求从小到大排序:
首先,我们选定基准元素Pivot,并记住这个位置index,这个位置相当于一个“坑”。并且设置两个指针left和right,指向数列的最左和最右两个元素:
接下来,从right指针开始,把指针所指向的元素和基准元素做比较。如果比pivot大,则right指针向左移动;如果比pivot小,则把right所指向的元素填入坑中。
在当前数列中,1<4,所以把1填入基准元素所在位置,也就是坑的位置。这时候,元素1本来所在的位置成为了新的坑。同时,left向右移动一位。
此时,left左边绿色的区域代表着小于基准元素的区域。
接下来,我们切换到left指针进行比较。如果left指向的元素小于pivot,则left指针向右移动;如果元素大于pivot,则把left指向的元素填入坑中。
在当前数列中,7>4,所以把7填入index的位置。这时候元素7本来的位置成为了新的坑。同时,right向左移动一位。
以此类推。
代码如下:
int PitMethod(int *a, int begin,int end)
{
int key = a[end];
int pit = end;
while (begin < end)
{
while (begin < end&&a[begin] <= key)
{
begin++;
}
a[pit] = a[begin];
pit = begin;
while (begin < end&&a[end] >= key)
{
end--;
}
a[pit] = a[end];
pit = end;
}
a[pit] = key;
return pit;
}
3.前后指针法
前后指针法是定义两个指针,一个cur指向数组第一个元素的位置,另一个pre指向cur的前一个位置,选取最右边的元素为key,比较cur指向的元素是否小于key,如果小于pre后移一个位置,并于cur所指向的位置的元素进行交换,然后cur后移一个位置,以此类推。
代码如下:
int PrevCurMethod(int* a, int begin, int end)
{
int pre = begin - 1;
int cur = begin;
while (cur < end)
{
if (a[cur] < a[end] && ++pre != cur)
swap(&a[cur], &a[pre]);
cur++;
}
pre++;
swap(&a[pre], &a[cur]);
return pre;
}
整体快速排序:
void QuickSort(int *a, int begin, int end)
{
if (begin >= end)
return;
//int keyindex = HoareMethod(a, begin, end); //Hoare法
//int keyindex = PitMethod(a, begin, end); //挖坑法
int keyindex = PrevCurMethod(a, begin, end); //前后指针法
QuickSort(a, begin, keyindex - 1);
QuickSort(a, keyindex + 1, end);
}