最近在复习数据结构,遇到排序算法,自然排序算法效率最高的当然是快速排序,今天我们就一起来讲讲快速排序。
快速排序主要分为三个步骤:
1)选择基准数:在待排序的序列中,按照某种方式悬着一个元素作为基准数。
2:)对基准数两边的数进行分组。把比基准数小的数放在基准数的左边,比基准数大的数放在基准数的右边。(可按照自己的需求来选择放在基准数的左边还是基准数的右边ps:需要升序还是降序)。
3:)用递归的方式来对基准数左右两边的数经行再次排序;直到快排完成。
既然知道了快排的基本思想,接下来就是实现快速排序算法了,按照上述的思想来看,选则基准数是决定了快速排序是否高效的一个标准,对于分支算法来说,每次进行分治,若能够分成两个等长的子序列是,那么分治算法的效率会达到最大,这也是为什么基准数的选择是非常重要的一个原因.所以最理想的基准数选择就是恰好能够把待排序序列分成两个等长的子序列。
方法一:最基本的快排(固定基准数)
思路:取待排序的第一个或者最后一个元素作为基准数。下面的算法是取第一个元素为基准数
int arr[100];//全局变量,需要在quicksort函数中用到
int n;//存储需要排序数字的个数
void quicksort(int start,int end)
{
int i;//标杆
int j;//标杆
int t;//临时变量
int temp;//存储基准数
/*
递归退出的条件
*/
if (start > end)
{
return;
}
temp = arr[start];//temp存储准基数
i = start;
j = end;
while( i != j)
{
while (arr[j] >= temp && i < j)
{
j--;
}
while (arr[i] <= temp && i < j)
{
i++;
}
if (i < j)
{
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
if (i == j)
{
arr[start] = arr[i];
arr[i] = temp;
}
quicksort(start,i - 1);
quicksort(i + 1, end);
}
这样处理的效果很不好,如果待排序序列已经有序,此时的基准数的选择就是一个非常不好的分割,因此每次划分只能是待排序序列减一,最坏的情况,快排沦为冒泡,在实际中,部分序列有序是非常常见的,所以这样的快排效率不高。
方法二:随机基准数
思路:在待排序的序列中选取任意一个元素作为基准数
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int arr[100];//全局变量,需要在quicksort函数中用到
int n;//存储需要排序数字的个数
void randIndex(int first,int end)
{
srand((unsigned)time(0));
int result = rand() % (end - first + 1) + first;
/*
和第一个数交换
*/
int temp = arr[first];
arr[first] = arr[result];
arr[result] = temp;
}
void quicksort(int start,int end)
{
int i;//标杆
int j;//标杆
int t;//临时变量
int temp;//存储基准数
if (start > end)
{
return;
}
randIndex(start,end);
printf("%d\n",arr[start]);
temp = arr[start];//temp存储准基数
i = start;
j = end;
while( i != j)
{
while (arr[j] >= temp && i < j)
{
j--;
}
while (arr[i] <= temp && i < j)
{
i++;
}
if (i < j)
{
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
if (i == j)
{
arr[start] = arr[i];
arr[i] = temp;
}
quicksort(start,i - 1);
quicksort(i + 1, end);
}
int main()
{
int i;
int j;
int t;
while (scanf("%d",&n) != EOF)
{
for (i = 1; i <= n; i++)
{
scanf("%d",&arr[i]);
}
quicksort(1,n);
for (i = 1; i <= n; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}
getchar();
getchar();
return 0;
}
由于基准数是随机的,那么残生的奋哥也不会是总是会出现差的分割,在整个序列都相等的情况是,是最坏的情况,时间复杂度O(n^2),但是,随机快排得到的理论最坏的情况可能性仅为1/(2^n)。
第三种:三数取中
虽然第二种放发减少了差的分割几率,但是最坏的情况下复杂度还是O(n^2),所以就有第三种
思路:根据前面的分析我们知道最好的分割结果是将待排序的序列分割成等长的子序列,最好的基准数即为序列的中间值,即2/N;但是,这样明显减慢了快速排序的速度,这样的中值估计法我们可以通过随机选取三个元素并用他们的中值作为基准数,事实上随机性 并没有很大的帮助,因此一般的做法是取最左边的和最右边的加上中间位置的中值作为基准数,这种方法消除了与排序输入不好的情况,并减少了大概14%的比较次数。
比如:待排序序列:1 9 2 8 7 3 6 4 10 5
最左边为:1 最右边为:5 中间为:7
将他们排序后,中值为5,即5为基准数。并用和第一个元素交换,用第一个元素来存储基准数
这里就只提供怎么杨确定基准数的方法,具体的排序方法可参照第一中方法:
void partintionMedianOfThree(int[] arr,int start,int end)
{
int mid = start + (end - start) / 2;
if (arr[mid] > arr[end])
{
int temp = arr[mid];
arr[mid] = arr[end];
arr[end] = temp;
}
if (arr[start] > arr[end])
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
}
if (arr[mid] > arr[start])
{
int temp = arr[start];
arr[start] = arr[start];
arr[start] = temp;
}
}
以上就是所有的内容了,如有不对,请多多指教。