其他分享
首页 > 其他分享> > 快速排序详解---C语言实现

快速排序详解---C语言实现

作者:互联网

5 快速排序

核心思想:初始时选择一个标志位(一般是第一个),让其余各位的值与该标志位的值相比较,刚开始从数组尾部开始选择小于该标志位的值放于前面,然后再从前面选择大于该标志位的值放于后面,如此循环,最终可以将数组以该标志位分为两部分,而后在对前后两部分循环操作。

实现过程(改进前):快速排序通俗点讲就是挖坑填补法,取一个值来将数组分成两部分,左侧都比所取标志的值小,右侧都比所取的标志值大,具体看下;
初始时,取数组的第一个元素为坑(分组标志),设置一个变量标记它,有int nValue = arr[0];
arr[0]标记后,则arr[0]的位置空了出来,由于我们排序是准备从小到大排序,即想所有相对小的在前面,所有相对大的在后面;因此我们就利用这个思想,初始时arr[0]空着,因此我们从数组最后一个位置,由后向前找一个比标志值nValue小的数将arr[0]处的位置填上,填上之后,数组后面位置又空出来一个位置,然后我们从数组前面找一个大于标志值nValue得数放于数值后面的空位,我们可以看出此时我们需要两个变量来标记数组前面后后面的位置,重复此操作,当前面标记等于后面标记时,说明第一次分组已经完成,而且凉后变量相等的位置就是nValue的位置,插入nValue,由此数组将被分成两部分,之后再对前后两个部分进行重复操作,详细过程见下图:
1选取标志变量nValue = arr[0],front标记前面位置,back标记后面位置,由于arr[0]为选取的标志,故front从第二个位置开始,如下所示:
在这里插入图片描述

初始时,arr[0]位置空着,因此从back位置由后向前查找比nValue小的数放前面,6 < 8,即6放于front位置,front后移如下图所示:
在这里插入图片描述back处的元素已经放于前面,因此back处此时的位置空出来,从前面front处往后查找比nValue值大的元素放于back位置,然后back往前移动一个位置,由于front处的值为3,3<8,因此front后移,·9大于8,将9插入back位置,如下所示:

在这里插入图片描述9插入back处位置后,9的位置空了出来,因此从back位置由后往前找比nValue值小的放于front处,20>8back往前移动,5<8,插入front位置,front后移,如下图所示:

在这里插入图片描述back处的值插入到了前面,back处空了出来,因此从front位置往后查找比nValue大的值放于back位置,15>8,直接插入,back前移,如下图所示:
在这里插入图片描述
15值插入了后面,因此front处的位置空了出来,由后往前查找比nValue值小的放于front位置,1<8,直接插入,然后front后移,如下如所示:
在这里插入图片描述1插入了前面的位置,因此1处的位置空了出来,从front处查找比nValue值大的元素放于back位置,然后back前移,由于29>8,直接插入,如下图所示:
在这里插入图片描述
从上图,我们可以看出,最终frontback指向了同一个地方,即循环终止条件,也就是nValue所要插入的位置,将nValue值插入,如下图所示:

在这里插入图片描述总结 :从上述过程,可以看出,当遍历一遍数组后,原本无序的数组被依据nValue大小分成了两部分,左侧的值都是小于nValue,右侧的值都是大于nValue,另外,当front = back时循环终止,且此位置就是nValue值要插入的位置。另外,我们对数组进行遍历一次就将数组分成了两部分,那么我们接下来对这两部分执行相同的操作,我们很容易想到可以使用递归实现,拆分数组如下图所示:
在这里插入图片描述代码实现

#include <stdio.h>

void QuickSort(int arr[],int nBegin,int nEnd)
{
	if(arr == NULL || nBegin >= nEnd) return;

	int front = nBegin;
	int back = nEnd;
	
	printf("nBegin = %d  nEnd = %d\n",nBegin,nEnd);
	//选择标志
	int nValue = arr[nBegin];

	while(front != back)
	{
		//由后往前查找小于nValue的值插入
		while(arr[back] > nValue && back > front)
		{
			back--;
		}
		if(front < back)
		{
			arr[front] = arr[back];
			front++;
		}
		else
			break;
		//由前往后查找大于nValue的值插入
		while(arr[front] < nValue && front < back)
		{
			front++;
		}
		if(front < back)
		{
		arr[back] = arr[front];
		back--;
		}
		else
			break;
	}
	//插入标志值
	arr[front] = nValue;
	//左侧数组
	QuickSort(arr,nBegin,back-1);
	//右侧数组
	QuickSort(arr,front+1,nEnd);
}
void Print(int arr[],int nLen)
{
	if(nLen <= 0) return;

	for(int i = 0;i < nLen;i++)
	{
		printf("%d\t",arr[i]);
	}
	printf("\n");
}
int main()
{


	int arr[] = {1,78,2,54,56,2,78,12,4555,8};
	QuickSort(arr,0,sizeof(arr)/sizeof(arr[0])-1);
	
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	return 0;
}

不过,从上面数组可以看出,当选取的标志元素为数组中最小的时,并不能有效的将数组分割,因此对于快速排序标志值得选取决定了排序效率的高低,标志值得选取这里不再详述,感兴趣的可以自己搜索一下如何选取快速排序的标志值。

上面是改进前的快速排序实现,由前后两个变量来标记位置,下面我们来看一下只使用一个标记变量如何来完成快速排序,具体实现如下。

改进后的核心思想:不从两个方向移动,只从最前面移动,此时标志比较的值基本选最后一个,从前移动时,设置一个标记位来记录小于标志值得最近的一个位置,如果值大于标志位,不更新位置,继续移动,如果值小于标志位,则交换此元素值与标志位的后一个元素值,更新标志位为后一个元素位置,如此循环。
默认选取标志位数组的最后一个元素:int nValue = arr[nLen-1];(nLen 表示数组的长度)
实现过程:取初始标记标量的下标为 int nFlag = -1(选取-1的目的是防止数组第一个元素大于nValue时不发生交换,因为每次交换时都是标志后面的值交换),见下图:
在这里插入图片描述

如上图所示,nFlag = -1, nValue = 6nFlag之后元素arr[0]的值8 > 6,不发生交换,i++,向后移动一位,使得arr[1]nValue比较,由于3 < 6,即将arr[1]nFlag之后的一个元素交换,然后更新标志位为nFlag++i++(注:只有发生交换才更新nFlag的位置)如下图所示:
在这里插入图片描述

如上所示,此时i =2,由于arr[2] = 9,并且9 > 6,因此,i++,此时i = 3arr[3] = 15,并且15 > 6,重复上述操作继续移动,直到i = 5,时,此时arr[5]=11 < 6,因此将arr[5]nFlag后面一个位置的元素交换,交换后更新nFlag,并且移动arr[5],如下图所示:
在这里插入图片描述

如上图所示,此时i = 6,并且arr[6] = 55 < 6,故交换移动,重复上述操作,如下图所示:
在这里插入图片描述
如上图所示,此时i = 7,并且arr[7] = 2020 > 6,因此不发生交换,然后继续移动arr[i],此时arr[i],已经移动到数组尾部,因此最后将数组最后的值与nFlag后面的值交换,如下图所示:

在这里插入图片描述后续操作为重复上述操作…
代码如下

#include <stdio.h>

void Print(int arr[],int nLen);
void Swap(int *a,int *b)
{
	*a = *a ^ *b;
	*b = *a ^ *b;
	*a = *a ^ *b;
}
void QuickSort(int arr[],int nBegin,int nEnd)
{
	if(nBegin >= nEnd) return;
	//定义比较的标准值
	int nValue = arr[nEnd];
	int nFlag = nBegin-1;//下标

	//分组
	int i;
	for(i = nBegin;i < nEnd;i++)
	{
		//查找第一个小于nValue的
		while(arr[i] > nValue && i < nEnd)
			{
				i++;//arr移动
			}
			if(i == nEnd)
				break;
			//交换
			if(arr[nFlag+1] != arr[i])
			Swap(&arr[nFlag+1],&arr[i]);
			nFlag++;
	}
	//数组末尾交换

	if(arr[nFlag+1] != arr[i])
	Swap(&arr[nFlag+1],&arr[i]);
	//Print(arr,nEnd+1);
	//左
	QuickSort(arr,nBegin,nFlag);
	//右
	QuickSort(arr,nFlag+2,nEnd);
}

void Print(int arr[],int nLen)
{
	if(nLen <= 0) return;

	for(int i = 0;i < nLen;i++)
	{
		printf("%d\t",arr[i]);
	}
	printf("\n");
}
int main()
{


	int arr[] = {1,78,2,54,56,2,78,12,4555,8};
	QuickSort(arr,0,sizeof(arr)/sizeof(arr[0])-1);
	
	Print(arr,sizeof(arr)/sizeof(arr[0]));
	return 0;
}

最好时间复杂度:O(nlogn)

最坏时间复杂度:O(n²)

平均时间复杂度:O( nlogn)

空间复杂度:O(logn)~O(n)

稳定性:不稳定

适用场合:数组量大且较无序

标签:arr,nValue,int,back,C语言,---,详解,数组,front
来源: https://blog.csdn.net/qq_43450920/article/details/118074429