排序 - 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语言版)<br>算法源码及运行演示系统使用说明<br>一、启动演示系统<br>双击演示系统应用程序文件“DS_VC_ALGO.EXE”启动演示系统,出现图1所示界面。<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>图1 《数据结构》(C语言版)算法源码及运行演示系统主界面<br>二、演示系统使用步骤<br>除了个别算法之外,演示系统给出了《数据结构》(C语言版)书中算法对应的程序代码(CPP文件)和测试运行程序(VC++6.0的EXE文件)。通过本系统,可以显示算法的源代码以及运行结果。具体操作步骤如下:<br>1.选择相应章<br>单击演示系统界面右侧章选择按钮。<br>例如,要选择第6章,则单击“第6章”选择按钮。<br>当相应章被选择后,窗口的右侧部分将列出本章的算法选择按钮。<br>例如,选择第6章后,窗口的右侧部分将显示第6章中的算法6.1-6.13和6.15的选择按钮。由于书中的算法6.14和6.16只是示意性算法,故未给出源码,其按钮上的文字为灰色,处于“无效”状态。<br>2.选择相应章中的算法<br>单击窗口右侧部分所列举的本章某个算法选择按钮,被选择算法的源码将在窗口左侧空白区域中显示。对于较长的源码,单击显示区域后,可用键盘的光标键和翻页键浏览源码。<br> 例如,选择了第6章中的算法6.5后界面如图2所示:<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>图2 选择算法6.5<br>3.运行测试程序<br>单击窗口上部的“运行”按钮,将弹出运行窗口,运行所选算法的测试程序。若运行按钮为灰色,表示该算法无单独测试程序。<br> 例如,算法6.5的测试运行窗口如图3所示:<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>图3 测试运行窗口<br>测试运行说明:<br>测试运行窗口显示程序的执行过程及结果。若在显示过程中出现运行窗口无法正常演示的情况,只需调节运行窗口大小即可正常显示(调节最小化按钮或窗口最大化/还原按钮“ ”)。<br>三、退出演示系统<br>使用完毕后,单击窗口右上角关闭按钮“ ”退出演示系统。<br>四、测试程序示例<br>在《数据结构》的课程教学中,各抽象数据类型的设计与实现是重要的学习和实践环节。为此,本系统只给出了各算法源码的测试程序的可执行文件。在此,给出算法6.5的测试程序示例,以供参考。<br>算法6.5是中序遍历线索二叉树的非递归算法,要对其源码进行测试,可首先调用算法6.6及6.7建立中序线索二叉树。以下是测试程序的源码,相关类型和辅助函数定义在文件include06.h和include06.cpp中,此略。<br>
算法C语言实现 (第1-4部分)基础知识、数据结构排序及搜索(原书第3版) 本书是Sedgewick彻底修订和重写的C算法系列的第一本。全书分为四部分,共16章。第一部分“基础知识”(第1—2章)介绍基本算法分析原理。第二部分“数据结构”(第3~5章)讲解算法分析中必须掌握的数据结构知识,主要包括基本数据结构、抽象数据结构、递归和树。第三部分“排序”(第6~11章)按章节顺序分别讨论基本排序方法(如选择排序、插入排序、冒泡排序、希尔排序等)、快速排序方法、归并和归并排序方法、优先队列与堆排序方法、基数排序方法以及特殊用途的排序方法,并比较了各种排序方法的性能特征。第四部分“搜索”(第12~16章)在进一步讲解符号表、树等抽象数据类型的基础上,重点讨论散列方法、基数搜索以及外部搜索方法。 书中提供了用C语言描述的完整算法源程序,并且配有丰富的插图和练习。作者用简洁的实现将理论和实践成功地结合了起来,这些实现均可在真实应用上测试,使得本书自问世以来备受程序员的欢迎。 本书可作为高等院校计算机相关专业算法数据结构课程的教材和补充读物,也可供自学之用。 目录 出版者的话 译者序 前言 第一部分 基础知识  第1章 引言   1.1 算法   1.2 典型问题——连通性   1.3 合并一查找算法   1.4 展望   1.5 主题概述  第2章 算法分析的原理   2.1 实现和经验分析   2.2 算法分析   2.3 函数的增长   2.4 大O符号   2.5 基本递归方程   2.6 算法分析示例   2.7 保证、预测及局限性 第二部分 数据结构  第3章 基本数据结构   3.1 构建组件   3.2 数组   3.3 链表   3.4 链表的基本处理操作   3.5 链表的内存分配   3.6 字符串   3.7 复合数据结构  第4章 抽象数据类型   4.1 抽象对象和对象集   4.2 下推栈ADT   4.3 栈ADT客户示例   4.4 栈ADT的实现   4.5 创建一个新ADT   4.6 FIFO队列和广义队列   4.7 复制和索引项   4.8 一级ADT   4.9 基于应用的ADT示例   4.10 展望  第5章 递归与树   5.1 递归算法   5.2 分治法   5.3 动态规划   5.4 树   5.5 树的数学性质   5.6 树的遍历   5.7 递归二叉树算法   5.8 图的遍历   5.9 综述 第三部分 排序  第6章 基本排序方法   6.1 游戏规则   6.2 选择排序   6.3 插入排序   6.4 冒泡排序   6.5 基本排序方法的性能特征   6.6 希尔排序   6.7 对其他类型的数据进行排序   6.8 索引和指针排序   6.9 链表排序   6.10 关键字索引统计  第7章 快速排序   7.1 基本算法   7.2 快速排序算法的性能特征   7.3 栈大小   7.4 小的子文件   7.5 三者取中划分   7.6 重复关键字   7.7 字符串和向量   ……  第8章 归并与归并排序  第9章 优先队列和堆排序  第10章 基数排序  第11章 特殊用途的排序方法 第四部分 搜索  第12章 符号表和二叉搜索树  第13章 平衡树  第14章 散列  第15章 基数搜索  第16章 外部搜索
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值