快速排序(Quick Sort)是一种常见且高效的排序算法,它的核心思想是分治法(Divide and Conquer)。具体而言,快速排序的主要步骤如下:
1. 选择基准元素:从数组中选择一个元素作为基准元素(pivot)。通常选择数组的中间元素,但也可以选择第一个或最后一个元素。
2. 分区(Partition):将数组中的元素重新排列,使得小于基准元素的元素位于基准元素的左侧,大于基准元素的元素位于基准元素的右侧。基准元素的准确位置称为分区点(partition point)。
3. 递归:对基准元素左右两侧的子数组分别进行快速排序。递归地应用快速排序算法,直到每个子数组的大小为 0 或 1,即数组已经有序。
4. 合并:由于分治法的思想,每个子数组都是有序的。当所有递归调用返回时,数组将被整体排序。
时间复杂度:快速排序的时间复杂度为 O(n log n),其中 n 是数组的大小。快速排序是一个原地排序算法,它在空间复杂度上通常为 O(log n)。然而,在最坏的情况下,快速排序的时间复杂度可能达到 O(n^2),这通常发生在选择的基准元素不恰当的情况下。为了改进这一点,通常会采用随机选择基准元素的方法。
代码演示:
#include <stdio.h>
int buf[] = {49, 38, 65, 97, 76, 13, 27, 49};
//用第一个元素将待排序的序列划分成左右两个部分
int Paritition(int *buf,int low,int high){
int pivot = buf[low];//第一个元素作为枢轴
while(low < high){//用low和hight搜索枢轴的最终位置
while(low < high && buf[high] >= pivot)
high--;
buf[low] = buf[high];//比枢轴小的元素移到左端
while(low < high && buf[low] <= pivot)
low++;
buf[high] = buf[low];//比枢轴大的元素移到右端
}
buf[low] = pivot;//枢轴元素存放到最终位置
return low;//返回枢轴的最终位置
}
void QuickSort(int *buf,int low, int high){
if(low < high){//递归算法退出的条件
int pivotpos = Paritition(buf, low, high);//划分
QuickSort(buf, low, pivotpos - 1);//划分左子表
QuickSort(buf, pivotpos + 1, high);//划分右子表
}
}
int main(void){
int i,j = 0;
for(i = 0; i < sizeof(buf)/sizeof(int);i++){
printf("排序前buf[%d] = %d\n",i,buf[i]);
}
QuickSort(buf, 0 ,sizeof(buf)/sizeof(int) - 1);
printf("-------------------------------------\n");
for(j = 0; j < sizeof(buf)/sizeof(int);j++){
printf("排序后buf[%d] = %d\n",j,buf[j]);
}
return 0;
}
结果:
- 补充:分治法的核心思想:
分治法(Divide and Conquer)是一种解决问题的算法设计策略,它将一个大问题划分成一些规模较小的子问题,然后递归地解决这些子问题,最后将它们的解合并起来,得到原问题的解。这个策略通常包括以下三个步骤:
1. 分解(Divide):将原问题分解为若干个规模较小且相互独立的子问题。这一步骤通常涉及到问题的递归划分。
2. 解决(Conquer):递归地解决每个子问题。当子问题的规模足够小时(通常定义一个基本情况),直接求解。
3.合并(Combine):将子问题的解合并成原问题的解。这一步骤通常涉及到合并排序过程中的合并步骤。
分治法的经典例子之一就是快速排序算法,它通过选择一个基准元素,将数组分成两个子数组,然后递归地对这两个子数组进行排序,最后合并子数组,完成整个排序过程。这里的分解即是选择基准元素,解决是递归地对子数组进行排序,合并是将子数组合并。
分治法的优点在于它将一个大问题分解成若干个规模较小的子问题,这样可以降低解决问题的复杂度。然而,适用分治法的问题通常需要满足子问题之间相互独立的条件,以确保递归求解子问题时不会发生重复计算。