现在的位置: 首页 > 综合 > 正文

让快速排序真正地”快起来”

2014年01月28日 ⁄ 综合 ⁄ 共 9227字 ⁄ 字号 评论关闭

1.背景:

 

      快速排序(Quicksort)是对冒泡排序的一种改进。由C.
A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割
成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

      同其他排序方法相比,快速排序是目前被认为是最好的一种内部排序方法,但快排算法比较多,而且不同快排算法在处理不同类型序列时,其性能也会有较大差异,因此,笔者将通过实例就各种快排算法性能进行比较,并从中找出性能较好的算法,从而让快速排序真正地"快起来"(当然也可能有更好地算法)。

 


2. 快速排序的C实现代码:

 

/*****************RedType.h*****************/
#ifndef RED_TYPE
#define RED_TYPE
#include <stdio.h>
typedef int KeyType;//定义关键字类型为整型
typedef int infoType;//定义定义其他数据项的类型为整型

typedef struct RedType
{//记录的类型
	KeyType key;//关键字
	infoType otherinfo;//其他数据项

}RedType;

//对两个数值型关键字的比较约定为如下的宏定义。
#define EQ(a,b) ((a) == (b))
#define LT(a,b) ((a) < (b))
#define LE(a,b) ((a) <= (b))

//基本操作函数
void InputFromFile(FILE *f,RedType &c);

#endif


 

/*****************RedType.cpp*******************/
#include "RedType.h"
void InputFromFile(FILE *f,RedType &c)
{//从文件输入记录的函数
	fscanf(f,"%d%d",&c.key,&c.otherinfo);
}

 

/*****************Sq_QuitSort.h*****************/
#ifndef SQ_QUITSORT_H
#define SQ_QUITSORT_H

#include "RedType.h"
#define MAX_SIZE 20 //一个用作示例的小顺序表的最大长度
typedef struct SqList
{//顺序表类型
	RedType r[MAX_SIZE];//r[0]闲置或用作哨兵单元
	int length;//顺序表长度
}SqList;
//函数声明
void PrintL(SqList &L);

void PrintL(SqList &L);

int Parition1(SqList &L,int low,int high);

int Parition2(SqList &L,int low,int high);

int Parition3(SqList &L,int low,int high);

int Parition4(SqList &L,int low,int high);

void Qsort(SqList &L,int low ,int high);

void QuikSort(SqList &L);

#endif 

 

/*****************Sq_QuitSort.cpp*****************/
#include "Sq_QuitSort.h"
#include <stdio.h>
typedef int Status;//状态类型
#define TRUE 1
#define FALSE 0
void PrintL(SqList &L)
{//输出顺序表L
	int i;
	for (i = 1; i <= L.length; ++i)
	{
		printf("(%d,%d) ",L.r[i].key,L.r[i].otherinfo);
	}
	printf("\n");
}

int Parition1(SqList &L,int low,int high)
{//交换顺序表L中子表L.r[low...high]的记录,使枢轴记录到位
//函数返回交换后枢轴的位置,其前的记录小于枢轴关键字,其后则大于
	KeyType pivotkey;//枢轴关键字
	pivotkey = L.r[low].key;//用子表的第一个关键字作枢轴的关键字
	while (low < high)
	{//从表的两端交替地向中间扫描
		while (low < high && L.r[high].key >= pivotkey)
		{//高端关键字大于等于枢轴关键字
			--high;//高端向低移,继续比较
		}
		L.r[0]	= L.r[low];//将比枢轴小的关键词记录交换到低端,枢轴到高端
		L.r[low] = L.r[high];
		L.r[high] = L.r[0];
		while (low < high && L.r[low].key <= pivotkey)
		{//低端关键词小于等于枢轴关键字
			++low;//低端向高移,继续比较
		}
		L.r[0]	= L.r[low];//将比枢轴大的关键词记录交换到高端,枢轴到低端
		L.r[low] = L.r[high];
		L.r[high] = L.r[0];
	}
	return low;//返回枢轴位置
}

int Parition2(SqList &L,int low,int high)
{//【改进算法1】交换顺序表L中子表L.r[low...high]的记录,使枢轴记录到位
//函数返回交换后枢轴的位置,其前的记录小于枢轴关键字,其后则大于
	KeyType pivotkey;//枢轴关键字
	pivotkey = L.r[low].key;//用子表的第一个关键字作枢轴的关键字
	L.r[0] = L.r[low];//将枢轴记录保存到[0],改进处
	while (low < high)
	{//从表的两端交替地向中间扫描
		while (low < high && L.r[high].key >= pivotkey)
		{//高端关键字大于等于枢轴关键字
			--high;//高端向低移,继续比较
		}
		L.r[low] = L.r[high];//将比枢轴关键字小的记录移动到低端,枢轴在[0]处不动,改进处
		while (low < high && L.r[low].key <= pivotkey)
		{//低端关键词小于等于枢轴关键字
			++low;//低端向高移,继续比较
		}
		L.r[high] = L.r[low];//将比枢轴关键字大的记录移动到高端,枢轴在[0]处不动,改进处
	}
	L.r[low] = L.r[0];//将枢轴记录移动到位,改进处
	return low;//返回枢轴位置
}

void SwapMid(SqList &L,int low,int high)
{//将顺序表L中记录L.r[low],L.r[(low + high)/2],L.r[high]的关键字为中值的记录和第一个记录L.r[low]互换
	int i,j,mid,m = (low + high)/2;
	SqList t;//存储以上三个记录的临时顺序表,用来对三个记录排序以便找出中值
	t.length = 3;
	t.r[1] = L.r[low];
	t.r[2] = L.r[m];
	t.r[3] = L.r[high];
	Status change = TRUE;//调整的标志,初值为TRUE
	for (i = t.length - 1; i >= 1 && change; --i)
	{
		change = FALSE;//本次循环未调整的标志
		for (j = 1; j <= t.length - 1; ++j)
		{
			if (LT(t.r[j + 1].key,t.r[j].key))//前面记录的关键字大于后面记录的关键字
			{//两记录互换
				t.r[0] = t.r[j];
				t.r[j] = t.r[j + 1];
				t.r[j + 1] = t.r[0];
				change = TRUE;//已调整的标志
			}
		}
	}

	if (EQ(t.r[2].key,L.r[high].key))
	{//中值为L.r[high].key
		mid = high;
	}
	else if (EQ(t.r[2].key,L.r[m].key))
	{//中值为L.r[m].key
		mid = m;
	}
	else
	{//中值为L.r[low].key
		mid = low;
	}
	if (mid != low)
	{//中值记录和第一个记录互换
		L.r[0] = L.r[mid];
		L.r[mid] = L.r[low];
		L.r[low] = L.r[0];
	}
}
int Parition3(SqList &L,int low,int high)
{//【改进算法2】交换顺序表L中子表L.r[low...high]的记录,使枢轴记录到位
//函数返回交换后枢轴的位置,其前的记录小于枢轴关键字,其后则大于
	KeyType pivotkey;//枢轴关键字
	SwapMid(L,low,high);//记录L.r[low],L.r[(low + high)/2],L.r[high]的关键字为中值的记录和L.r[low]互换,改进处
	pivotkey = L.r[low].key;//用子表的第一个关键字作枢轴的关键字
	L.r[0] = L.r[low];//将枢轴记录保存到[0]
	while (low < high)
	{//从表的两端交替地向中间扫描
		while (low < high && L.r[high].key >= pivotkey)
		{//高端关键字大于等于枢轴关键字
			--high;//高端向低移,继续比较
		}
		L.r[low] = L.r[high];//将比枢轴关键字小的记录移动到低端,枢轴在[0]处不动
		while (low < high && L.r[low].key <= pivotkey)
		{//低端关键词小于等于枢轴关键字
			++low;//低端向高移,继续比较
		}
		L.r[high] = L.r[low];//将比枢轴关键字大的记录移动到高端,枢轴在[0]处不动
	}
	L.r[low] = L.r[0];//将枢轴记录移动到位
	return low;//返回枢轴位置
}
int Parition4(SqList &L,int low,int high)
{//【改进算法2】交换顺序表L中子表L.r[low...high]的记录,使枢轴记录到位
//函数返回交换后枢轴的位置,其前的记录小于枢轴关键字,其后则大于
	KeyType pivotkey;//枢轴关键字
	RedType t;
	SwapMid(L,low,high);//记录L.r[low],L.r[(low + high)/2],L.r[high]的关键字为中值的记录和L.r[low]互换
	pivotkey = L.r[low].key;//用子表的第一个关键字作枢轴的关键字
	L.r[0] = L.r[low];//将枢轴记录保存到[0]
	while (low < high)
	{//从表的两端交替地向中间扫描
		while (low < high && L.r[high].key >= pivotkey)
		{//高端关键字大于等于枢轴关键字
			if (LT(L.r[high].key,L.r[high - 1].key))
			{//在相邻两记录处于“逆序时”进行互换,改进处
				t = L.r[high];
				L.r[high] = L.r[high - 1];
				L.r[high - 1] = t;
			}
			--high;
		}
		L.r[low] = L.r[high];//将比枢轴关键字小的记录移动到低端,枢轴在[0]处不动
		while (low < high && L.r[low].key <= pivotkey)
		{//低端关键词小于等于枢轴关键字
			if (LT(L.r[low + 1].key,L.r[low].key))
			{//在相邻两记录处于“逆序时”进行互换,改进处
				t = L.r[low];
				L.r[low] = L.r[low + 1];
				L.r[low + 1] = t;
			}
			++low;//低端向高移,继续比较
		}
		L.r[high] = L.r[low];//将比枢轴关键字大的记录移动到高端,枢轴在[0]处不动
	}
	L.r[low] = L.r[0];//将枢轴记录移动到位
	return low;//返回枢轴位置
}

void Qsort(SqList &L,int low ,int high)
{//对顺序表L中的子序列L.r[low..high]作快速排序
	static int c = 1;//统计趟数
	int pivotloc;//枢轴位置
	if (low < high)
	{//子序列长度大于1
		pivotloc = Parition4(L,low,high);//将L.r[low..high]按关键字一分为二,pivotlc是枢轴位置
		printf("第%d趟排序后 顺序表L:\n",c++);
		PrintL(L);
		Qsort(L,low,pivotloc - 1);//对关键字小于枢轴关键字的低子表递归快速排序
		Qsort(L,pivotloc + 1,high);//对关键字大于枢轴关键字的高子表递归快速排序
	}
}

void QuikSort(SqList &L)
{//对整个顺序表L作快速排序
	Qsort(L,1,L.length);
}
//



 

/*****************main.cpp*****************/
#include "Sq_QuitSort.h"
#include <stdio.h>

int main(void)
{
	FILE *f; 
	SqList m; 
	int i;
    f=fopen("1.txt","r"); 
    fscanf(f,"%d",&m.length); 
    for(i=1;i<=m.length;i++) 
    {  
	    InputFromFile(f,m.r[i]); 
	}
	fclose(f);  
    printf("排序前:\n");
    PrintL(m); 
	QuikSort(m); 
    printf("快速排序后:\n");
    PrintL(m); 

	return 0;
}

//排序前按乱序
//(20,1) (17,2) (65,3) (97,4) (76,5) (13,6) (27,7) (49,8) (49,9)

//P1,P2处理乱序
//第1趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (97,4) (76,5) (65,3) (27,7) (49,8) (49,9)
//第2趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (97,4) (76,5) (65,3) (27,7) (49,8) (49,9)
//第3趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (49,9) (76,5) (65,3) (27,7) (49,8) (97,4)
//第4趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (65,3) (76,5) (49,8) (97,4)
//第5趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (49,8) (65,3) (76,5) (97,4) #第5趟排好  共5趟

//P3处理乱序
//第1趟排序后 顺序表L:
//(20,1) (17,2) (27,7) (13,6) (49,9) (76,5) (97,4) (49,8) (65,3)
//第2趟排序后 顺序表L:
//(13,6) (17,2) (27,7) (20,1) (49,9) (76,5) (97,4) (49,8) (65,3)
//第3趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (76,5) (97,4) (49,8) (65,3)
//第4趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (65,3) (49,8) (76,5) (97,4)
//第5趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (49,8) (65,3) (76,5) (97,4) #第5趟排好  共5趟

//P4处理乱序
//第1趟排序后 顺序表L:
//(17,2) (20,1) (27,7) (13,6) (49,9) (76,5) (97,4) (49,8) (65,3)
//第2趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (76,5) (97,4) (49,8) (65,3)
//第3趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (76,5) (97,4) (49,8) (65,3)
//第4趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (65,3) (49,8) (76,5) (97,4)
//第5趟排序后 顺序表L:
//(13,6) (17,2) (20,1) (27,7) (49,9) (49,8) (65,3) (76,5) (97,4) #第5趟排好  共5趟


//排序前逆序:
//(9,1) (8,2) (7,3) (6,4) (5,5) (4,6) (3,7) (2,8) (1,9)


//P1 P2处理按逆序:
//第1趟排序后 顺序表L:
//(1,9) (8,2) (7,3) (6,4) (5,5) (4,6) (3,7) (2,8) (9,1)
//第2趟排序后 顺序表L:
//(1,9) (8,2) (7,3) (6,4) (5,5) (4,6) (3,7) (2,8) (9,1)
//第3趟排序后 顺序表L:
//(1,9) (2,8) (7,3) (6,4) (5,5) (4,6) (3,7) (8,2) (9,1)
//第4趟排序后 顺序表L:
//(1,9) (2,8) (7,3) (6,4) (5,5) (4,6) (3,7) (8,2) (9,1)
//第5趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (6,4) (5,5) (4,6) (7,3) (8,2) (9,1)
//第6趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (6,4) (5,5) (4,6) (7,3) (8,2) (9,1)
//第7趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (6,4) (7,3) (8,2) (9,1)  # 第7趟排好
//第8趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (6,4) (7,3) (8,2) (9,1)  共8趟
//请按任意键继续. . .

//P3处理逆序:
//第1趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第2趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第3趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第4趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (7,3) (6,4) (8,2) (9,1)
//第5趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (6,4) (7,3) (8,2) (9,1)  # 第5趟排好  共5趟 
//请按任意键继续. . .

//P3处理逆序:
//第1趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第2趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第3趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (9,1) (6,4) (7,3) (8,2)
//第4趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (6,4) (7,3) (8,2) (9,1)  #第4趟排好  共5趟
//第5趟排序后 顺序表L:
//(1,9) (2,8) (3,7) (4,6) (5,5) (6,4) (7,3) (8,2) (9,1)
//请按任意键继续. . .


//排序前按顺序:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)

//P1 P2处理顺序:
//第1趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9) 
//第2趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第3趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第4趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第5趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第6趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第7趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第8趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)  共8趟

//P3处理顺序:
//第1趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9) 
//第2趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第3趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第4趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)
//第5趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (4,4) (5,5) (6,6) (7,7) (8,8) (9,9)  共5趟

//P4处理顺序:
//第1趟排序后 顺序表L:
//(1,1) (2,2) (3,3) (1,1) (5,5) (6,6) (7,7) (8,8) (9,9) 
//第2趟排序后 顺序表L:
//(1,1) (1,1) (2,2) (3,3) (5,5) (6,6) (7,7) (8,8) (9,9)
//第3趟排序后 顺序表L:
//(1,1) (1,1) (2,2) (3,3) (5,5) (6,6) (7,7) (8,8) (9,9)
//第4趟排序后 顺序表L:
//(1,1) (1,1) (2,2) (3,3) (5,5) (6,6) (7,7) (8,8) (9,9)
//第5趟排序后 顺序表L:
//(1,1) (1,1) (2,2) (3,3) (5,5) (6,6) (7,7) (8,8) (9,9)  共5趟

//排序性能分析:
//当序列按乱序排列时,P1,P2,P3的平均性能相差不大
//当序列按逆序排列时,平均性能P4 > P3 > P1,P2
//当序列按顺序排列时,平均性能P4 = P3 > P1,P2
//P1,P2处理乱序序列时的性能优于处理顺序和逆序序列时的性能
//综上所述,P4算法平均排序性能最好

 

抱歉!评论已关闭.