【数据结构】快速排序递归实现和循环实现三种方法挖坑法、前后指针法、交换法

快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将 待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
我以为,此函数需要主体函数分隔函数

1.主体函数

这个函数要做的事是,先得到一个数组,然后通过分隔函数得到分隔下标,将数组分为两份,然后继续分隔左半部分和右半部分,直到数组剩下一个数,此时数组已经有序。

void QuickSort(int* array,int left,int right)
{
	int div;
	if(left<right)
	{
   		div = partion_3(array,left,right);     //进入分隔函数,返回分隔下标
   		QuickSort(array,left,div);
   		QuickSort(array,div+1,right);
	}
}

上面的是递归版本的,还可以利用栈将递归改为循环:

void QuickSort(int* array,int left,int right)
{
 	int div;
	int _left,_right;
 	DATA_TYPE region;
 	Stack ps;   			//创建栈
 	StackInit(&ps);			//初始化栈
	div = partion_3(array,left,right);		//得到第一次分隔的下标
 	region.left = div+1;
	region.right = right;
	StackPush(&ps,region);		//将分隔之后的右边数组首坐标和末坐标压入栈
	region.left = left;
	region.right = div;
	StackPush(&ps,region); 		//将分隔之后的左边数组首坐标和末坐标压入栈
 	while(!IsNull(&ps))
	 {
 		 _left = StackTop(&ps).left;
 		 _right = StackTop(&ps).right;
 		 div = partion_3(array,StackTop(&ps).left,StackTop(&ps).right);
 		 //栈顶是左边的数组,先分隔左边的,记录分隔下标
 		 StackPop(&ps);	//将之前已经分隔过的左边下标出栈
	 	 if(div+2<_right)      //如果新分隔后右边的数组大于2,就继续压入
	  	{
 			  region.left = div+1;
  			 region.right = _right;
	  		 StackPush(&ps,region);
	 	 }
	 	 if(_left+1<div)	//如果新分隔后左边的数组大于2,就继续压入
		  {
	 		  region.left = _left;
 			  region.right = div;
 			  StackPush(&ps,region);
	 	 }
	 }
}

2.分隔函数

分隔函数就是将传入的数组,以数组中某个数作为基准值,将小于此基准值的放在基准值左边,大于基准值的放在基准值右边。这样数组就被分为两份,然后这个函数返回基准值的下标位置。分割函数有三种实现方法:

Ⅰ、交换法

这个方法就是将数组中最后一个数作为基准值,然后将数组的0下标处标记为begin,将数组中倒数第二个数的下标位置标记为end,从begin开始往后找,找如果找到一个大于基准值的数,就停下。从end开始往前找,找如果找到一个小于等于基准值的数,就停下。然后将两个数位置进交换,begin往后走,end往前走。重复上述操作,直到end和begin相等,说明已经交换完成。最后将end位置处的数和基准值比较,如果大于基准值,就和基准值交换,如果小于等于基准值,就将end+1处的值和基准值交换。
在这里插入图片描述
代码实现如下:

int partion_3(int* array,int left,int right)  //交换法
{
 int div;
 int end = right-2;
 int begin = left;
 div  = array[right-1];
 while(begin < end)
 {
  while(array[begin] <= div && begin < end)
   begin++;
  while(array[end] > div && begin < end)
   end--;
  if(begin < end)
   Swap(&array[end],&array[begin]);
 }
 if(array[end] >= div)
 {
  Swap(&array[end],&array[right-1]);
  return end;
 }
 else
 {
  Swap(&array[end+1],&array[right-1]);
  return end+1;
 }
}

Ⅱ、挖坑法

将数组中最后一个数最为基准值,然后保存起来,设置begin=0下标,end是倒数第一个数的下标,然后从左边开始找比基准值大的数,如果找到就把它直接放在end的位置,因为end的数已经保存起来。如果begin处的数放在了end处那么,begin这个位置就是一个坑,因为它已经保存到end处了。然后end往前找,找比基准值小的数,如果找到直接放到begin的那个坑的地方,end又成为了新坑,就这样重复操作,直到end和begin相等,把最开始的基准值赋值给end。
在这里插入图片描述
代码实现如下:

int partion_2(int* array,int left,int right)  //挖坑法
{
 int div = array[right-1];
 int end = right-1;
 int begin = left;
 while(begin < end)
 {
  while(array[begin] <= div && begin < end)
   begin++;
  if(begin >= end)
   break;
  array[end--] = array[begin];
  while(array[end] > div && begin < end )
   end--;
  if(begin >= end)
   break;
  array[begin++] = array[end];
 }
 array[begin] = div;
 return begin;
}

Ⅲ、前后指针法

设置基准值为数组最后一个数,然后定义一个当前下标cur,和cur的前一个下标pre。然后让cur位置的数去判断是否大于基准值,如果大于cur就往后走,pre不动,如果小于等于,再判断pre+1位置处的数是否大于基准值,如果大于,就让pre+1位置的数和cur位置的数交换,pre往后走一下,cur往后走一下,如果不大于,pre往后走一下,cur往后走一下,不交换。就只让重复操作,直到cur走到最后一个数的位置,最后将pre+1位置的数和基准值交换一下。
在这里插入图片描述
接上图

在这里插入图片描述
代码实现如下:

int partion_1(int* array,int left,int right)  //前后指针法
{
 int div = right-1;
 int pre = left-1;
 int cur = left;
 while(cur<right-1)
 {
  if(array[cur]>array[div])
   cur++;
  else
  {
   if(array[++pre]>array[div])
    Swap(&array[++pre],&array[cur++]);
   else
   {
    pre++;
    cur++;
   }
  }
 }
 Swap(&array[++pre],&array[div]);
 return pre;
}

猜你喜欢

转载自blog.csdn.net/qq_39487033/article/details/83246525