数据结构及算法之排序

排序

  将一个数据元素的任意序列,重新排列成一个按关键字有序的序列。

#define MAXSIZE 20                //一个用作示例的小顺序表的最大长度
typedef int KeyType;                   //定义关键字类型为整型
typedef struct{
	KeyType       key;                   //关键字项
	InfoType      otherinfo;             //其他数据项
}RedType;                                //记录类型
typedef struct{
	RedType       r[ MAXSIZE + 1 ];     //r[0]闲置或用作哨兵单元
	int           length;               //顺序表长度
}SqList;                                //顺序表类型

1 插入排序

(1) 直接插入排序

  它是将一个记录插入到以排好序的有序表中,从而得到一个新的、记录数增1的有序表。
在这里插入图片描述

void
InsertSort( SqList &L ){
	int i,j;
	for( i = 2; i <= L.length; ++i ){
		if( L.r[ i ].key < L.r[ i - 1 ].key ){      //若<,需将L.r[ i ]插入有序子表
			L.r[ 0 ] = L.r[ i ];                   //复制为哨兵
			for( j = i - 1;L.r[ 0 ].key < L.r[ j ].key; --j ){
				L.r[ j + 1 ] = L.r[ j ];           //记录后移
			}
			L.r[ j + 1 ] = L.r[ 0 ];               //插入到正确位置
		} 
	}
}

(2) 二分插入排序

  由于插入排序的基本操作是在一个有序表中进行查找和插入,这个查找操作可利用二分查找来实现。

void 
BinsertSort( SqList &L ){
	for( i = 2; i <= L.length; ++i ){          //依次插入第2-第n个元素
		L.r[ 0 ] = L.r[ i ];                   //将当前插入元素存到哨兵位置
		low = 1;
		high = i - 1;                         //采用二分查找法查找插入位置
		while( low <= high ){
			mid = ( low + high ) / 2;
			if( L.r[ 0 ].key < L.r[ mid ].key )
				high = mid -1;
			else
				low = mid + 1;
		}  //循环结束,high+1则为插入位置
	for( j = i - 1; j >= high + 1; --j )
		L.r[ j + 1 ] = L.r[ j ];             //移动元素
		L.r[ high + 1 ] = L.r[ 0 ];	         //插入到正确位置
	}
}

(3) 希尔排序

  先将整个待排记录列表分割成若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
  希儿排序的一个特点是:子序列的构成不是简单的“逐段分割”,而是将相隔某个“增量”的记录组成的一个子序列。

void
ShellInsert( SqList &L, int dk ){
	//对顺序表L进行一趟增量为dk的shell排序,dk为步长因子
	for( i = dk + 1; i <= L.length; ++i )
		if( L.r[ i ].key < L.r[ i - dk ].key ){
			L.r[ 0 ] = L.r[ i ];
			for( j = i -dk; j > 0 && L.r[ 0 ].key < L.r[ j].key );
				j = j - dk;
				L.r[ j + dk ] = L.r[ j ];
				L.r[ j + dk ] = L.r[ 0 ];
		}
}

void
ShellSort( SqList &L, int dlta[], int t ){
	//按增量序列dlta[0..t-1]对顺序表L作希尔排序
	for( k = 0; k < t; ++k )
	ShellInsert( L, dlta[ k ] );	     //一趟增量为dlta[k]的插入排序
}

2 交换排序

(1)冒泡排序

  先将第一个记录的关键字和第二个记录的关键字进行比较,若为逆序,则将两个记录交换之,然后比较第二个记录和第三个记录的关键字。依次类推。

void
bubble_sort( SqList &L ){               //冒泡排序算法
	int m,i,j; 
	RedType x;                          //交换时临时存储
	for( m = 1; m < n - 1; m++ ){       //总共需m趟
		for( j = 1; j < n - m; j++ )
			if( L.r[ j ].key > L.r[ j + 1 ].key ){   //发生逆序
				x = L.r[ j ];
				L.r[ j ] = L.r[ j + 1 ];
				L.r[ j + 1 ] = x;                    //交换
		}
	}
}
void
bubble_sort( SqList &L ){               //改进的冒泡排序算法
	int m,i,j; 
	flag = 1;                           //flag作为是否有交换的标记
	RedType x;                          //交换时临时存储
	for( m = 1; m < n - 1 && flag == 1; m++ ){       //总共需m趟
		flag = 0;
		for( j = 1; j <= m; j++ )
			if( L.r[ j ].key > L.r[ j + 1 ].key ){   //发生逆序
				flag = 1;                //发生交换,flag置为1,若本趟没发生交换,flag保持0
				x = L.r[ j ];
				L.r[ j ] = L.r[ j + 1 ];
				L.r[ j + 1 ] = x;                    //交换
		}
	}
}

(2)快速排序

  首先任意选取一个记录(通常可选第一个记录)作为枢轴(或支点),然后按照下述原则重新排列其余记录:
  将所有关键字较它小的记录都安置在它的位置之前,将所有关键字较大的记录都安置在它的位置之后。

void 
main(){
	Qsort( L, 1, L.length );
}

void
Qsort( SqList &L, int low, int high ){
	//对顺序表L快速排序
	if( low < high ){         //表长大于1
	//将L一分为2,pivotloc为枢轴元素排好序的位置
		pivotloc = partition( L, low, high );
		Qsort( L, low, pivotloc - 1 )//对低子表递归排序
		Qsort( L, pivotloc + 1, high )//对高子表递归排序
	}
}

int
partition( SqList &L, int low, int high ){
	L.r[ 0 ] = L.r[ low ];           //用子表的第一个记录作枢轴记录
	pivotkey = L.r[ low ].key;      //枢轴记录关键字
	while( low < high ){            //从表的两端交替的向中间扫描
		while( low < high && L.r[ high ].key >= pivotkey )
		--high;                     //将比枢轴记录小的记录移到低端
		L.r[ low ] = L.r[ high ];
	while( low < high ){
		while( low < high && L.r[ high ].key <= pivotkey )
		++low;                     //将比枢轴记录大的记录移到高端
		L.r[ high ] = L.r[ low ];	
	}
	L.r[ low ] = L.r[ 0 ];        //枢轴记录到位
	return low;                   //返回枢轴位置
}

3 选择排序

(1) 简单选择排序

在这里插入图片描述

void
SelectSort( SqList &L ){
	for( i = 1; i < L.length; ++i ){
		k = i;
		for( j = i + 1; j <= L.length; j++ )
			if( L.r[ j ].key <  L.r[ k ].key )
				k = j;               //记录最小值位置
			if( k != i )
				L.r[ i ] = L.r[ k ]; //交换
	}
}

(2) 堆排序

在这里插入图片描述
堆的含义表明,完全二叉树中所有非终端结点的值均不大于其左右孩子结点的值。若在输出堆顶的最小值后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素中的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
从一个无序序列建堆的过程就是一个反复“筛选”的过程。

void
HeapAdjust( elem R[], int s, int m ){
	//已知E[s...m]中记录的关键字除R[s]之外均满足堆的定义,本函数调整R[s]的关键字,使R[s...m]成为一个大根堆。
	rc = R[s];
	for( j = 2 * s; j <= m; j *= 2 ){      //沿key较大的孩子结点向下筛选
		if( j < m && R[ j ] < R[ j + 1 ] )
			++j;                          //j为key较大的记录的下标
		if( rc >= R[ j ] )
			break;
		R[ s ] = R[ j ];
		s = j;                           //rc应插入在位置s上
	}
	R[ s ] = rc;                         //插入
}
void
HeapSort( elem R[] ){
	//对R[1]到R[n]进行堆排序
	int i;
	for( i = n / 2; i >= 1; i-- )
		HeapAdjust( R, i, n );           //建初始堆
	for( i = n; i > 1; i-- ){            //进行n-1趟排序
		Swap( R[ 1 ], R[ i ] );          //根与最后一个元素交换
		HeapAdjust( R, 1, i - 1 );       //对R[1]到R[i-1]重新建堆
	}
}

4 归并排序

  归并的含义是将2个或2个以上的有序表组合成一个新的有序表。
在这里插入图片描述
假设初始序列含有n个记录,则可看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2或1的有序子序列,再两两归并,重复。,直至得到一个长度为n的有序列表为止,这种排序方法称为2-路归并排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值