数据结构学习笔记 --- 排序(选择排序、堆排序)

原创 2012年03月24日 21:07:01

1. 引言 


本文主要讲解一些常见的选择排序和堆排序。


2. 选择排序
选择排序的基本思想是每步从待排序的记录中选出排序码最小的记录,顺序存放在已排序的记录序列的后面,直到全部排完。选择排序中主要使用直接选择排序和堆排序。
直接选择排序(不稳定)
    直接选择排序的过程是:首先在所有记录中选出序码最小的记录,把它与第1个记录交换,然后在其余的记录内选出排序码最小的记录,与第2个记录交换......依次类推,直到所有记录排完为止。
    无论文件初始状态如何,在第i趟排序中选出最小关键字的记录,需要做n-i次比较,因此,总的比较次数为n(n-1)/2=O(n^2)。当初始文件为正序时,移动次数为0;文件初态为反序时,每趟排序均要执行交换操作,总的移动次数取最大值3(n-1)。直接选择排序的平均时间复杂度为O(n^2)。直接选择排序是不稳定的。
void Dir_Choose(int A[],int n)  //直接选择排序
{
    int k,t;
    for(int i=0;i<n-1;i++)
    {
        k=i;
        for(int j=i+1;j<n;j++)
        {
            if(A[j]<A[k]) k=j;
        }
        if(k!=i)
        {
            t=A[i];
            A[i]=A[k];
            A[k]=t;
        }
    }
}


#include "ds.h"

/* 选择排序的思想是:每一趟在 n-i+1(1<=i<n) 个记录中选取关键字最小的记录作为有序序列中的第i个记录。 */
/* 简单排序的操作为:通过 n-i 次关键字间的比较,从 n-i+1(1<=i<n) 个关键字中选出最小的记录,并和第i个记录交换之。 */

#define 	MAX_SIZE 	20			// 一个用作示例的小顺序表的最大长度
typedef		int			KeyType;	// 定义关键字类型为整型
typedef 	int 		InfoType; // 定义其它数据项的类型

#define 	EQ(a,b) 	((a)==(b))
#define 	LT(a,b) 	((a)<(b))
#define 	LQ(a,b) 	((a)<=(b))

struct RedType						// 记录类型
{
	KeyType		key;				// 关键字项
	InfoType	otherinfo;			// 其它数据项,具体类型在主程中定义
};

struct SqList						// 顺序表类型
{
	RedType		r[MAX_SIZE+1];		// r[0]闲置或用作哨兵单元
	int 		length;				// 顺序表长度
};

// 返回在L.r[i..L.length]中key最小的记录的序号
int SelectMinKey(SqList L, int i)
{
	KeyType min;
	int 	j, k;
	k = i;			// 设第i个为最小
	min = L.r[i].key;
	for (j = i + 1; j <= L.length; j++)
	{
		if (L.r[j].key < min)	// 找到更小的
		{
			k = j;
			min = L.r[j].key;
		}
	}
	return k;
}

// 对顺序表L作简单选择排序。算法10.9
void SelectSort(SqList &L)
{
	int i, j;
	RedType t;
	
	for (i = 1; i <= L.length; ++i)
	{//  选择第i小的记录,并交换到位
		
		j = SelectMinKey(L, i);			// 在L.r[i..L.length]中选择key最小的记录
		if (j != i)
		{
			// 与第i个记录交换
			t = L.r[i];
			L.r[i] = L.r[j];
			L.r[j] = t;
		}
	}
}

// 树形选择排序
void TreeSort(SqList &L)
{ 
   int i,j,j1,k,k1,l,n=L.length;
   RedType *t;
   l=(int)ceil(log(n)/log(2))+1; // 完全二叉树的层数
   k=(int)pow(2,l)-1; // l层完全二叉树的结点总数
   k1=(int)pow(2,l-1)-1; // l-1层完全二叉树的结点总数
   t=(RedType*)malloc(k*sizeof(RedType)); // 二叉树采用顺序存储结构
   for(i=1;i<=n;i++) // 将L.r赋给叶子结点
     t[k1+i-1]=L.r[i];
   for(i=k1+n;i<k;i++) // 给多余的叶子的关键字赋无穷大
     t[i].key=INT_MAX;
   j1=k1;
   j=k;
   while(j1)
   { // 给非叶子结点赋值
     for(i=j1;i<j;i+=2)
       t[i].key<t[i+1].key?(t[(i+1)/2-1]=t[i]):(t[(i+1)/2-1]=t[i+1]);
     j=j1;
     j1=(j1-1)/2;
   }
   for(i=0;i<n;i++)
   {
     L.r[i+1]=t[0]; // 将当前最小值赋给L.r[i]
     j1=0;
     for(j=1;j<l;j++) // 沿树根找结点t[0]在叶子中的序号j1
       t[2*j1+1].key==t[j1].key?(j1=2*j1+1):(j1=2*j1+2);
     t[j1].key=INT_MAX;
     while(j1)
     {
       j1=(j1+1)/2-1; // 序号为j1的结点的双亲结点序号
       t[2*j1+1].key<=t[2*j1+2].key?(t[j1]=t[2*j1+1]):(t[j1]=t[2*j1+2]);
     }
   }
   free(t);
}

void print(SqList L)
{
   	int i;
   	for(i = 1; i <= L.length; i++)
     	printf("(%d,%d)", L.r[i].key, L.r[i].otherinfo);
   	printf("\n");
}

#define N 8
int main()
{
   RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8}};
   SqList l;
   int i;
   for(i=0;i<N;i++)
     l.r[i+1]=d[i];
   l.length=N;
   printf("排序前:\n");
   print(l);
#if 0
   	SelectSort(l);
#else
	TreeSort(l);
#endif
   printf("排序后:\n");
   print(l);
}

3. 堆排序
 堆排序是一种树形选择排序,是对直接选择排序的有效改进。n个关键字序列
K1,K2,...,Kn称为堆,当且仅当该序列满足(Ki<=K2iKi<=K2i+1)(Ki>=K2iKi>=K2i+1),(1<=i<=n/2)。根结点(堆顶)的关键字是堆里所有结点关键字中最小者,称为小根堆;根结点的关键字是堆里所有结点关键字中最大者,称为大根堆。
    若将此序列所存储的向量R[1..n]看作是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
    堆排序的关键步骤有两个:一是如何建立初始堆;二是当堆的根结点与堆的最后一个结点交换后,如何对少了一个结点后的结点序列做调整,使之重新成为堆。堆排序的最坏时间复杂度为O(nlog2n),堆排序的平均性能较接近于最坏性能。由于建初始堆所需的比较 次数较多,所以堆排序不适宜于记录较少的文件。堆排序是就地排序,辅助空间为O(1),它是不稳定的排序方法。

#include "ds.h"

#define 	MAX_SIZE 	20			// 一个用作示例的小顺序表的最大长度
typedef		int			KeyType;	// 定义关键字类型为整型
typedef 	int 		InfoType; // 定义其它数据项的类型

#define 	EQ(a,b) 	((a)==(b))
#define 	LT(a,b) 	((a)<(b))
#define 	LQ(a,b) 	((a)<=(b))

struct RedType						// 记录类型
{
	KeyType		key;				// 关键字项
	InfoType	otherinfo;			// 其它数据项,具体类型在主程中定义
};

struct SqList						// 顺序表类型
{
	RedType		r[MAX_SIZE+1];		// r[0]闲置或用作哨兵单元
	int 		length;				// 顺序表长度
};

typedef 	SqList 		HeapType;

// 已知H.r[s..m]中记录的关键字除H.r[s].key之外均满足堆的定义,本函数
// 调整H.r[s]的关键字,使H.r[s..m]成为一个大顶堆(对其中记录的关键字而言)
void HeapAdjust(HeapType &H, int s, int m)
{
	RedType 	rc;
	int 		j;
	rc = H.r[s];
	// 沿key较大的孩子结点向下筛选
	for (j = 2*s; j <= m; j *= 2)
	{
		if (j < m && LT(H.r[j].key, H.r[j+1].key))
			++j;	// j为key较大的记录的下标
		if (!LT(rc.key, H.r[j].key))
			break;	// rc应插入在位置s上
		H.r[s] = H.r[j];
		s = j;
	}
	H.r[s] = rc;	// 插入
}

// 对顺序表H进行堆排序。算法10.11
void HeapSort(HeapType &H)
{
	RedType t;
	int 	i;
	for (i = H.length/2; i > 0; --i)	// 把H.r[1..H.length]建成大顶堆
		HeapAdjust(H, i, H.length);
	for (i = H.length; i > 1; --i)
	{// 将堆顶记录和当前未经排序子序列H.r[1..i]中最后一个记录相互交换
		t = H.r[1];
		H.r[1] = H.r[i];
		H.r[i] = t;
		HeapAdjust(H, 1, i-1); // 将H.r[1..i-1]重新调整为大顶堆
	}
}

void print(HeapType H)
{
   	int i;
   	for(i=1;i<=H.length;i++)
     	printf("(%d,%d)",H.r[i].key,H.r[i].otherinfo);
   	printf("\n");
}

#define N 8
int main()
{
   RedType d[N]={{49,1},{38,2},{65,3},{97,4},{76,5},{13,6},{27,7},{49,8}};
   HeapType h;
   int i;
   for(i=0;i<N;i++)
     h.r[i+1]=d[i];
   h.length=N;
   printf("排序前:\n");
   print(h);
   HeapSort(h);
   printf("排序后:\n");
   print(h);
}


C++代码,数据结构-内部排序-选择排序-堆排序

最简单的选择排序,的时间复杂度为N2;其主要操作就是比较,从减少次数出发来改进算法,书上提到了树形选择排序,但是有需要的辅助空间较多等缺点,为了弥补,威洛姆斯提出了另一种形式的选择排序——堆排序, ...
  • y519476132
  • y519476132
  • 2014年03月14日 21:35
  • 751

【数据结构与算法】内部排序之三:堆排序(含完整源码)

堆排序、快速排序、归并排序(下篇会写这两种排序算法)的平均时间复杂度都为O(n*logn)。要弄清楚堆排序,就要先了解下二叉堆这种数据结构。本文不打算完全讲述二叉堆的所有操作,而是着重讲述堆排序中要用...
  • mmc_maodun
  • mmc_maodun
  • 2014年03月04日 00:01
  • 22923

数据结构 — 堆排序

1、堆排序的时间复杂度与归并排序相同,O(nlogn)。堆排序的优势在与他只需要固定数量的额外空间,堆排序要比空间复杂性为O(n)的归并排序稍微慢一些,但是比空间复杂性为O(1)的归并排序要快。 2、...
  • PeersLee
  • PeersLee
  • 2015年12月20日 13:14
  • 841

数据结构示例——堆排序过程

完整算法见[例程],本文用一个例子,演示堆排序的过程。例:对{57, 40, 38, 11, 13, 34, 48, 75, 6, 19, 9, 7}进行堆排序的过程。算法如下:void HeapSo...
  • sxhelijian
  • sxhelijian
  • 2015年12月14日 15:43
  • 8126

数据结构 - 堆排序(heap sort) 详解 及 代码(C++)

堆排序(heap sort) 详解 及 代码(C++)本文地址: http://blog.csdn.net/caroline_wendy堆排序包含两个步骤: 第一步: 是建立大顶堆(从大到小排序)或小...
  • u012515223
  • u012515223
  • 2014年06月16日 11:10
  • 4258

C语言-数据结构-堆排序(heap sort)-源代码

1. 堆排序 堆排序的定义及思想可以参考百度百科:堆排序 用一句概括,堆排序就是一种改进的选择排序,改进的地方在于,每次做选择的时候,不单单把最大的数字选择出来,而且把排序过程中的一些操作进行了记录,...
  • kuweicai
  • kuweicai
  • 2017年01月24日 21:07
  • 1207

数据结构之排序--交换类排序

二  交换类排序    基本思想是:两两比较待排序记录的关键字,发现两记录的次序相反时即进行交换,直到没有反序的记录为止! 1. 冒泡排序     冒泡的原则:轻气泡不能在重气泡之下!     如何进...
  • year_9
  • year_9
  • 2013年11月17日 16:19
  • 730

堆排序算法原理及实现

堆排序是排序中一种比较重要的算法,和快速排序一样,其复杂度也是O(nlogn);同时也是一种原地排序算法:在任何时候,数组中只有常数个元素存储在输入数组以外。堆这种数据结构是处理海量数据比较常见的结构...
  • sunjunior
  • sunjunior
  • 2015年06月09日 22:20
  • 2076

【数据结构排序算法系列】数据结构八大排序算法

排序算法在计算机应用中随处可见,如Windows操作系统的文件管理中会自动对用户创建的文件按照一定的规则排序(这个规则用户可以自定义,默认按照文件名排序)因此熟练掌握各种排序算法是非常重要的,本博客将...
  • htq__
  • htq__
  • 2016年03月25日 22:36
  • 94228

快速排序和堆排序的使用场景比较

快速排序 是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο (n log n)次比较。在最坏状况下则需要 Ο (n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 ...
  • tumaolin94
  • tumaolin94
  • 2016年12月16日 15:35
  • 1778
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:数据结构学习笔记 --- 排序(选择排序、堆排序)
举报原因:
原因补充:

(最多只允许输入30个字)