之前貌似把冒泡排序、插入排序、选择排序弄懂了(其实现在又忘了……),所以膨胀了,又想要弄清楚快速排序……
花了至少两天,大概弄懂了吧?
这个算法目前来看大概有四种变形?
方法名称我自己瞎起的,反正也没人打我。
只看自定义函数的部分就行。
大小元素交换法
参考博客
快速排序(QuickSort)算法介绍 这个博客我只看了前两个部分
快速排序(过程图解)这个文章转载文满CSDN全是,特意找了个图还没崩的……
恕我直言,上面两篇博客给出的代码都有BUG。
第一篇流程设计有缺陷,如下所述:
给出的样例不能涵盖所有情况。
按照这个样例输入排序,不存在某一次调用,使传入元素仅为两个,且大数在后,小数在前。
令i=left+1,则会使两个元素交换位置,排序错误。
受第一篇博客影响写出的代码,写得过于复杂了。
/*快速排序——不稳定*/
#include<stdio.h>
#define num 4
void quicksort(int arr[],int left,int right);
int main(void)
{
int i;
// int array[num]={21,25,49,25,16,8,9,1,4,22,8548,453,215,4452,21,12,5,4,20,453};
int array[num]={1,3,2,3};
for(i=0;i<num;i++){
printf("%d ",array[i]);
}
printf("\n");
for(i=0;i<10;i++)
printf("**\t");
printf("\n");
quicksort(array,0,num-1);
for(i=0;i<num;i++){
printf("%d ",array[i]);
}
return 0;
}
void quicksort(int arr[],int left,int right)
{
if(left>=right)
return;//不加上这个语句无法结束?
int std=arr[left],i=left+1,j=right,temp;
while(i<j){
while(arr[j]>=std&&i<j){//两个内层循环至少一个有"=",否则若存在相等元素,则陷入死循环
j--;
}//j负责挑出遇见的小于std的元素
while(arr[i]<=std&&i<j){
i++;
}//i负责挑出遇见的大于std的元素
temp=arr[j];//交换arr[i]与arr[j]
arr[j]=arr[i];
arr[i]=temp;
}//最终i与j相遇,结束循环,i=j,j左面元素不大于std,右面元素不小于std
// for(i=0;i<num;i++)//为了方便调试,每次调用都打印数组
// printf("%d ",arr[i]);
// printf("\n");
if(std>arr[j]){//当(任意某一次调用)只有两个元素时,不会进行上述循环,所以要进行判断
arr[left]=arr[j];//这种情况是j不动,i递增,
arr[j]=std;
quicksort(arr,left,j-1);//递归调用自身
quicksort(arr,j+1,right);
}
else//这种情况是i不动,j递减,i左面数值都小于等于std,但 arr[i]>std,所以要用j-1与arr[left]交换
{
arr[left]=arr[j-1];
arr[j-1]=std;//标杆值转移到j-1
quicksort(arr,left,j-2);
quicksort(arr,j,right);
}
}
博客提供的代码错误之处在于:i与j相遇的数值并不一定≤std,详情请看注释。这个缺陷困扰了我好久?,经高人指点才发觉。
修改第二篇博客的函数:
第二篇自定义函数连数组都不传入没看懂是什么操作,改完之后运行倒没什么毛病……
void quicksort(int arr[],int left, int right) {
int i, j, t, temp;
if(left > right)
return;
temp = arr[left]; //temp中存的就是基准数
i = left;
j = right;
while(i < j) { //顺序很重要,要先从右边开始找
while(a[j] >= temp && i < j)
j--;
while(a[i] <= temp && i < j)//再找左边的
i++;
if(i < j)//交换两个数在数组中的位置
{
t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
arr[left] = arr[i];//最终将基准数归位
arr[i] = temp;
quicksort(arr,left, i-1);//继续处理左边的,这里是一个递归的过程
quicksort(arr,i+1, right);//继续处理右边的 ,这里是一个递归的过程
}
这篇是我在敲这篇博文时才发现的,目前运行并没有发现什么BUG,以后抽出时间回过头来研究研究具体流程。
元素循环赋值法
参考博客
7中排序算法学习总结(图解+程序代码)
这篇博客给出的思路我一开始没看懂?,回过头来才理解了一些
#include<stdio.h>
#define num 20
void quicksort(int arr[],int left,int right);
int main(void)
{
int i;
int array[num]={21,25,49,25,16,8,9,1,4,22,8548,453,215,4452,21,12,5,4,20,453};
// int array[num]={1,3,2,3};
for(i=0;i<num;i++){
printf("%d ",array[i]);
}
printf("\n");
for(i=0;i<10;i++)
printf("**\t");
printf("\n");
quicksort(array,0,num-1);
for(i=0;i<num;i++){
printf("%d ",array[i]);
}
return 0;
}
void quicksort(int arr[],int left,int right)
{
if(left>=right)
return;
int std=arr[left],i=left,j=right;//std为标杆值
while(i<j){
while(arr[j]>=std&&i<j)
j--;
arr[i]=arr[j];//将小于std的元素放置到左面
while(arr[i]<=std&&i<j)
i++;
arr[j]=arr[i];//将大于std的元素放置到右面
}//i=j时,结束循环,此时j左面元素不大于std,右面元素不大于std
arr[j]=std;//最后将标杆值赋给"空出"的位置
quicksort(arr,left,j-1);
quicksort(arr,j+1,right);
}
大致流程:一开始把最左值作为标杆值,"抽出"此值赋给std,此时i=left,j=right。arr[i]=arr[left],这个位置变相空出。
进行循环与内层循环。
内层循环①结束,j找到一个小于标杆值的值,并且把这个值赋给arr[i](为了把小于标杆的值都放在左面),此时arr[j]变相空出。
内层循环②结束,i找到一个大于标杆值的值,并且把这个值赋给arr[j],把大于标杆值的值都放在右面,,此时arr[i]变相空出。
进行以上循环,直至i=j,此时j左面元素不大于std,右面元素不大于std。
将std赋给arr[j],标杆重回数组,作为分割线。递归调用自身,完成排序。
交替标杆交换法
几种排序方法详解(选择排序、冒泡排序、插入排序、快速排序)
其实就是第二种的变形,使标杆值交替与j从右往左遇见的小值/i从左往右遇见的大值交换,直至i=j,进行下一次调用。
与第二种写法区别在于,这个程序的标准值始终在数组里。
那我就不多想了ε=ε=ε=( ̄▽ ̄)
高人给出的写法
void quicksort1(int *pt,int left,int right)
{
if(left>=right)
return;
int std=pt[left];
int i=left,j=right;
while(i<j){
while((pt[j]>=std)&&(i<j))
j--;
if(i!=j)
pt[i++]=pt[j];
while((pt[i]<std&&(i<j)))
i++;
if(i!=j)
pt[j--]=pt[i];
}
pt[j]=std;
quicksort1(pt,left,j-1);
quicksort1(pt,j+1,right);
}
应该是第二种写法的变形,这是以写诗的姿态来写代码吗?
♪(^∇ ^*)o我感觉相当优雅
头疼
2019年2月15日23点52分