2019.2.15想了两天的快速排序……

之前貌似把冒泡排序、插入排序、选择排序弄懂了(其实现在又忘了……),所以膨胀了,又想要弄清楚快速排序……
花了至少两天,大概弄懂了吧?
这个算法目前来看大概有四种变形?
方法名称我自己瞎起的,反正也没人打我。
只看自定义函数的部分就行。

大小元素交换法

参考博客
快速排序(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分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值