C语言:L2-012 关于堆的判断 (25 分) — 小顶堆

一、题目

将一系列给定数字顺序插入一个初始为空的小顶堆H[]。随后判断一系列相关命题是否为真。命题分下列几种:

  • x is the rootx是根结点;
  • x and y are siblingsxy是兄弟结点;
  • x is the parent of yxy的父结点;
  • x is a child of yxy的一个子结点。

输入格式:

每组测试第1行包含2个正整数N(≤ 1000)和M(≤ 20),分别是插入元素的个数、以及需要判断的命题数。下一行给出区间[−10000,10000]内的N个要被插入一个初始为空的小顶堆的整数。之后M行,每行给出一个命题。题目保证命题中的结点键值都是存在的。

输出格式:

对输入的每个命题,如果其为真,则在一行中输出T,否则输出F。

输入样例:

5 4
46 23 26 24 10
24 is the root
26 and 23 are siblings
46 is the parent of 23
23 is a child of 10

输出样例:

F
T
F
T

二、方法1

1、思路

本题考察的是对堆的认识。

(1)知识点

① 完全二叉树

若设二叉树的深度为 h ,除第 h 层外,其它各层 (1 ~ h - 1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

例如:
在这里插入图片描述
一棵二叉树至多只有最下面的一层上的结点的度数可以小于2,并且最下层上的结点都集中在该层最左边的若干位置上,而在最后一层上,右边的若干结点缺失的二叉树,则此二叉树成为完全二叉树。

② 小顶堆和大顶堆

是一棵完全二叉树,其任何一非叶节点满足性质:
任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。

  • 小顶堆:堆顶的关键字是所有关键字中最小的
  • 大顶堆:堆顶的关键字是所有关键字中最大的
③ 堆的简单性质

堆的最大特点就是可以用数组来表示,不需要用指针来建立二叉树。
在这里插入图片描述
用数组来表示堆,只需要弄清楚三个问题:

  • 给定一个父亲的下标 x,能知道其左右孩子的下标;
  • 给定一个左孩子的下标 x,能知道其父亲和右兄弟的下标;
  • 给定一个右孩子的下标 x,能知道其左兄弟和父亲的下标。

通过数学证明,得出一个优美的论断:

  • 已知孩子的下标 x,其父亲的下标为 ( x - 1)// 2;
  • 已知父亲的下标 x,其左右孩子的下标分别为
    ( x + 1)* 2 - 1 ,( x + 1)* 2;
④ 创建堆
void creat(int x)
{
	heap[++cnt] = x;
	int t = cnt;
	while (t > 1 && heap[t] < heap[t / 2])
	{
		heap[t] = heap[t / 2];
		heap[t / 2] = x;
		t = t / 2;
	}
	heap[t] = x;
}

(2)题目的判断

① 判断命题输入

命题一: x is the root:x是根结点;
命题二: x and y are siblings:x和y是兄弟结点;
命题三: x is the parent of y:x是y的父结点;
命题四: x is a child of y:x是y的一个子结点。

主要是利用 scanf() 函数不能接收空格的特点来区分各个命题:

  1. 先用 scanf() 接收 x
  2. 然后用 scanf() 接收一次 str,判断 str 的首字母 是否为 a,若为 a,则为命题二
  3. 若不为 a,则再用 scanf() 接收一次 str,判断 str 的首字母 是否为 a,若为 a,则为命题四
  4. 若不为 a,则再用 scanf() 接收一次 str,判断 str 的首字母,若为 r,则为命题一;若为 p,则为命题三
② 判断命题真假
  1. x是根结点:a[x] == 1
  2. xy是兄弟结点:a[x] / 2 == a[y]
  3. xy的父结点:a[x] / 2 == a[y]
  4. xy的一个子结点:a[y] / 2 == a[x]

(3)易错点提示

由于题目上要求数的范围为 [−10000,10000],直接处理数据会导致数组溢出,测试点3发生段错误,这是因为有负数导致的结果,只需要将所有数据+10000 即可。

2、代码

#include<stdio.h>
#define max 20010

int heap[max];
int a[max];
int n, m;
int i, j, k;
int key, cnt = 0;
int x, y;
char str[50];

void creat(int x);

int main() 
{
	scanf("%d %d", &n, &m);
	for (i = 1; i <= n; i++) 
	{
		scanf("%d", &key);
		creat(key);
	}

	for (i = 1; i < max; i++)
	{
		a[heap[i] + 10000] = i;
	}

	while (m--)
	{
		scanf("%d ", &x);
		x += 10000;
		scanf("%s", str);
		if (str[0] == 'a')
		{
			scanf("%d", &y);
			y += 10000;
			if (a[x] / 2 == a[y] / 2)
				printf("T\n");
			else
				printf("F\n");
			scanf("%s %s", str, str);
		}
		else
		{
			scanf("%s", str);
			if (str[0] == 'a')
			{
				scanf("%s %s %d", str, str, &y);
				y += 10000;
				if (a[x] / 2 == a[y])
					printf("T\n");
				else
					printf("F\n");
			}
			else
			{
				scanf("%s", str);
				if (str[0] == 'r')
				{
					if (a[x] == 1)
						printf("T\n");
					else
						printf("F\n");
				}
				else
				{
					scanf("%s %d", str, &y);
					y += 10000;
					if (a[y] / 2 == a[x])
						printf("T\n");
					else
						printf("F\n");
				}
			}

		}
	}
	return 0;
}

void creat(int x)
{
	heap[++cnt] = x;
	int t = cnt;
	while (t > 1 && heap[t] < heap[t / 2])
	{
		heap[t] = heap[t / 2];
		heap[t / 2] = x;
		t = t / 2;
	}
	heap[t] = x;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WE-ubytt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值