<堆及堆排序>题目练习《数据结构(C语言版)》

目录

题目:

【单选题】

1.下列关于堆的叙述错误的是( )

2.下列关键字序列中,序列( )是堆。

3.下列关于向下调整算法的说法正确的是( )

4.在一个堆中,下标为 i(i > 0) 的结点的左右孩子结点及父结点的下标分别是(   )

5.将一个顺序表整理成堆的时间复杂度为(   )

【编程题】

6.堆的实现

7.堆排序实现

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知


题目:

【单选题】

1.下列关于堆的叙述错误的是( )

A.堆是一种完全二叉树

B.堆通常使用顺序表存储

C.小堆指的是左右孩子结点都比根结点小的堆

D.堆的删除是将尾部结点放到队顶后执行向下调整算法

2.下列关键字序列中,序列( )是堆。

A.{16,72,31,23,94,53}

B.{94,23,31,72,16,53}

C.{16,53,23,94,31,72}

D.{16,23,53,31,94,72}

 3.下列关于向下调整算法的说法正确的是( )

A.构建堆的时候要对每个结点都执行一次

B.删除操作时要执行一次

C.插入操作时要执行一次

D.以上说法都不正确

4.在一个堆中,下标为 i(i > 0) 的结点的左右孩子结点及父结点的下标分别是(   )

A.2 i、2 i + 1、i /2

B.2i、2i + 1、(i - 1)/2

C.2i + 1、2i + 2、(i - 1)/2

D.2i + 1、2i + 2、i/2-1

5.将一个顺序表整理成堆的时间复杂度为(   )

A.O(nlogn)

B.O(logn)

C.O(1)

D.O(n)

【编程题】

6.堆的实现

7.堆排序实现

解析:

1.答案:C

解析:大堆小堆所表示的意思是堆顶元素为最大值/最小值。并且每一个子结构也都满足这样的性质。

2.答案:D

解析:选项D是一个小根堆。

3.答案:B

解析:A: 建堆时,每一个非叶子节点都要执行一次向下调整算法。

B: 删除元素时,首先交换堆顶元素的堆尾元素,删除堆尾元素,然后执行向下调整算法。

C: 插入操作需要执行向上调整算法。

4.

5.答案:D

解析:建立堆的顺序是bottom-top的。 

正确的证明方法应当如下:

A.具有n个元素的平衡二叉树,树高为㏒n,我们设这个变量为h。

B.最下层非叶节点的元素,只需做一次线性运算便可以确定大根,而这一层具有2^(h-1)个元素,我们假定O(1)=1,那么这一层元素所需时间为2^(h-1) × 1。

C.由于是bottom-top建立堆,因此在调整上层元素的时候,并不需要同下层所有元素做比较,只需要同其中之一分支作比较,而作比较次数则是树的高度减去当前节点的高度。因此,第x层元素的计算量为2^(x) × (h-x)。

D.又以上通项公式可得知,构造树高为h的二叉堆的精确时间复杂度为: 

S = 2^(h-1) × 1 + 2^(h-2) × 2 + …… +1 × (h-1) ①

E.通过观察第四步得出的公式可知,该求和公式为等差数列和等比数列的乘积,因此用错位相减法求解,给公式左右两侧同时乘以2,可知: 

2S = 2^h × 1 + 2^(h-1) × 2+ …… +2 × (h-1) ②

用②减去①可知: S =2^h × 1 - h +1 ③

将h = ㏒n 带入③,得出如下结论:

S = n - ㏒n +1 = O(n)

6.

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity; 
}Heap;
 
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
 
// TopK问题:找出N个数里面最大/最小的前K个问题。
// 比如:未央区排名前10的泡馍,西安交通大学王者荣耀排名前10的韩信,全国排名前10的李白。等等问题都是Topk问题,
// 需要注意:
// 找最大的前K个,建立K个数的小堆
// 找最小的前K个,建立K个数的大堆
void PrintTopK(int* a, int n, int k);
void TestTopk();

#include "Heap.h"
 
void Swap(HPDataType* x1, HPDataType* x2)
{
	HPDataType x = *x1;
	*x1 = *x2;
	*x2 = x;
}
 
void AdjustDown(HPDataType* a, int n, int root)
{
	int parent = root;
	int child = parent*2+1;
	while (child < n)
	{
		// 选左右孩纸中大的一个
		if (child+1 < n 
			&& a[child+1] > a[child])
		{
			++child;
		}
 
		//如果孩子大于父亲,进行调整交换 
		if(a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent*2+1;
		}
		else
		{
			break;
		}
	}
}
 
void AdjustUp(HPDataType* a, int n, int child)
{
	int parent;
	assert(a);
	parent = (child-1)/2;
	//while (parent >= 0)
	while (child > 0)
	{
        //如果孩子大于父亲,进行交换
		if (a[child] > a[parent])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child-1)/2;
		}
		else
		{
			break;
		}
	}
}
 
void HeapInit(Heap* hp, HPDataType* a, int n)
{
	int i;
	assert(hp && a);
	hp->_a = (HPDataType*)malloc(sizeof(HPDataType)*n);
	hp->_size = n;
	hp->_capacity = n;
 
	for (i = 0; i < n; ++i)
	{
		hp->_a[i] = a[i];
	}
 
	// 建堆: 从最后一个非叶子节点开始进行调整
    // 最后一个非叶子节点,按照规则: (最后一个位置索引 - 1) / 2
    // 最后一个位置索引: n - 1
    // 故最后一个非叶子节点位置: (n - 2) / 2
	for(i = (n-2)/2; i >= 0; --i)
	{
		AdjustDown(hp->_a, hp->_size, i);
	}
}
 
void HeapDestory(Heap* hp)
{
	assert(hp);
 
	free(hp->_a);
	hp->_a = NULL;
	hp->_size = hp->_capacity = 0;
}
 
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
    //检查容量
	if (hp->_size == hp->_capacity)
	{
		hp->_capacity *= 2;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType)*hp->_capacity);
	}
	//尾插
	hp->_a[hp->_size] = x;
	hp->_size++;
	//向上调整
	AdjustUp(hp->_a, hp->_size, hp->_size-1);
}
 
void HeapPop(Heap* hp)
{
	assert(hp);
    //交换
	Swap(&hp->_a[0], &hp->_a[hp->_size-1]);
	hp->_size--;
	//向下调整
	AdjustDown(hp->_a, hp->_size, 0);
}
 
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	return hp->_a[0];
}
 
int HeapSize(Heap* hp)
{
	return hp->_size;
}
 
int HeapEmpty(Heap* hp)
{
	return hp->_size == 0 ? 0 : 1;
}
 
void HeapPrint(Heap* hp)
{
	int i;
	for (i = 0; i < hp->_size; ++i)
	{
		printf("%d ", hp->_a[i]);
	}
	printf("\n");
}
 
//TopK问题: 找出N个数里面最大/最小的前K个问题。
//这里实现两个版本:
//1. 找最大的K个元素
//假设堆为小堆
void PrintTopK(int* a, int n, int k)
{
    Heap hp;
    //建立含有K个元素的堆
	HeapInit(&hp, a, k);
 
    for (size_t i = k; i < n; ++i)  // N
	{
        //每次和堆顶元素比较,大于堆顶元素,则删除堆顶元素,插入新的元素
		if (a[i] > HeapTop(&hp)) // LogK
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
    for(int i = 0; i < k; ++i){
        printf("%d ",HeapTop(&hp));
        HeapPop(&hp);
    }
}
 
//2. 找最小的K个元素
//假设堆为大堆
void PrintTopK(int* a, int n, int k)
{
    Heap hp;
    //建立含有K个元素的堆
	HeapInit(&hp, a, k);
 
    for (size_t i = k; i < n; ++i)  // N
	{
        //每次和堆顶元素比较,小于堆顶元素,则删除堆顶元素,插入新的元素
		if (a[i] < HeapTop(&hp)) // LogK
		{
			HeapPop(&hp);
			HeapPush(&hp, a[i]);
		}
	}
    for(int i = 0; i < k; ++i){
        printf("%d ",HeapTop(&hp));
        HeapPop(&hp);
    }
}
void TestTopk()
{
    int n = 10000;
	int* a = (int*)malloc(sizeof(int)*n);
	srand(time(0));
    //随机生成10000个数存入数组,保证元素都小于1000000
	for (size_t i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
    //确定10个最大的数
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;
    
     PrintTopK(int* a, n, 10);
}

7.

// 对数组进行堆排序
void HeapSort(int* a, int n);
// 排升序
void HeapSort(int* a, int n)
{

	// 建大堆 -》
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		// 选出次大的
		AdjustDown(a, end, 0);
		--end;
	}
}

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                           ——By 作者:新晓·故知

猜你喜欢

转载自blog.csdn.net/m0_57859086/article/details/124055479