《大话数据结构》第九章 排序

第九章 排序

  高效率的内排序算法应该是尽可能少的进行关键字比较和尽可能少的进行记录移动。**排序分内排序和外排序,内排序是在排序整个过程中,要排序的记录全部放在内存中。外排序是由于排序的记录太多,多到无法再内存中存放,整个排序过程需要在内外存之间交换数据才能。**本书主要是介绍内排序,外排序不做了解。
  希尔排序相当于直接插入排序的升级版,堆排序相当于简单选择排序算法的升级算法。快速排序是我们认为的最慢的冒泡排序的升级版。
在这里插入图片描述
  适用所有排序算法的结构:

#define MAXSIZE 10000  /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
	int r[MAXSIZE+1];	/* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
	int length;			/* 用于记录顺序表的长度 */
}SqList;

  数组两元素常用的交换:

/* 交换L中数组r的下标为i和j的值 */
void swap(SqList *L,int i,int j) 
{ 
	int temp=L->r[i]; 
	L->r[i]=L->r[j]; 
	L->r[j]=temp; 
}

内排序

插入类排序

直接插入排序

  直接插入排序基本操作是将一个记录插入到已经排好序的有序表,从而得到一个新的、记录数增1的有序表。直接插入排序过程类似摸到扑克牌理牌的过程。核心思想在于,外循环从2到length,遍历查找当前是否有r[i]<r[i - 1]处,若有,显然有问题(理想是每一个a[i-1]<a[i]),找到i的位置。开始内循环,将r[i]赋值给r[0]作为哨兵,j=i - 1,j在内循环自减过程中不断r[j+1] = r[j],将r[j]后移,直到r[0]>=r[j]时,即找到r[0]的位置,r[j+1] = r[0]。
代码:

/* 对顺序表L作直接插入排序 */
void InsertSort(SqList *L)
{ 
	int i,j;
	for(i=2;i<=L->length;i++)
	{
		if (L->r[i]<L->r[i-1]) /* 需将L->r[i]插入有序子表 */
		{
			L->r[0]=L->r[i]; /* 设置哨兵 */
			for(j=i-1;L->r[j]>L->r[0];j--)
				L->r[j+1]=L->r[j]; /* 记录后移 */
			L->r[j+1]=L->r[0]; /* 插入到正确位置 */
		}
	}
}

  例如{0,5,3,4,6,2}的排序,
        在这里插入图片描述
  外循环先找出不满足r[i-1]<r[i]情况,显然此时i = 2时不满足。内循环,哨兵r[0] = r[3],j = i -1 = 1,r[j] = 5>r[0],r[j+1] = r[j],j–,r[j]>r[0]条件不满足,退出内循环,r[j+1] = r[0]。
        在这里插入图片描述
  外循环i = 2执行结束,i依次增加。
        在这里插入图片描述

希尔排序

  在希尔排序出现之前,计算机学术界都认为排序算法的复杂度不可能突破O(n2),而希尔排序最终以n3/2,n的2分之3次方,突破了固定的思维。希尔排序关键不是将顺序表随便分组后各自排序,而是将相隔某个“增量”的记录组成一个子序列,实现跳跃式移动,使排序的效率提高。

/* 对顺序表L作希尔排序 */
void ShellSort(SqList *L)
{
	int i,j,k=0;
	int increment=L->length;
	do
	{
		increment=increment/3+1;/* 增量序列 */
		for(i=increment+1;i<=L->length;i++)
		{
			if (L->r[i]<L->r[i-increment])/*  需将L->r[i]插入有序增量子表 */ 
			{ 
				L->r[0]=L->r[i]; /*  暂存在L->r[0] */
				for(j=i-increment;j>0 && L->r[0]<L->r[j];j-=increment)
					L->r[j+increment]=L->r[j]; /*  记录后移,查找插入位置 */
				L->r[j+increment]=L->r[0]; /*  插入 */
			}
		}
		printf("	第%d趟排序结果: ",++k);
		print(*L);
	}
	while(increment>1);

}

  以顺序列{0,9,1,5,8,3,7,4,6,2}为例演示希尔排序算法执行过程。第一遍do循环,increment = 4,for循环中i从5开始遍历到length,比较r[i-increment]和r[i]大小,若大的在前面,交换位置。
        在这里插入图片描述
  i依次增加,i = 6,比较r[i - increment]和r[i]大小,若大的在前面,交换位置。
        在这里插入图片描述
  i = 7时,交换。
        在这里插入图片描述
  i 继续增加,i = 9时,交换。
         在这里插入图片描述
  第一轮increment = 4结束后,increment = increment / 3 +1 = 2,此时开始第二轮的for循环。
        在这里插入图片描述
       在这里插入图片描述
  最后一轮的increment = 1时。
       在这里插入图片描述
希尔排序难点在于如何选取一个好的增量,并且需要注意增量序列最后一个增量值必须是1才行。
       在这里插入图片描述

选择类排序

简单选择排序算法

  简单选择排序,所有的排序外循环肯定都是需要i从1到length,关键在于内循环,选择排序重在内循环中j从i到length过程中,关键在于选择出内循环的I到length中的最小值,如何选择,每次内循环将a[min] min = i+1一开始作为最小值,然后j从i+1到length中,若min的下标对应的值有比a[j]小,则min = j,这样每次内循环就能找出i+1到length之间最小值。选择排序最大的特点是交换移动数据次数少,但是比较的次数和冒泡排序相同,时间复杂度是O(n2),但是总的来说性能上优于冒泡排序

/* 对顺序表L作简单选择排序 */
void SelectSort(SqList *L)
{
	int i,j,min;
	for(i=1;i<L->length;i++)
	{ 
		min = i;						/* 将当前下标定义为最小值下标 */
		for (j = i+1;j<=L->length;j++)/* 循环之后的数据 */
        {
			if (L->r[min]>L->r[j])	/* 如果有小于当前最小值的关键字 */
                min = j;				/* 将此关键字的下标赋值给min */
        }
		if(i!=min)						/* 若min不等于i,说明找到最小值,交换 */
			swap(L,i,min);				/* 交换L->r[i]与L->r[min]的值 */
	}
}

  i = 1时的内循环,min = 2之后,内循环j从3到9都找不到a[j]比a[min]小,所以min是2。
        在这里插入图片描述
  i = 2时,内循环min最终是9,交换a[2]和a[min]
        在这里插入图片描述
  i = 3时,内循环min = 5,交换a[3]和a[min]
        在这里插入图片描述

堆排序

  堆在排序中指特殊的完全二叉树,分大顶堆和小顶堆。大顶堆:每个结点的值都大于或者等于其左右孩子结点的值。小顶堆:每个结点的值都小于或者等于其左右孩子结点的值。如下图,左边为大顶堆,右边为小顶堆。
在这里插入图片描述
  堆排序基本思想是,将待排序的序列构造成一个大顶堆,此时整个序列的最大值就是堆顶的根结点,把它与堆数组的末尾元素交换,那么末尾元素就是最大值,然后将剩余的n-1个序列重新构造成一个堆,这样会得到原来n个元素中的第二大的数,如此反复就能得到一个有序序列。例如图1是初始的大顶堆,90是最大值,将90与20(末尾元素互换),90成为了整个堆序列的最后一个元素,此时除了90以外要保证其他结点仍然满足大顶堆定义(所有结点都大于等于其子孩子),于是要将20先与80换,20再与50换。
在这里插入图片描述
  然后开始第二轮的堆顶根结点与末尾元素互换,重复图1、图2过程。
              在这里插入图片描述
  然而堆排序算法最关键是要解决两个问题:

  1. 如何由一个无序序列构建成一个堆?
  2. 在每次输出堆顶元素(最大值)后,如何调整剩余元素成为一个新的堆?

  将需要排序的序列构建成一个大顶堆方法:从下到上、从右到左,将每个非顶端结点(非叶结点)当做根结点,将其和其子树调整成大顶堆。思想类似公司挑选人才一样,先从基层选出最有能力、有代表的人选,再从中层选,最后这么一组序列的人中人上人必定是人才。下图中,只要保证将灰色的结点与其子树满足大顶堆关系,那么整个完全二叉树必定是大顶堆。从下到上、从右到左,依次调整。在正式排序时,第i次取顶重新构建堆需要O(logi)的时间,并且需要取n-1次记录,重新构建堆的时间复杂度O(nlogn),性能远远比冒泡、简单选择、直接插入的O(n2)好的多
               在这里插入图片描述
代码实现:传入的参数s是指需要调整的是哪个结点及其子树,m是总的结点数目。

/* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义 */
/* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */
void HeapAdjust(SqList *L,int s,int m)
{ 
	int temp,j;
	temp=L->r[s];
	for(j=2*s;j<=m;j*=2) /* 沿关键字较大的孩子结点向下筛选 */
	{
		if(j<m && L->r[j]<L->r[j+1])
			++j; /* j为关键字中较大的记录的下标 */
		if(temp>=L->r[j])
			break; /* rc应插入在位置s上 */
		L->r[s]=L->r[j];
		s=j;
	}
	L->r[s]=temp; /* 插入 */
}

第一次调用HeapAdjust过程如下:传入参数s = 4,m = 9
                在这里插入图片描述
  最后一次调整的是1结点:
              在这里插入图片描述
  接下来就是交换根结点(最大值)与末尾元素交换,交换后这次交换的末尾元素成为最大值。当然第二次交换,第二轮交换的末尾元素是第二大值(第一轮交换的最大值放在第一轮的末尾可以不用满足大顶堆)。


/*  对顺序表L进行堆排序 */
void HeapSort(SqList *L)
{
	int i;
	for(i=L->length/2;i>0;i--) /*  把L中的r构建成一个大根堆 */
		 HeapAdjust(L,i,L->length);

	for(i=L->length;i>1;i--)
	{ 
		 swap(L,1,i); /* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */
		 HeapAdjust(L,1,i-1); /*  将L->r[1..i-1]重新调整为大根堆 */
	}
}

分析:当i = 9时,交换20与90,当前的根结点20进行大顶堆的调整。
在这里插入图片描述
当i = 8时,交换30和80,进行大顶堆构建过程。此时序列变成{70,60,50,30,10,40,20,80,90}
在这里插入图片描述
最后结果:见图
在这里插入图片描述

交换类排序

冒泡排序

  冒泡排序是一种交换排序,基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序记录为止。假设我们待排序的关键字是{9,1,5,8,3,7,4,6,2},正宗的冒泡排序算法代码:注意j是从后往前循环,i是肯定需要从1到length都遍历,但是j从length后端到i

/* 对顺序表L作冒泡排序 */
void BubbleSort(SqList *L)
{ 
	int i,j;
	for(i=1;i<L->length;i++)
	{
		for(j=L->length-1;j>=i;j--)  /* 注意j是从后往前循环 */
		{
			if(L->r[j]>L->r[j+1]) /* 若前者大于后者(注意这里与上一算法的差异)*/
			{
				 swap(L,j,j+1);/* 交换L->r[j]与L->r[j+1]的值 */
			}
		}
	}
}

      在这里插入图片描述
              在这里插入图片描述
  冒泡排序复杂度:每次j循环的次数就是移动的次数,n-1 、n-2、n-3、 . . … . .3、2、1,总的时间复杂度是O(n2)。
在这里插入图片描述

快速排序

  快速排序一次递归的方法:通过一趟排序将待排记录分割成独立的两部分low是最小下标,high是最大下标,选定一个中枢轴,一开始选定r[low],然后high不断自减,low不断自增,r[low]和r[high]都与中枢轴比较,若r[low]>中枢轴,交换r[low]、r[high]两者结点数据,若r[high]<中枢轴,交换r[low]、r[high]两者结点数据,直到low = high,一次递归结束。
  大体上递归的思路,最优情况下,快速排序算法时间复杂度是O(nlogn),最坏情况下就相当于冒泡排序,O(n2)。
在这里插入图片描述
代码:

/* 快速排序******************************** */
/* 交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */
/* 此时在它之前(后)的记录均不大(小)于它。 */
int Partition(SqList *L,int low,int high)
{ 
	int pivotkey;

	pivotkey=L->r[low]; /* 用子表的第一个记录作枢轴记录 */
	while(low<high) /*  从表的两端交替地向中间扫描 */
	{ 
		 while(low<high&&L->r[high]>=pivotkey)
			high--;
		 swap(L,low,high);/* 将比枢轴记录小的记录交换到低端 */
		 while(low<high&&L->r[low]<=pivotkey)
			low++;
		 swap(L,low,high);/* 将比枢轴记录大的记录交换到高端 */
	}
	return low; /* 返回枢轴所在位置 */
}
/* 对顺序表L中的子序列L->r[low..high]作快速排序 */
void QSort(SqList *L,int low,int high)
{ 
	int pivot;
	if(low<high)
	{
			pivot=Partition(L,low,high); /*  将L->r[low..high]一分为二,算出枢轴值pivot */
			QSort(L,low,pivot-1);		/*  对低子表递归排序 */
			QSort(L,pivot+1,high);		/*  对高子表递归排序 */
	}
}
/* 对顺序表L作快速排序 */
void QuickSort(SqList *L)
{ 
	QSort(L,1,L->length);
}

二路归并类排序

  归并排序是假设初始序列含有n个记录,看做成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2或者1的有序子序列;再两两归并,,,如此重复,直到得到一个长度为N的有序序列为止,此种方法二路归并排序,空间复杂度,nlogn

/* 递归法 */
/* 将SR[s..t]归并排序为TR1[s..t] */
void MSort(int SR[],int TR1[],int s, int t)
{
	int m;
	int TR2[MAXSIZE+1];
	if(s==t)
		TR1[s]=SR[s];
	else
	{
		m=(s+t)/2;				/* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */
		MSort(SR,TR2,s,m);		/* 递归地将SR[s..m]归并为有序的TR2[s..m] */
		MSort(SR,TR2,m+1,t);	/* 递归地将SR[m+1..t]归并为有序的TR2[m+1..t] */
		Merge(TR2,TR1,s,m,t);	/* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */
	}
}
/* 对顺序表L作归并排序 */
void MergeSort(SqList *L)
{ 
 	MSort(L->r,L->r,1,L->length);
}

  当执行 MSort(SR,TR2,s,m); 时,s = 1,t = 5,m = (1+5)/2 = 3,此时会不断将50、10、90、30、70拆分,细分到单个记录时,s = t,递归返回,如下图。
在这里插入图片描述
  执行MSort(SR,TR2,m+1,t);会将40、80、60、20拆分。
在这里插入图片描述
通过merge函数,将{10、30、50、70、90}和{20、40、60、80}归并成一个最终有序的序列。
在这里插入图片描述
Merge函数:

/* 归并排序********************************** */

/* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
void Merge(int SR[],int TR[],int i,int m,int n)
{
	int j,k,l;
	for(j=m+1,k=i;i<=m && j<=n;k++)	/* 将SR中记录由小到大地并入TR */
	{
		if (SR[i]<SR[j])
			TR[k]=SR[i++];
		else
			TR[k]=SR[j++];
	}
	if(i<=m)
	{
		for(l=0;l<=m-i;l++)
			TR[k+l]=SR[i+l];		/* 将剩余的SR[i..m]复制到TR */
	}
	if(j<=n)
	{
		for(l=0;l<=n-j;l++)
			TR[k+l]=SR[j+l];		/* 将剩余的SR[j..n]复制到TR */
	}
}

当最后{10、30、50、70、90}和{20、40、60、80}进行Merge时如下
          在这里插入图片描述
最后一次Merge看起来算比较的次数多一点
          在这里插入图片描述
大功告成。
          在这里插入图片描述
  
本章所有排序算法测试案例:

#include <stdio.h>    
	#include <string.h>
	#include <ctype.h>      
	#include <stdlib.h>   
	#include <io.h>  
	#include <math.h>  
	#include <time.h>
	
	#define OK 1
	#define ERROR 0
	#define TRUE 1
	#define FALSE 0
	
	#define MAX_LENGTH_INSERT_SORT 7 /* 用于快速排序时判断是否选用插入排序阙值 */
	
	typedef int Status; 
	
	
	#define MAXSIZE 10000  /* 用于要排序数组个数最大值,可根据需要修改 */
	typedef struct
	{
		int r[MAXSIZE+1];	/* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
		int length;			/* 用于记录顺序表的长度 */
	}SqList;
	
	/* 交换L中数组r的下标为i和j的值 */
	void swap(SqList *L,int i,int j) 
	{ 
		int temp=L->r[i]; 
		L->r[i]=L->r[j]; 
		L->r[j]=temp; 
	}
	
	void print(SqList L)
	{
		int i;
		for(i=1;i<L.length;i++)
			printf("%d,",L.r[i]);
		printf("%d",L.r[i]);
		printf("\n");
	}
	
	/* 对顺序表L作交换排序(冒泡排序初级版) */
	void BubbleSort0(SqList *L)
	{ 
		int i,j;
		for(i=1;i<L->length;i++)
		{
			for(j=i+1;j<=L->length;j++)
			{
				if(L->r[i]>L->r[j])
				{
					 swap(L,i,j);/* 交换L->r[i]与L->r[j]的值 */
				}
			}
		}
	}
	
	/* 对顺序表L作冒泡排序 */
	void BubbleSort(SqList *L)
	{ 
		int i,j;
		for(i=1;i<L->length;i++)
		{
			for(j=L->length-1;j>=i;j--)  /* 注意j是从后往前循环 */
			{
				if(L->r[j]>L->r[j+1]) /* 若前者大于后者(注意这里与上一算法的差异)*/
				{
					 swap(L,j,j+1);/* 交换L->r[j]与L->r[j+1]的值 */
				}
			}
		}
	}
	
	/* 对顺序表L作改进冒泡算法 */
	void BubbleSort2(SqList *L)
	{ 
		int i,j;
		Status flag=TRUE;			/* flag用来作为标记 */
		for(i=1;i<L->length && flag;i++) /* 若flag为true说明有过数据交换,否则停止循环 */
		{
			flag=FALSE;				/* 初始为False */
			for(j=L->length-1;j>=i;j--)
			{
				if(L->r[j]>L->r[j+1])
				{
					 swap(L,j,j+1);	/* 交换L->r[j]与L->r[j+1]的值 */
					 flag=TRUE;		/* 如果有数据交换,则flag为true */
				}
			}
		}
	}
	
	
	/* 对顺序表L作简单选择排序 */
	void SelectSort(SqList *L)
	{
		int i,j,min;
		for(i=1;i<L->length;i++)
		{ 
			min = i;						/* 将当前下标定义为最小值下标 */
			for (j = i+1;j<=L->length;j++)/* 循环之后的数据 */
	        {
				if (L->r[min]>L->r[j])	/* 如果有小于当前最小值的关键字 */
	                min = j;				/* 将此关键字的下标赋值给min */
	        }
			if(i!=min)						/* 若min不等于i,说明找到最小值,交换 */
				swap(L,i,min);				/* 交换L->r[i]与L->r[min]的值 */
		}
	}
	
	/* 对顺序表L作直接插入排序 */
	void InsertSort(SqList *L)
	{ 
		int i,j;
		for(i=2;i<=L->length;i++)
		{
			if (L->r[i]<L->r[i-1]) /* 需将L->r[i]插入有序子表 */
			{
				L->r[0]=L->r[i]; /* 设置哨兵 */
				for(j=i-1;L->r[j]>L->r[0];j--)
					L->r[j+1]=L->r[j]; /* 记录后移 */
				L->r[j+1]=L->r[0]; /* 插入到正确位置 */
			}
		}
	}
	
	/* 对顺序表L作希尔排序 */
	void ShellSort(SqList *L)
	{
		int i,j,k=0;
		int increment=L->length;
		do
		{
			increment=increment/3+1;/* 增量序列 */
			for(i=increment+1;i<=L->length;i++)
			{
				if (L->r[i]<L->r[i-increment])/*  需将L->r[i]插入有序增量子表 */ 
				{ 
					L->r[0]=L->r[i]; /*  暂存在L->r[0] */
					for(j=i-increment;j>0 && L->r[0]<L->r[j];j-=increment)
						L->r[j+increment]=L->r[j]; /*  记录后移,查找插入位置 */
					L->r[j+increment]=L->r[0]; /*  插入 */
				}
			}
			printf("	第%d趟排序结果: ",++k);
			print(*L);
		}
		while(increment>1);
	
	}
	
	
	/* 堆排序********************************** */
	
	/* 已知L->r[s..m]中记录的关键字除L->r[s]之外均满足堆的定义, */
	/* 本函数调整L->r[s]的关键字,使L->r[s..m]成为一个大顶堆 */
	void HeapAdjust(SqList *L,int s,int m)
	{ 
		int temp,j;
		temp=L->r[s];
		for(j=2*s;j<=m;j*=2) /* 沿关键字较大的孩子结点向下筛选 */
		{
			if(j<m && L->r[j]<L->r[j+1])
				++j; /* j为关键字中较大的记录的下标 */
			if(temp>=L->r[j])
				break; /* rc应插入在位置s上 */
			L->r[s]=L->r[j];
			s=j;
		}
		L->r[s]=temp; /* 插入 */
	}
	
	/*  对顺序表L进行堆排序 */
	void HeapSort(SqList *L)
	{
		int i;
		for(i=L->length/2;i>0;i--) /*  把L中的r构建成一个大根堆 */
			 HeapAdjust(L,i,L->length);
	    
		for(i=L->length;i>1;i--)
		{ 
			 swap(L,1,i); /* 将堆顶记录和当前未经排序子序列的最后一个记录交换 */
			 HeapAdjust(L,1,i-1); /*  将L->r[1..i-1]重新调整为大根堆 */
		}
	}
	
	/* **************************************** */
	
	
	/* 归并排序********************************** */
	
	/* 将有序的SR[i..m]和SR[m+1..n]归并为有序的TR[i..n] */
	void Merge(int SR[],int TR[],int i,int m,int n)
	{
		int j,k,l;
		for(j=m+1,k=i;i<=m && j<=n;k++)	/* 将SR中记录由小到大地并入TR */
		{
			if (SR[i]<SR[j])
				TR[k]=SR[i++];
			else
				TR[k]=SR[j++];
		}
		if(i<=m)
		{
			for(l=0;l<=m-i;l++)
				TR[k+l]=SR[i+l];		/* 将剩余的SR[i..m]复制到TR */
		}
		if(j<=n)
		{
			for(l=0;l<=n-j;l++)
				TR[k+l]=SR[j+l];		/* 将剩余的SR[j..n]复制到TR */
		}
	}
	
	
	/* 递归法 */
	/* 将SR[s..t]归并排序为TR1[s..t] */
	void MSort(int SR[],int TR1[],int s, int t)
	{
		int m;
		int TR2[MAXSIZE+1];
		if(s==t)
			TR1[s]=SR[s];
		else
		{
			m=(s+t)/2;				/* 将SR[s..t]平分为SR[s..m]和SR[m+1..t] */
			MSort(SR,TR2,s,m);		/* 递归地将SR[s..m]归并为有序的TR2[s..m] */
			MSort(SR,TR2,m+1,t);	/* 递归地将SR[m+1..t]归并为有序的TR2[m+1..t] */
			Merge(TR2,TR1,s,m,t);	/* 将TR2[s..m]和TR2[m+1..t]归并到TR1[s..t] */
		}
	}
	
	/* 对顺序表L作归并排序 */
	void MergeSort(SqList *L)
	{ 
	 	MSort(L->r,L->r,1,L->length);
	}
	
	/* 非递归法 */
	/* 将SR[]中相邻长度为s的子序列两两归并到TR[] */
	void MergePass(int SR[],int TR[],int s,int n)
	{
		int i=1;
		int j;
		while(i <= n-2*s+1)
		{/* 两两归并 */
			Merge(SR,TR,i,i+s-1,i+2*s-1);
			i=i+2*s;        
		}
		if(i<n-s+1) /* 归并最后两个序列 */
			Merge(SR,TR,i,i+s-1,n);
		else /* 若最后只剩下单个子序列 */
			for(j =i;j <= n;j++)
				TR[j] = SR[j];
	}
	
	/* 对顺序表L作归并非递归排序 */
	void MergeSort2(SqList *L)
	{
		int* TR=(int*)malloc(L->length * sizeof(int));/* 申请额外空间 */
	    int k=1;
		while(k<L->length)
		{
			MergePass(L->r,TR,k,L->length);
			k=2*k;/* 子序列长度加倍 */
			MergePass(TR,L->r,k,L->length);
			k=2*k;/* 子序列长度加倍 */       
		}
	}
	
	/* **************************************** */
	
	/* 快速排序******************************** */
	 
	/* 交换顺序表L中子表的记录,使枢轴记录到位,并返回其所在位置 */
	/* 此时在它之前(后)的记录均不大(小)于它。 */
	int Partition(SqList *L,int low,int high)
	{ 
		int pivotkey;
	
		pivotkey=L->r[low]; /* 用子表的第一个记录作枢轴记录 */
		while(low<high) /*  从表的两端交替地向中间扫描 */
		{ 
			 while(low<high&&L->r[high]>=pivotkey)
				high--;
			 swap(L,low,high);/* 将比枢轴记录小的记录交换到低端 */
			 while(low<high&&L->r[low]<=pivotkey)
				low++;
			 swap(L,low,high);/* 将比枢轴记录大的记录交换到高端 */
		}
		return low; /* 返回枢轴所在位置 */
	}
	
	/* 对顺序表L中的子序列L->r[low..high]作快速排序 */
	void QSort(SqList *L,int low,int high)
	{ 
		int pivot;
		if(low<high)
		{
				pivot=Partition(L,low,high); /*  将L->r[low..high]一分为二,算出枢轴值pivot */
				QSort(L,low,pivot-1);		/*  对低子表递归排序 */
				QSort(L,pivot+1,high);		/*  对高子表递归排序 */
		}
	}
	
	/* 对顺序表L作快速排序 */
	void QuickSort(SqList *L)
	{ 
		QSort(L,1,L->length);
	}
	
	/* **************************************** */
	
	/* 改进后快速排序******************************** */
	
	/* 快速排序优化算法 */
	int Partition1(SqList *L,int low,int high)
	{ 
		int pivotkey;
	
		int m = low + (high - low) / 2; /* 计算数组中间的元素的下标 */  
		if (L->r[low]>L->r[high])			
			swap(L,low,high);	/* 交换左端与右端数据,保证左端较小 */
		if (L->r[m]>L->r[high])
			swap(L,high,m);		/* 交换中间与右端数据,保证中间较小 */
		if (L->r[m]>L->r[low])
			swap(L,m,low);		/* 交换中间与左端数据,保证左端较小 */
		
		pivotkey=L->r[low]; /* 用子表的第一个记录作枢轴记录 */
		L->r[0]=pivotkey;  /* 将枢轴关键字备份到L->r[0] */
		while(low<high) /*  从表的两端交替地向中间扫描 */
		{ 
			 while(low<high&&L->r[high]>=pivotkey)
				high--;
			 L->r[low]=L->r[high];
			 while(low<high&&L->r[low]<=pivotkey)
				low++;
			 L->r[high]=L->r[low];
		}
		L->r[low]=L->r[0];
		return low; /* 返回枢轴所在位置 */
	}
	
	void QSort1(SqList *L,int low,int high)
	{ 
		int pivot;
		if((high-low)>MAX_LENGTH_INSERT_SORT)
		{
			while(low<high)
			{
				pivot=Partition1(L,low,high); /*  将L->r[low..high]一分为二,算出枢轴值pivot */
				QSort1(L,low,pivot-1);		/*  对低子表递归排序 */
				/* QSort(L,pivot+1,high);		/*  对高子表递归排序 */
				low=pivot+1;	/* 尾递归 */
			}
		}
		else
			InsertSort(L);
	}
	
	/* 对顺序表L作快速排序 */
	void QuickSort1(SqList *L)
	{ 
		QSort1(L,1,L->length);
	}
	
	#define N 9
	int main()
	{
	   int i;
	   
	   /* int d[N]={9,1,5,8,3,7,4,6,2}; */
	   int d[N]={50,10,90,30,70,40,80,60,20};
	   /* int d[N]={9,8,7,6,5,4,3,2,1}; */
	
	   SqList l0,l1,l2,l3,l4,l5,l6,l7,l8,l9,l10;
	   
	   for(i=0;i<N;i++)
	     l0.r[i+1]=d[i];
	   l0.length=N;
	   l1=l2=l3=l4=l5=l6=l7=l8=l9=l10=l0;
	   printf("排序前:\n");
	   print(l0);
	
	   printf("初级冒泡排序:\n");
	   BubbleSort0(&l0);
	   print(l0);
	   
	   printf("冒泡排序:\n");
	   BubbleSort(&l1);
	   print(l1);
	   
	   printf("改进冒泡排序:\n");
	   BubbleSort2(&l2);
	   print(l2);
	   
	   printf("选择排序:\n");
	   SelectSort(&l3);
	   print(l3);
	   
	   printf("直接插入排序:\n");
	   InsertSort(&l4);
	   print(l4);
	
	   printf("希尔排序:\n");
	   ShellSort(&l5);
	   print(l5);
		
	   printf("堆排序:\n");
	   HeapSort(&l6);
	   print(l6);
	
	   printf("归并排序(递归):\n");
	   MergeSort(&l7);
	   print(l7);
	
	   printf("归并排序(非递归):\n");
	   MergeSort2(&l8);
	   print(l8);
	
	   printf("快速排序:\n");
	   QuickSort(&l9);
	   print(l9);
	
	   printf("改进快速排序:\n");
	   QuickSort1(&l10);
	   print(l10);
		return 0;
	}

运行结果:
           在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_41262453/article/details/88284457