神奇的树

       建堆以及堆排序(最小堆)

#include <stdio.h>
int h[101];  //用来存放堆的数组
int n;  //用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y)
{
    int t;
    t=h[x];
    h[x]=h[y];
    h[y]=t;

}
//向下调整函数
void siftdown(int i)  //传入一个需要向下调整的结点编号i
{
    int t,flag=0; //flag用来标记是否需要继续向下调整
    //当i结点有儿子(其实是至少有左儿子的情况下)并且有需要继续调整的时候,循环就执行
    while(i*2<=n&&flag==0)
    {
        //首先判断它和左儿子的关系,并用t记录值较小的结点编号
         if(h[i]>h[i*2])
            t=i*2;
        else
            t=i;
        //如果它有右儿子,再对右儿子进行讨论
        if(i*2+1<=n)
        {
           //如果右儿子的值更小,更新较小的结点编号
           if(h[t]>h[i*2+1])
            t=i*2+1;
        }
        //如果发现最小的结点编号不是自己,说明子结点中有比父节点更小的
        if(t!=i)
        {
           swap(t,i); //交换他们,注意swap函数需要自己来写
            i=t; //更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
        }
        else
            flag=1; //否则说明当前的父结点已经比两个子结点都要小了,不需要再进行调整了
    }
}
//建立堆的函数
void creat()
{
    int i;
    //从最后一个非叶结点到第一个结点依次进行向上调整
    for(i=n/2;i>=1;i--)
    {
        siftdown(i);
    }
}
//删除最大的元素
int deletemax()
{
    int t;
    t=h[1]; //用一个临时变量记录堆顶点的值
    h[1]=h[n]; //将堆的最后一个点赋值到堆顶
    n--; //堆的元素减少1
    siftdown(1); //向下调整
    return t; //返回之前记录的堆的顶点的最大值
}
int main() {
    int i,num;
    //读入要排序的数字的个数
    scanf("%d",&num);

    for(i=1;i<=num;i++)
        scanf("%d",&h[i]);
    n=num;

    //建堆
    creat();

    //删除顶部元素,连续删除n次,其实也就是从大到小把数输出来
    for(i=1;i<=num;i++)
        printf("%d ",deletemax());

    getchar();getchar();
    return 0;
}

        建堆以及堆排序(最大堆)

#include <stdio.h>
int h[101];  //用来存放堆的数组
int n;  //用来存储堆中元素的个数,也就是堆的大小

//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y)
{
    int t;
    t=h[x];
    h[x]=h[y];
    h[y]=t;

}
//向下调整函数
void siftdown(int i)  //传入一个需要向下调整的结点编号i
{
    int t,flag=0; //flag用来标记是否需要继续向下调整
    //当i结点有儿子(其实是至少有左儿子的情况下)并且有需要继续调整的时候,循环就执行
    while(i*2<=n&&flag==0)
    {
        //首先判断它和左儿子的关系,并用t记录值较大的结点编号
         if(h[i]<h[i*2])
            t=i*2;
        else
            t=i;
        //如果它有右儿子,再对右儿子进行讨论
        if(i*2+1<=n)
        {
           //如果右儿子的值更大,更新较大的结点编号
           if(h[t]<h[i*2+1])
            t=i*2+1;
        }
        //如果发现最大的结点编号不是自己,说明子结点中有比父节点更大的
        if(t!=i)
        {
           swap(t,i); //交换他们,注意swap函数需要自己来写
            i=t; //更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
        }
        else
            flag=1; //否则说明当前的父结点已经比两个子结点都要大了,不需要再进行调整了
    }
}
//建立堆的函数
void creat()
{
    int i;
    //从最后一个非叶结点到第一个结点依次进行向上调整
    for(i=n/2;i>=1;i--)
    {
        siftdown(i);
    }
}
//堆排序
void heapsort()
{
    while(n>1)
    {
        swap(1,n);
        n--;
        siftdown(1);
    }
}
int main()
{
    int i,num;
    //读入要排序的数字的个数
    scanf("%d",&num);

    for(i=1;i<=num;i++)
        scanf("%d",&h[i]);
    n=num;

    //建堆
    creat();

    //堆排序
    heapsort();

    //输出
    for(i=1;i<=num;i++)
        printf("%d ",h[i]);

    getchar();getchar();
    return 0;
}

      擒贼先擒王——并查集

#include<stdio.h>

int f[1000]={0},n,m,k,sum=0;
//这里是初始化,非常的重要,数组里面存的是自己数组下标的编号就好了
void init()
{
    int i;
    for(i=1;i<=n;i++)
        {
            f[i]=i;
        }
}
//这是找爹的递归函数,不停地去找爹,直到找到祖宗为止,其实就是去找犯罪团伙的最高领导人,“擒贼先擒王”原则
int getf(int v)
{
    if(f[v]==v)
    return v;
    else
    {
    /*这里是路径压缩,每次在函数返回的时候,顺带把路上遇到的人的“BOSS”改为最后找到的祖宗编号,
    也就是犯罪团伙的最高领导人编号。这样可以提高今后找到犯罪团伙的最高领导人(其实就是树的祖先)的速度。*/
        f[v]=getf(f[v]);
        return f[v];
    }
}
//这里是合并两子集合的函数
void merge(int v,int u)
{
    int t1,t2;
    t1=getf(v);
    t2=getf(u);
    if(t1!=t2) //判断两个结点是否在同一个集合中,即是否为同一个祖先
    {
        f[t2]=t1;
        //“靠左”原则,左边变成右边的BOSS。即把右边的集合,作为左边集合的子集和。
        //经过路径压缩以后,将f[u]的根的值也赋值为v的祖先f[t1]
    }
}

//请从此开始阅读程序,从主函数开始阅读程序是一个好习惯
int main()
{
    int  i,x,y;
    scanf("%d %d",&n,&m);

    //初始化是必须的
    init();
    for(i=1;i<=m;i++)
    {
        //开始合并犯罪团伙
        scanf("%d %d",&x,&y);
        merge(x,y);
    }

    //最后扫描有多少个独立的犯罪团伙
    for(i=1;i<=n;i++)
    {
        if(f[i]==i)
            sum++;
    }

    getchar();getchar();
    printf("%d\n",sum);
    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值