史上最详细的快速排序算法
最近学了快速排序算法,在csdn上找了很多篇博客,虽然代码可以执行正确,但是解释却有点“官方”,有很多细节,很多需要注意的地方并没有写到,故此,我写了这篇博客,看了我这篇博客,你绝对会恍然大悟。
先给大家看看清楚明了的快速排序的动态图。
在详细讲解之前,先来看看代码,已检验过,无任何问题。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//快速排序算法
void swap(int* p1, int* p2)
{
int temp=*p1;
*p1 = *p2;
*p2 = temp;
}
void print(int* str,int length)
{
static int count = 0;
count++;
printf("第%d次交换后的结果 ", count);
for (int i = 0; i < length;i++)
printf("%4d", str[i]);
printf("\n");
}
void quicksort(int* str, int left, int right)//将数据集合左值作为基准值(右值也是一样的)
{
int i, j;//i递增,j递减,每次调用这个函数,都会向堆栈中压入两个变量
if (left < right)//这就是递归的终止条件,为什么
{
i = left;
j = right+1;
while (1)//是将比基准值大的和小的数据集合分离开来
{
do{
i++;
} while (!(str[i]>str[left])||i==right);//不是& 而是||
do{
j--;
} while (!(str[j] < str[left]||j==left));//left是基准值,是不拿来比较的
if (i < j)
{
swap(&str[i], &str[j]);
print(str, 10);
}
else
break; // 最终都会出现i>=j
}//为什么这个模块能够将数据分离开来,这是实现快速排序的最重要的一步
//执行完while语句后,i与j的值之间有什么关系
swap(&str[left], &str[j]);//str[j]其实就是交换基准值小的数据集合的最右边的值
quicksort(str,left,j-1);
quicksort(str,j+1,right);
}
}
void main()
{
int str[10] = { 2,3,4,8,9,7,5,6,1,0};
quicksort(str,0,9);
printf("the result of quicksort is:");
for (int i = 0; i < 10; i++)
printf("%4d", str[i]);
system("pause");
}
在上述的代码块中,我们只讲解最关键的quicksort函数。
快速排序的思想是:
算法步骤:
1 从数列中挑出一个元素,称为 “基准”(pivot),
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。虽然一直递归下去,但是这个算法总会退出。
先来看看quicksort函数的主要内容的思维导图。
1.大while语句
while大循环模块的作用是是将比基准值大的和小的数据集合分离开来。
大家联想,有一个数据集合,再有一个基准值,是不是一定可以将这个数据集合分为比基准值小的和比基准值大的数据集合呢?答案是肯定的那么我们怎么设计一个比较好的语句将这个思想给实现呢,就是这个while语句。while语句实现的功能就是:从左边选取一个比基准值大的数据,从右边选取一个比基准值小的数据,如果i<j 那么就将数据进行交换,这个语句的终止条件是i>=j
经过我多次的验证,得到的结论就是:
但while循环语句执行完后, i=j+1。
2.递归以及终止条件
先来看看我画的一个不好看的图,忍耐一下。
while语句后面的两个quicksort就是实现递归的语句。递归主要的过程就是本片文章的那个动态图,就不用多说了。
最后,我们来捋捋思路。
1.定义一个乱序的数组。
2.调用quicksort函数。
3.执行while语句,以实现将数据集合分为比基准值小和比基准值大的两个数据集合。
4.将str[left]于str[j]进行交换,将基准值插入两个数据集合之间,并且再次将str[left]作为新的数据集合的基准值。
5.不断递归,最终就实现了将无序数组变为有序数组
温馨提示(看懂了,你就都懂了):
1.有一个基准值,就一定可以把数据集合分为一个比基准值大和比基准值小的两个数据集合(相等的不管它)我们假设在这两个数据集合之间有一个分界线(虚拟的)。
2.每次while循环结束后str【j】是比基准值小的数据集合最右边的值。
str【i】是比基准值大的数据集合最左边的值。
3.基准值是用来比较的,但是基准值所对应的数组序号没有用途。
4.这个算法是用left作为基准值,right也可以,left的值始终为0。
5.right是不可能小于left,最终的终止条件一定是left==right。(结合i=j+1考虑)
6.right之所以要加一是do while语句是执行再判断。
7.quicksort函数的形参是将数组最左端和最右端的数据所对应的下标。
8.while语句后面的swap(&str[left], &str[j]);语句是将str[left]于str[j],而不是str[left]与str[i]交换。