树形选择排序(第十章 P279)

树形选择排序

 

概述:

树形选择排序(又称锦标赛排序),是一种按照锦标赛的思想进行选择排序的方法。首先对n个记录的关键字进行两两比较,然后在n/2个较小者之间再进行两两比较,如此重复,直至选出最小的记录为止。

 

排序过程:

 

树形选择排序(Tree Selection Sort),这个过程可用一棵有n个叶子结点的完全二叉树表示。例如,图中的二叉树表示从8个数中选出最小数的过程。

8个叶子结点中依次存放排序之前的8个关键字,每个非终端结点中的关键字均等于其左、右孩子结点中较小的那个关键字,则根结点中的关键字为叶子结点中的最小关键字

 

 

在输出最小关键字之后,根据关系的可传递性,欲选出次小关键字,仅需将叶子结点中的最小关键字(13)改为“最大值”,然后从该叶子结点开始,和其左右兄弟的关键字进行比较,修改从叶子结点到根结点的路径上各结点的关键字,则根结点的关键字即为次小值

 

同理,可依次选出从小到大的所有数。

 

 

 

算法性能:

 

时间复杂度:由于含有n个叶子结点的完全二叉树的深度为[log2n]+1,则在树形选择排序中,除了第一次选择最小关键字需要进行 n-1 次比较以外,每选择一个次小关键字仅需进行[log2n]次比较,因此,它的时间复杂度为O(nlogn)。

空间复杂度:这种排序方法减少了许多排序时间,但是使用了较多的附加存储。

如果有 n 个对象,必须使用至少 2n-1 个结点来存放。最多需要找到满足   的 k ,使用  个结点。每个结点包括排序码、结点序号和比较标志三种信息。所以需要的辅助存储空间较多。

 

 

代码:

#include<malloc.h> /* malloc()等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<process.h> /* exit() */


typedef int InfoType; /* 定义其它数据项的类型 */

#define INT_MAX 999

/* ---------------------------    待排记录的数据类型     ------------------------------*/

#define MAXSIZE 20 /* 一个用作示例的小顺序表的最大长度 */
typedef int KeyType; /* 定义关键字类型为整型 */
typedef struct
{
	KeyType key; /* 关键字项 */
	InfoType otherinfo; /* 其它数据项,具体类型在主程中定义 */
}RedType; /* 记录类型 */

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


/* ------------------------------------------------------------------------------------------*/


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
void 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);
	TreeSort(&l);
	printf("排序后:\n");
	print(l);
}

运行结果:

  • 7
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值