排序 - C语言实现(摘自数据结构与算法分析C语言描述))

一、概述

        根据是否使用外存,排序可以分为内部排序和外部排序。若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。

        内排序的方法有许多种,按所采用策略的不同,可归纳为五类:插入排序、选择排序、交换排序、归并排序和分配排序。

        其中,插入排序主要包括直接插入排序和希尔排序两种;选择排序主要包括直接选择排序和堆排序;交换排序主要包括气(冒)泡排序和快速排序。

二、实现

      这里我们主要讨论插入排序、希尔排序、堆排序、归并排序和快速排序。

◎插入排序

      最简单的排序算法之一是插入排序(insertionsort)。插入排序由N – 1趟(pass)排序组成。对于P = 1趟到P = N – 1趟,插入排序保证从位置0到位置P上的元素为已排序状态。插入排序利用了这样的事实:位置0到位置P – 1上的元素是已排过序的。

◎希尔排序

      通过比较相距一定间隔的元素来工件各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止,因此希尔排序有时也叫做缩小增量排序(diminishing increment sort)。

      增量序列的一种流行(但是不好)的选择是使用Shell建议的序列:ht =⌊N / 2⌋和hk =⌊hk + 1 / 2⌋。

◎堆排序

      由二叉堆的性质可知(可参考本人其它博文:优先队列(堆) - C语言实现(摘自数据结构与算法分析C语言描述)),建立一个N个元素的二叉堆需花费O(N)时间,然后我们执行N次DeleteMin操作。按照顺序,最小的元素先离开该堆。通过将这些元素记录到第二个数组,然后再将数组拷贝回来,我们得到N个元素的排序。由于每个DeleteMin花费时间O(log N),因此总的运行时间是O(N log N)

      该算法的主要问题在于它使用了一个附加的数组。因此,存储需求增加一倍。避免使用第二个数组的聪明的做法是利用这样的事实:在每次DeleteMin之后,堆缩小了1。因此,位于堆中最后的单元可以用来存放刚刚删去的元素。

◎归并排序

      归并排序以O(N log N)最坏情形运行时间运行,而所使用的比较次数几乎是最优的。这个算法的基本的操作是合并两个已排序的表。因为这两个表是已排序,所以若将输出放到第三个表中时则该算法可以通过对输入数据一趟排序来完成。基本的合并算法是取两个输入数组AB,一个输出数组C,以及三个计数器AptrBptrCptr,它们初始置于对应数组的开始端。A[Aptr]和B[Bptr]中的较小者被拷贝到C中的下一个位置,相关的计数器向前推进一步。当两个输入表有一个用完的时候,则将另一个表中剩余总分拷贝到C中。

      该算法是经典的分治(divide-and-conquer)策略,它将问题分成一些小的问题然后递归求解,而治的阶段解得的各个答案修补到一些,分治是递归常有力的用法。

◎快速排序

      正如它的名字所标示的,快速排序(quicksort)是在实践中最快的已知排序算法,它的平均运行时间是O(N log N)。

      像归并排序一样,快速排序也是一种分治的递归算法。将数组S排序的基本算法由下列简单的四步组成:

1.      如果S中元素个数是0或1,则返回;

2.      取S中任一元素v,称为枢纽元(pivot);

3.      将(S中其余元素)分成两个不相交的集合:S1 = { xS - { v } | xv } 和S1 = { xS - { v } | xv };

4.      返回{quicksort(S1)后,继随v,继而quicksort(S2)}。


文件名:sort.h

#ifndef _Sort_H

typedef int ElementType;

void PrintElements( ElementType A[ ], int N );

void InsertionSort( ElementType A[ ], int N );

void ShellSort( ElementType A[ ], int N );

void PercDown( ElementType A[ ], int i, int N );
void HeapSort( ElementType A[ ], int N );

void Merge( ElementType A[ ], ElementType TmpArray[ ],
		   int Lpos, int Rpos, int RightEnd );
void MSort( ElementType A[ ], ElementType TmpArray[ ],
		   int Left, int Right);
void MergeSort( ElementType A[ ], int N );

ElementType Median3( ElementType A[ ], int Left, int Right );
void Qsort( ElementType A[ ], int Left, int Right );
void QuickSort( ElementType A[ ], int N );

#endif

文件名:sort.c

#include "sort.h"
#include "fatal.h"
#include <stdio.h>

#define LeftChild( i ) ( 2 * ( i ) + 1 )
#define Cutoff ( 3 )

void
PrintElements( ElementType A[ ], int N )
{
	int i;
	for ( i = 0; i < N; i++)
		printf( "%3d", A[ i ] );
	printf( "\n" );
}

void
Swap( ElementType *Lhs, ElementType *Rhs )
{
	ElementType Tmp = *Lhs;
	*Lhs = *Rhs;
	*Rhs = Tmp;
}

void 
InsertionSort( ElementType A[ ], int N )
{
	int j, P;

	ElementType Tmp;
	for ( P = 1; P < N; P++)
	{
		Tmp = A[ P ];
		for ( j = P; j > 0 && A[ j - 1 ] > Tmp; j-- )
			A[ j ] = A[ j - 1 ];
		A[ j ] = Tmp;
	}
}

void
ShellSort( ElementType A[ ], int N )
{
	int i, j, Increment;
	ElementType Tmp;

	for ( Increment = N / 2; Increment > 0; Increment /= 2 )
	{
		for ( i = Increment; i < N; i++ )
		{
			Tmp = A[ i ];
			for ( j = i; j >= Increment; j -= Increment )
				if ( Tmp < A[ j - Increment ] )
					A[ j ] = A[ j - Increment ];
				else
					break;
			A[ j ] = Tmp;
		}
	}
}

void 
PercDown( ElementType A[ ], int i, int N )
{
	int Child;
	ElementType Tmp;

	for ( Tmp = A[ i ]; LeftChild( i ) < N; i = Child)
	{
		Child = LeftChild( i );
		if ( Child != N - 1 && A[ Child + 1 ] > A[ Child ] )
			Child++;
		if ( Tmp < A[ Child ] )
			A[ i ] = A[ Child ];
		else
			break;
	}
	A[ i ] = Tmp;
}

void 
HeapSort( ElementType A[ ], int N )
{
	int i;
	for ( i = N / 2; i >= 0; i-- ) /* BuildHeap */
		PercDown( A, i, N );
	for ( i = N - 1; i > 0; i-- )
	{
		Swap( &A[ 0 ], &A[ i ] ); /* DeleteMax */
		PercDown( A, 0, i );
	}
}

void 
MSort( ElementType A[ ], ElementType TmpArray[ ],
	  int Left, int Right)
{
	int Center;

	if ( Left < Right )
	{
		Center = ( Left + Right ) / 2;
		MSort( A, TmpArray, Left, Center );
		MSort( A, TmpArray, Center + 1, Right );
		Merge( A, TmpArray, Left, Center + 1, Right);
	}
}

void 
MergeSort( ElementType A[ ], int N )
{
	ElementType *TmpArray;

	TmpArray = malloc( N * sizeof( ElementType ) );
	if ( TmpArray != NULL )
	{
		MSort( A, TmpArray, 0, N - 1 );
		free( TmpArray );
	}
	else
		FatalError( "No space for tmp array!!!" );
}

/* Lpos = start of left half, Rpos = start of right half */
void
Merge( ElementType A[ ], ElementType TmpArray[ ],
	  int Lpos, int Rpos, int RightEnd )
{
	int i, LeftEnd, NumElements, TmpPos;

	LeftEnd = Rpos - 1;
	TmpPos = Lpos;
	NumElements = RightEnd - Lpos + 1;

	/* main loop */
	while ( Lpos <= LeftEnd && Rpos <= RightEnd )
		if ( A[ Lpos ] <= A[ Rpos ] )
			TmpArray[ TmpPos++ ] = A[ Lpos++ ];
		else
			TmpArray[ TmpPos++ ] = A[ Rpos++ ];

	while( Lpos <= LeftEnd ) /* Copy rest of first half */
		TmpArray[ TmpPos++ ] = A[ Lpos++ ];
	while ( Rpos <= RightEnd ) /* Copy rest of second half */
		TmpArray[ TmpPos++ ] = A[ Rpos++ ];

	/* Copy TmpArray back */
	for (i = 0; i < NumElements; i++, RightEnd-- )
		A[ RightEnd ] = TmpArray[ RightEnd ];

}

/* Return median of left, Center and Right */
/* Order these and hide the pivot */
ElementType 
Median3( ElementType A[ ], int Left, int Right )
{
	int Center = ( Left + Right ) / 2;

	if ( A[ Left ] > A[ Center ] )
		Swap( &A[ Left ], &A[ Center ] );
	if ( A[ Left ] > A[ Right ] )
		Swap( &A[ Left ], &A[ Right ] );
	if ( A[ Center ] > A[ Right ] )
		Swap( &A[ Center ], &A[ Right ] );

	/* Invariant:A[ Left ] <= A[ Center ] <= A[ Right ] */

	Swap( &A[ Center ], &A[ Right - 1 ] );
	return A[ Right - 1 ];
}

void 
Qsort( ElementType A[ ], int Left, int Right )
{
	int i, j;
	ElementType Pivot;

	if ( Left + Cutoff <= Right )
	{
		Pivot = Median3( A, Left, Right );
		i = Left; j = Right - 1;
		for ( ; ; )
		{
			while ( A[ ++i ] < Pivot ) {}
			while ( A[ --j ] > Pivot ) {}
			if ( i < j )
				Swap( &A[ i ], &A[ j] );
			else
				break;
		}
		Swap( &A[ i ], &A[ Right - 1 ] ); /* Restore pivot */
		
		Qsort( A, Left, i - 1 );
		Qsort( A, i + 1, Right );
	}
	else /* Do an insertion sort on the subarray */
		InsertionSort( A + Left, Right - Left + 1 );
}

void
QuickSort( ElementType A[ ], int N )
{
	Qsort( A, 0, N - 1 );
}

文件名:main.c

#include "sort.h"
#include <stdio.h>

void
main()
{
	ElementType  A[ ] = { 34, 8, 64, 51, 32, 21 };
	printf( "Insertion sort: " );
	PrintElements( A, 6 );
	InsertionSort( A, 6 );
	printf( "Result: " );
	PrintElements( A, 6 ) ;
	printf( "\n" );

	ElementType B[ ] = { 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15, 8, 16 };
	printf( "Shell sort:");
	PrintElements( B, 16 );
	ShellSort( B, 16 );
	printf( "Result: " );
	PrintElements( B, 16 ) ;
	printf( "\n" );

	ElementType C[ ] = { 26, 41, 53, 97, 59, 58, 31 };
	printf( "Heap sort:");
	PrintElements( C, 7 ) ;
	HeapSort( C, 7 );
	printf( "Result: " );
	PrintElements( C, 7 ) ;
	printf( "\n" );

	ElementType D[ ] = { 13, 1, 24, 27, 15, 2, 28, 26 };
	printf( "Merge sort:" );
	PrintElements( D, 8 );
	MergeSort( D, 8 );
	printf( "Result: " );
	PrintElements( D, 8 ) ;
	printf( "\n" );

	ElementType E[ ] = { 13, 81, 92, 43, 31, 65, 57, 26, 75, 0 };
	printf( "Quick sort:" );
	PrintElements( E, 10 );
	QuickSort( E, 10 );
	printf( "Result: " );
	PrintElements( E, 10 ) ;
	printf( "\n" );
} 


附录:上述代码中用到了Error、FatalError等函数,其实现如下(即fatal.h文件):
#include <stdio.h>
#include <stdlib.h>

#define Error( Str )        FatalError( Str )
#define FatalError( Str )   fprintf( stderr, "%s\n", Str ), exit( 1 )

备注:本文摘自《数据结构与算法分析 C语言描述 Mark Allen Weiss著》,代码经gcc编译测试通过。

附件下载:http://download.csdn.net/detail/shuxiao9058/4212399#sort_20120407.tar.gz



你可能感兴趣的:(数据结构,c,算法,语言,PIVOT,merge)