关于堆的判断
将一系列给定数字顺序插入一个初始为空的小顶堆H[]
。随后判断一系列相关命题是否为真。命题分下列几种:
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的一个子结点。
输入格式:
每组测试第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.判断是否为根节点,因为只需取一个数字;2.剩下三种情况,都需取两个数字)
- 在小顶堆数组中查找上一步提取的数字下标。
- 根据小顶堆数组下标规则判断命题是否正确。
代码展示:
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
int parent(int n)//修改数组下标
{
return (n-1)/2;
}
void buildHeap(int n , int data[])//建立小顶堆
{
for(int i= 1; i < n ; i++)
{
int t = i;
while( t != 0 && data[parent(t)] > data[t])
{
int temp = data[t];
data[t] = data[parent(t)];
data[parent(t)] = temp;
t = parent(t);
}
}
}
int main()
{
int data[1000],n,m;
string s;
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>data[i];
buildHeap(n,data);
for(int i=0;i<m;i++)
{
if(i==0)
getchar();
getline(cin,s);
if(s.find("root")<s.length())//判断是否是根节点
{
int num=0;
bool f=0;
for(int j=0;j<6;j++)//因为整数在[-10000,10000]中,故加上负号最多处理六位
if(s[j]-'0'>=0&&s[j]-'0'<=9)
num=num*10+s[j]-'0';
else if(s[j]=='-')//若有负号则f=1
f=1;
else
break;
if(f)//若f=1,数字为负,则num取反
num=-num;
if(data[0]==num)
cout<<"T"<<endl;
else
cout<<"F"<<endl;
}
else //判断是否是兄弟节点/父节点/子节点
{
int num1=0,num2=0,m1=-1,m2=-1,j;
bool f=0;
for(j=0;j<6;j++)
if(s[j]-'0'>=0&&s[j]-'0'<=9)
num1=num1*10+s[j]-'0';
else if(s[j]=='-')//若有负号则f=1
f=1;
else
break;
if(f)//如果有负号则num1取反
num1=-num1;
f=0;
//处理第二个数字
if(s.find("parent")<s.length())//处理parent情况
for(int k=j+18;k<j+24;k++)
if(s[k]-'0'>=0&&s[k]-'0'<=9)
num2=num2*10+s[k]-'0';
else if(s[k]=='-')
f=1;
else
break;
else if(s.find("child")<s.length())//处理child情况
for(int k=j+15;k<j+21;k++)
if(s[k]-'0'>=0&&s[k]-'0'<=9)
num2=num2*10+s[k]-'0';
else if(s[k]=='-')
f=1;
else
break;
else
for(int k=j+5;k<j+10;k++)//处理是否兄弟节点情况
if(s[k]-'0'>=0&&s[k]-'0'<=9)
num2=num2*10+s[k]-'0';
else if(s[k]=='-')
f=1;
else
break;
if(f)//如果有负号则num2取反
num2=-num2;
for(int j=0;j<n;j++)//求两个数在小顶堆数组中的下标
if(data[j]==num1)
m1=j;
else if(data[j]==num2)
m2=j;
//cout<<m1<<m2;
if((m1-1)/2==m2&&s.find("child")<s.length())//判断num1是否是num2孩子
cout<<"T"<<endl;
else if((m2-1)/2==m1&&s.find("parent")<s.length())//判断num1是否是num1父节点
cout<<"T"<<endl;
else if(abs(m1-m2)==1&&(m2-1)/2==(m1-1)/2&&s.find("siblings")<s.length())//判断是否是兄弟节点
cout<<"T"<<endl;
else//三种情况都不是
cout<<"F"<<endl;
}
}
}
PTA测试结果:
相关知识补充:
1.堆是一种非线性结构,(本篇随笔主要分析堆的数组实现)可以把堆看作一个数组,也可以被看作一个完全二叉树,通俗来讲堆其实就是利用完全二叉树的结构来维护的一维数组,按照堆的特点可以把堆分为大顶堆和小顶堆。
大顶堆:每个结点的值都大于或等于其左右孩子结点的值
小顶堆:每个结点的值都小于或等于其左右孩子结点的值
我们用简单的公式来描述一下堆的定义就是:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
参考:浅谈大顶堆与小顶堆
2.如何创建小顶堆序列数组
参考:小顶堆构建过程