大话数据结构---排序

1、冒泡排序

  • 排序用的顺序表结构以及交换函数:
#define MAXSIZE 10   /*用于要排序数组个数最大值,可根据需要修改*/
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;
}
  • 冒泡排序(Bubble Sort)一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。总的时间复杂度为O(n^2)
/*对顺序表L作冒泡排序*/
void BubbleSort(SqList *L)
{
	int i, j;
	for(i = 1; i < L->length; i++)
	{
		for(j = L->length - 1; 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为false则退出循环*/
	{
		flag = FALSE;   /*初始化为false*/
		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]的值*/
				flag = TRUE;   /*如果有数据交换,则flag为true*/
			}
		}
	}
}

2、简单选择排序

  • 简单选择排序法(Simple Selection Sort)就是通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i(1 <= i <= n)个记录交换之。总的时间复杂度为O(n^2)
/*对顺序表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*/
			min = j;
		}
		if(i != min)   /*若min不等于i,说明找到最小值,交换*/
			swap(L, i, min);   /*交换L->r[i]与L->r[min]的值*/
	}
}

3、直接插入排序

  • 直接插入排序(Straight Insertion Sort)的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录增1的有序表。总的时间复杂度为O(n^2)
/*对顺序表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];   /*插入到正确位置*/
		}
	}
}

4、希尔排序

  • 将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。
/*对顺序表L作希尔排序*/
void ShellSort(SqList *L)
{
	int i, j;
	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];   /*插入*/
			}
		}
	}
	while(increment > 1);
}

5、堆排序

  • 堆是具有下列性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。堆排序(Heap Sort)就是利用堆进行排序的方法。它的基本思想是,将待排序的序列构造成一个大顶堆。此时整个序列的最大值就是堆顶的根结点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n - 1个序列重新构造成一个堆,这样就会得到n个元素中的次小值。如此反复执行,便能得到一个有序序列了。总的时间复杂度为O(nlogn)
/*对顺序表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]重新调整为大顶堆*/
	}
}

/*已知L->r[s...m]中记录的关键字除L->r[s]之外均满足堆的定义*/
/*本函数调整L->r[s]的guanjianzi关键字,使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;   /*插入*/
}

6、归并排序

  • 归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。比较占内存,但却效率高且稳定的算法。
/*对顺序表L作归并排序*/
void MergeSort(SqList *L)
{
	MSort(L->r, L->r, 1, L->length);
}

/*将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]*/
	}
}

/*将有序的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 - 1; l++)
			TR[k + 1] = SR[i + 1];   /*将剩余的SR[i...m]复制到TR*/
	}
	if(j <= n)
	{
		for(l = 0; l <= n - j; l++)
			TR[k + 1] = SR[j + 1];   /*将剩余的SR[j...n]复制到TR*/
	}
}
  • 对顺序表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;   /*子序列长度加倍*/
	}
}

/*将SR[]中相邻长度为s的子序列两两归并到TR[]*/
void MergePass(int SR[], int TR[], int s, int n)
{
	int i = 1;
	int j;
	while(i <= n - 2 * s + 1)
	{
		Nerge(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];
}

7、快速排序

  • 快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
/*对顺序表L作快速排序*/
void QuickSort(SqList *L)
{
	QSort(L, 1, L->length);
}

/*对顺序表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中子表的记录,使枢轴记录到位,并返回其所在位置*/
/*此时在它之前(后)的记录均不大(小)于它*/
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;   /*返回枢轴所在位置*/
}
  • 合理选取枢轴值对快速排序算法进行优化——三数取中法。即三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数,也可以随机选取。
/*增加代码*/
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);   /*交换中间与左端数据,保证左端较小*/
/*此时L.r[low]已经为整个序列左中右三个关键字的中间值*/

pivotkey = L->r[low];   /*用子表的第一个记录作枢轴记录*/
/*快速排序优化算法*/
int Partition1(SqList *L, int low, int high)
{
	int pivotkey;
	//这里省略三数取中代码
	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];   /*将枢轴值替换回L.r[low]*/
	return low;   /*返回枢轴所在位置*/
}
  • 优化小数组时的排序方案
#define MAX_LENGTH_INSERT_SORT 7   /*数组长度阈值*/
/*对顺序表L中的子序列L.r[low...high]作快速排序*/
void QSort(SqList &L, int low, int high)
{
	int pivot;
	if((high - low) > MAX_LENGTH_INSERT_SORT)
	{/*当high - low大于常数时用快速排序*/
		pivot = Partition(L, low, high);
		QSort(L, low, pivot - 1);
		QSort(L, pivot + 1, high);
	}
	else   /*当high - low小于等于常数时用直接插入排序*/
		InsertSort(L);
}
  • 优化递归操作
/*对顺序表L中的子序列L.r[low...high]作快速排序*/
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);
			QSort1(L, low, pivot - 1);   /*对低子表递归排序*/
			low = pivot + 1;   /*尾递归*/
		}
	}
	else
		InsertSort(L);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值