快速排序
一、定义:快速排序是一种基于分治法的排序算法,通过一次划分将待排序的序列分为两个子序列,其中一个子序列的元素都小于另一个子序列的元素,然后对两个子序列递归进行快速排序,直到所有的元素都有序。
二、过程:选择一个基准元素;
通过一遍遍历,将待排序的序列分成两个部分,一部分比基准元素小,另一部分比基准元素大或等于基准元素。
对这两部分分别递归进行快速排序;
合并结果。
一般有三种实现方法:
1.霍尔法:用pivot标记基准元素的下标(通常是数组的第一个元素),利用两个指针left和right分别指向数组的最左侧和最右侧,right指针先找比pivot基准元素小的元素,然后left找比pivot基准元素大的元素,找到后将两个数的位置交换,同时实现大数右移和小数左移,直到left和right相遇则排序完成,此时将pivot基准元素的下标返回给函数调用者就完成了单趟排序。
2.挖坑法:将pivot基准元素用变量单独保存,然后将pivot的位置空出来形成一个坑,left和right分别从左右两端向中心遍历,此时left先指向这个坑,right找到比pivot小的元素,找到后将该位置的元素直接放进坑里,并将自己空出来的位置设置为坑,left找比pivot大的元素,找到后将该元素放进坑里,并将现在空出来的位置设置为坑,一直遍历,直到left与right相遇,相遇的位置一定为坑(left和right一定有一个指向坑),此时将pivot基准元素放进坑内,并返回基准元素下标完成单趟排序。
3. 前后指针法。
注:还可以使用非递归的方式实现,如栈储存。
如果基准元素pivot选择的是数组中最大或最小的数值,则快速排序的递归深度会非常深,排序效率会很低。
三、选择基准元素的方法:
1.随机选择:利用rand函数随机选择数组中的一个数作为随机数,但是仍然有概率选到最大值或最小值。
2.选中间位置:选取左右下标的中间下标作为基准元素,这种方法对于有序数组可以达到最大效率,但是无序数组仍可能选到最大值或最小值。
3.三值取中:在left、right和中间下标的值中选取一个折中值,基准元素不可能为最大值或最小值(若数组中的元素未出现重复)。
四、快速排序程序:
void quick_sort(int a[], int low, int high) {
int i = low;
int j = high;
int key = a[i];
while (i < j) {
while (i < j && a[j] >= key) {
j--;
}
a[i] = a[j];
while (i < j && a[i] <= key) {
i++;
}
a[j] = a[i];
}
a[i] = key;
if (i - 1 > low) {
quick_sort(a, low, i - 1);
}
if (i + 1 < high) {
quick_sort(a, i + 1, high);
}
}
或者
void exchange(int a[], int b, int c)
{
int t;
t=a[b];
a[b]=a[c];
a[c]=t;
}
void quick_sort(int a[], int low, int high) {
int i = low;
int j = high;
int key = a[i];
while (i < j) {
while (i < j && a[j] >= key) {
j--;
}
exchange(a, i, j);
//a[i] = a[j];
while (i < j && a[i] <= key) {
i++;
}
//a[j] = a[i];
exchange(a, i, j);
}
//a[i]=key;
if (i - 1 > low) {
quick_sort(a, low, i - 1);
}
if (i + 1 < high) {
quick_sort(a, i + 1, high);
}
}
或者:
void quicksort(int a[], int left, int right) {
int i = left, j = right;//i和j分别是左和右的指针的下标
int mid = a[(left + right) / 2];//取数组中间位置为基准值
while (i <= j) //当左指针在右指针的左侧时进行
{
while (a[i] < mid) i++;//当左边小于基准值时,向左继续比较
while (a[j] > mid) j--;//当右边大于基准值时,向右继续比较
if (i <= j) //此时左指针到达大于基准值处,右指针到达小于基准值处
{
int t;
t=a[i];
a[i]=a[j];
a[j]=t;
//交换左右指针当前位置的数值,使得小于基准值的数移到基准值位置的左边,大于基准值的数移到基准值位置的右边
i++;
j--;
//交换完成后,左指针向右移动,右指针向左移动
}
}
if (left < j) quicksort(a, left, j);
//若右指针最后大于最左位置,则在基准值的左边再次进行排序
if (i < right) quicksort(a, i, right);
//若左指针最后小于最右位置,则在基准值的右边再次进行排序
//递归
}