基本概念:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序数列。
算法概念:
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,
也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j--), 找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]的值交换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
示范代码:
void sort(int *a, int left, int right)
{
if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return ;
}
/*在左边大于或等于右边的情况下,赋值即可*/
int i = left; //将左边的值赋给i
int j = right; //将右边的值赋给j
int key = a[left]; //将最左边的值赋给key
/*在左边小于右边的情况下,进入循环*/
while(i < j) /*控制在当组内寻找一遍*/
{
while(i < j && key <= a[j])
/*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/
{
j--;/*向前寻找*/
}
a[i] = a[j];
/*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
a[left],那么就是给key)*/
while(i < j && key >= a[i])
/*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
{
i++;
}
a[j] = a[i];
}
a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
sort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
sort(a, i + 1, right);/*用同样的方式对分出来的右边的小组进行同上的做法*/
/*当然最后可能会出现很多分左右,直到每一组的i = j 为止*/
}
个人理解与思路:
实现快速排序,我认为需要设置一个位于中间的基准值,需要将小于这个基准值的元素均放在基准值的左边,将大于这个基准值的元素放在右边。通过循环比较,如果左边第一个元素小于基准值,则下标自增,继续进行比较,通过比较左边第一个元素的值是否大于基准值,如果大与基准值,将这个元素的值和下标与基准值交换,也就是说这个元素将会作为一个新的基准值,然后接着循环比较;右边比较进行反向思维,如果右边最后一个元素的值大于基准值,则下标自减,如果最后一个元素的值要小于基准值,则将最后一个元素的值和下标与基准值进行交换。当左右双方的下标发生重合,则需要将基准值重新放到下标重合的这个位置,然后在保证基准值的两侧至少有一个元素时,进行递归运算,重复循环操作,直到排序全部完成。
这里运用到了一个概念,就是递归。
函数间允许相互调用,也可以自己调用自己。函数如果在函数内直接或间接地自己调用自己就称为递归方法。
/*当基准值下标减去最左边下标大于1时,说明p下标不是0,左边存在至少一个元素,此时使用递归,传参为当前的arr数组,最左边下标left,最右边下标right减1,也就是说接下递归,将会省略数组内最右边的元素*/
if (p - left > 1) {
sort(arr, left, p - 1);
}
/*当最右边下标减去基准值下标大于1时,说明right下标不为0,右边至少存在一个元素,此时使用递归,传参为当前的arr数组,最左边下标left加1,也就是说下次递归将会省略掉数组内最左边的元素,并且传递最右边下标right作为参数*/
if (right - p > 1) {
sort(arr,p + 1, right);
}
c代码:
#include <stdlib.h>
/*实现快速排序算法的sort函数*/
int sort(int arr[], int left, int right) {
/*计算中间元素的下标*/
int p = (left + right) / 2;
/*将中间元素作为基本值,单独声明一个变量进行保存*/
int pivot = arr[p];
int i=0, j=0;
/*将所有小于基准值的元素放在左边,大于基准值的元素放在右边*/
for (i = left, j = right; i < j;) {
/*当出现左边元素小于基准值,
并且元素下标小于基准值的下标,
则继续下一个左边元素与基准值进行比较*/
while (arr[i] < pivot && i < p) {
i++;
}
/*如果左边元素大于基准值,
并且下标小于基准值下标*/
if (i < p) {
/*将i指向的这个元素的值赋给p指向的位置*/
arr[p] = arr[i];
/*并且将i作为p的新下标*/
p = i;
}
/*当出现右边元素大于基准值,
并且元素下标大于基准值,
则继续上一个右边元素与基准值进行比较*/
while (arr[j] > pivot && j > p) {
j--;
}
/*如果右边元素小于基准值,
并且下标大于基准值*/
if (j > p) {
/*将j指向的这个元素的值赋给p指向的位置*/
arr[p] = arr[j];
/*并且将j作为p的新下标*/
p = j;
}
}
/*i和j重合,将基准值放在重合的位置*/
arr[p] = pivot;
/*使用递归重复以上过程,
在保证基准值左右两侧存在至少一个元素的时候,
才需要递归,
否则不需要*/
if (p - left > 1) {
sort(arr, left, p - 1);
}
if (right - p > 1) {
sort(arr,p + 1, right);
}
}
/*主函数*/
int main() {
/*声明数组变量*/
int arr[9] = {20, 33, 67, 91, 1, 59, 42, 25, 28, 96};
/*被调用函数sort声明*/
sort(arr, 0, 9);
/*声明一个循环打印的变量*/
int i = 0;
/*用开始从第一个数字打印,因为数组中有10个数,所以i不能大于9*/
for (i=0;i<9;i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
运行结果:
1 20 25 28 33 42 59 67 91