堆的创建,关于堆的判断,堆排序(又是刷Pat的一天,pat使我精神焕发) PAT L2-012 关于堆的判断

一.首先,还是一样的套路,我们必须要知道堆是什么?

堆,是一种经过排序的完全二叉树。
之前的总结里总结了二叉树
而这是一棵完全二叉树。
堆分为大顶堆和小顶堆。
大顶堆就是 根节点比其他结点都大(注意与二叉排序树的区别:二叉排序树:可能是一棵空树,不是空树的话左子树比根结点小,右子树比根结点大,并且左右子树同样是二叉排序树。)
小顶堆就是 根结点比其他结点都小(并且左右子树也同样是堆,也是一种递归的定义:一棵树是大顶堆(小顶堆),左右子树(不为空)也是大顶堆(小顶堆)。

二.堆的存储结构

前面提到堆是完全二叉树,那么堆就可以用数组来存储了。

那么堆可以来干什么呢?可以用来堆排序。
还有做我们的L2-012关于堆的判断的题目。

三.构造堆

第一步:我们必须要构造堆:

1.最后一个非叶子节点(从最后一个非叶子点开始构造)

对于网上的一些构造堆的方法,他们要找到最后一个非叶子节点,那么最后一个非叶子节点就是:长度/2-1
这里我来解释一下,为什么?
答:

需要分两种情况:
①堆的最后一个非叶子节点若只有左孩子

②堆的最后一个非叶子节点有左右两个孩子

完全二叉树的节点序号为i,它的左孩子序号为2i,右孩子序号为2i+1。
对于①左孩子的序号为n-1,则n-1=2*i,推出i=(n-1)/2-1;注意了:这里关系到取整的问题,这种情况树的结点数是偶数,n是奇数,n-1是偶数,由于很多语言默认向下取整,因此此时 (n-1)/2 -1= n/2 -1;

对于②左孩子的序号为n-2,在n-2=2i,推出i=n/2-1;右孩子的序号为n-1,则n-1=2i+1,推出i=n/2-1;
所以最后一个非叶子节点就是:长度/2-1
实现代码,可以参考其他大佬的博客。
推荐博客:
https://www.cnblogs.com/lanhaicode/p/10546257.html
2.上浮法构造(从第一个子节点开始构造)
方法如下:
从第一个子节点开始遍历,与其双亲结点比较,如果比双亲结点大的情况下,就上浮到双亲结点,此时双亲结点继续上浮,直到根节点。(注意这是一次循环,还需要循环n-1次)。注意找双亲结点,子节点的编号为i,双亲结点的编号为i/2(i>1).因此其数组下标就是(n-1)/2.而构造堆中使用的也是下标。
因此代码如下:

#include<stdio.h>//完全二叉树可以用数组来存就比较方便
int arr[1005];
int FindParent(int n)
{
    return (n-1)/2;//返回的是数组序号,数组序号与完全二叉树的结点编号差1.(结点编号比数组序号多1)
}
//从第一个子节点开始遍历,与其父节点比较,如果比父节点大的情况下,就上浮到父节点,此时父节点继续上浮,直到根节点。
void BuiltH(int *arr,int n)//建堆
{
    for (int i=1;i<n;i++)
    {
        int t = i;
        while (t!=0&&arr[t]<arr[FindParent(t)])
        {
            int temp;
            temp = arr[t];
            arr[t] = arr[FindParent(t)];
            arr[FindParent(t)] = temp;
            t = FindParent(t);
        }
    }

}

这种构造方法比较容易理解,上述代码构造的是小顶堆。大顶堆的构造也只是改个比较符合。
堆构造好了,那就先来我们的应用一:

四.堆排序:

堆排序的过程也可以看看其他大佬的博客
推荐博客:
https://www.cnblogs.com/lanhaicode/p/10546257.html
下面是堆排序代码:

#include<stdio.h>//完全二叉树可以用数组来存就比较方便
int arr[1005];

int FindParent(int n)
{
    return (n-1)/2;
}
//从第一个子节点开始遍历,与其父节点比较,如果比父节点大的情况下,就上浮到父节点,此时父节点继续上浮,直到根节点。
void BuiltH(int *arr,int n)//建堆
{
    for (int i=1;i<n;i++)
    {
        int t = i;
        while (t!=0&&arr[t]<arr[FindParent(t)])
        {
            int temp;
            temp = arr[t];
            arr[t] = arr[FindParent(t)];
            arr[FindParent(t)] = temp;
            t = FindParent(t);
        }
    }

}
void swap(int *arr,int n)//交换数组末尾与堆顶的元素
{
     int tmp = arr[0];
     arr[0]=arr[n];
     arr[n]=arr[0];
}
int main()
{
    int len;
    scanf("%d",&len);
    for (int i=0;i<len;i++{
         scanf("%d",arr[i]);
    }
    for (int i=len;i>=0;i--)//从末尾开始。一个一个的移走,再构造剩下的数组元素的堆,再移走。
    {
         BuiltH(arr,i);
         swap(arr,i);
    }
    for (i=0;i<len;i++)
    {
         printf("%d\n",arr[i]);
    }
    return 0;
}
     

五:Pat L2-012 关于堆的判断

先看题目,学了那么堆的知识,可以拿这道题目来练练手:
将一系列给定数字顺序插入一个初始为空的小顶堆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

解法:构造堆,然后再判断命题,对于命题的输入也特别有讲究,而如果全部看做字符串输入也比较麻烦,当然可以使用其他编程语言提供的split函数来分割字符串,也会比较方便,但我们这里使用的是c语言,就会混合着数字和字符串的输入,(记住,必须把一行所有字符串,数字都输入进来(在一次判断中),不然可能会出现scanf读取同一行的字符串。而一个命题调用了两次的情况,也可以使用gets(),但也要保证所有字符串,数字都输入进来(在一次判断中),所以这就需要多次用gets(),其实还不如用scanf,让它把数字和与数字挨着的字符串输入,会更加方便)再用字符串的不同来判断是哪一种命题,用数字x和y调用哪一种函数。这里有四个命题,就有四个函数.。
下面就看代码:

#include<stdio.h>//完全二叉树可以用数组来存就比较方便
int arr[1005];
int N;
int FindParent(int n)
{
    return (n-1)/2;
}
//从第一个子节点开始遍历,与其父节点比较,如果比父节点大的情况下,就上浮到父节点,此时父节点继续上浮,直到根节点。
void BuiltH(int *arr,int n)//建堆
{
    for (int i=1;i<n;i++)
    {
        int t = i;
        while (t!=0&&arr[t]<arr[FindParent(t)])
        {
            int temp;
            temp = arr[t];
            arr[t] = arr[FindParent(t)];
            arr[FindParent(t)] = temp;
            t = FindParent(t);
        }
    }

}
void IsRoot(int x)//编号为i的结点存在数组下标为i-1的地方,因此得到数组下标后要加一。
{
    if (x==arr[0])
    {
        printf("T\n");
    }
    else
    {
        printf("F\n");
    }

}
void IsSiblings(int x,int y)
{
    int a=0;
    int b=0;
    for (int i=0;i<N;i++)
    {
        if (arr[i]==x)
        {
            a =i+1;
        }
        if (arr[i]==y)
        {
            b = i+1;
        }
    }
    if (a>b)
    {
        int t = a;
        a = b;
        b = t;
    }
    if (a%2==0&&(b-a)==1)
    {
        printf("T\n");
    }
    else
    {
        printf("F\n");
    }

}
void IsParent(int x,int y)
{
    int a=0;
    int b=0;
    for (int i=0;i<N;i++)
    {
        if (arr[i]==x)
        {
            a =i+1;
        }
        if (arr[i]==y)
        {
            b = i+1;
        }
    }
    if (2*a==b||(2*a+1)==b)
    {
        printf("T\n");

    }
    else
    {
        printf("F\n");
    }


}
void IsSon(int x,int y)
{
    int a=0;
    int b=0;
    for (int i=0;i<N;i++)
    {
        if (arr[i]==x)
        {
            a =i+1;
        }
        if (arr[i]==y)
        {
            b = i+1;
        }
    }
    if (a==2*b||a==(2*b+1))
    {
        printf("T\n");

    }
    else
    {
        printf("F\n");
    }


}

int main()//注意输入格式。
{
    int M;
    scanf("%d %d",&N,&M);
    for (int i=0;i<N;i++)//不要在循环体里改变 i的值
    {
        scanf("%d",&arr[i]);
    }
    BuiltH(arr,N);
    int x=0;
    int y=0;
    char ch[100];
    for (int i=0;i<M;i++)
    {
        scanf("%d %s",&x,ch);
        getchar();
        if (ch[0]=='a')//观察规律,写if语句。
        {
            scanf("%d %s %s",&y,ch,ch);
            IsSiblings(x,y);
        }
        else
        {
            scanf("%s",ch);
            getchar();
            if (ch[0]=='a')
            {
                scanf("%s %s %d",ch,ch,&y);
                IsSon(x,y);

            }
            else
            {
                scanf("%s",ch);
                getchar();
                if (ch[0]=='r')
                {
                    IsRoot(x);
                }
                else
                {
                    scanf("%s %d",ch,&y);
                    IsParent(x,y);
                }

            }
        }
		
    }
    return 0;



}






今天的总结就这样了,困了,需要睡觉了,拜拜。
如果有错误,希望大佬指正。非常感谢。

发布了16 篇原创文章 · 获赞 12 · 访问量 1469

猜你喜欢

转载自blog.csdn.net/weixin_45290352/article/details/105698049