数据结构---树


二叉树

还原二叉树   (25分)

给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。

输入格式:

输入首先给出正整数N(≤\le50),为树中结点总数。下面两行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区别大小写)的字符串。

输出格式:

输出为一个整数,即该二叉树的高度。

输入样例:

9
ABDFGHIEC
FDHGIBEAC

输出样例:

5

解题思路:

本题需要了解树的先序遍历和中序遍历的特点,即树中的每个节点在各遍历中的位置规律。

更具此规律,将树构造出来,然后对复原的树求高度。

此处的代码将两步缩减为一步,直接求树高啦。

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 54;
char DLR[MAXN];
char LDR[MAXN];
int Index = 0;
int GetHigth(int l, int r);//在中序序列中确定左右界限,保证该界限内是一棵子树
int main()
{
    int n;
    scanf("%d", &n);
    getchar();//读取一个回车
    for(int i = 0; i < n; ++i)
        scanf("%c", &DLR[i]);
    getchar();
    for(int i = 0; i < n; ++i)
        scanf("%c", &LDR[i]);
    printf("%d\n",GetHigth(0, n));
    return 0;
}
int GetHigth(int l, int r)
{
    int h1 = 0, h2 = 0;//定义左右子树高度
    for(int i = l; i < r; ++i)
    {
        if(DLR[Index] == LDR[i])
        {
            Index++;//在先序序列中,父结点在前,如果在中序序列中找到相同节点,则在中序序列中寻找先序序列中的后一个元素
            h1 = GetHigth(l, i) + 1;//在当前父结点下求左子树树高
            h2 = GetHigth(i + 1, r) + 1;//在当前父结点下求右子树树高
        }
    }
    return h1 > h2? h1: h2;//返回较高的子树
}

树的同构   (25分)

给定两棵树T1和T2。如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是“同构”的。例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A、B、G的左右孩子互换后,就得到另外一棵树。而图2就不是同构的。


图1


图2

现给定两棵树,请你判断它们是否是同构的。

输入格式:

输入给出2棵二叉树树的信息。对于每棵树,首先在一行中给出一个非负整数NNN (≤10\le 1010),即该树的结点数(此时假设结点从0到N−1N-1N1编号);随后NNN行,第iii行对应编号第iii个结点,给出该结点中存储的1个英文大写字母、其左孩子结点的编号、右孩子结点的编号。如果孩子结点为空,则在相应位置上给出“-”。给出的数据间用一个空格分隔。注意:题目保证每个结点中存储的字母是不同的。

输出格式:

如果两棵树是同构的,输出“Yes”,否则输出“No”。

输入样例1(对应图1):

8
A 1 2
B 3 4
C 5 -
D - -
E 6 -
G 7 -
F - -
H - -
8
G - 4
B 7 6
F - -
A 5 1
H - -
C 0 -
D - -
E 2 -

输出样例1:

Yes

输入样例2(对应图2):

8
B 5 7
F - -
A 0 3
C 6 -
H - -
D - -
G 4 -
E 1 -
8
D 6 -
B 5 -
E - -
H - -
C 0 2
G - 3
F - -
A 1 4

输出样例2:

No

解题思路:

1)利用树状结构存储数据,采用结构体存储

2)使用层次遍历,判断是否同构

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 12;
struct node{
    char data, lc, rc;
}Node1[MAXN], Node2[MAXN], Order1[MAXN], Order2[MAXN];//定义存储结构
bool isRoot1[MAXN], isRoot2[MAXN];//判断是否为根结点

int main()
{
    int n1 = 0, n2 = 0, f = 0, e = 0;
    int root1 = 0, root2 = 0;
    bool Yes = true;
    cin>>n1;
    for(int i = 0; i < n1; ++i)
        isRoot1[i] = true;
    for(int i = 0; i < n1; ++i)//读入数据并判断是否为根结点
    {
        cin>>Node1[i].data>>Node1[i].lc>>Node1[i].rc;
        if(Node1[i].lc != '-') isRoot1[Node1[i].lc - '0'] = false;
        if(Node1[i].rc != '-') isRoot1[Node1[i].rc - '0'] = false;
    }
    for(int i = 0; i < n1; ++i)//得到根结点
        if(isRoot1[i]) root1 = i;
    Order1[e++] = Node1[root1];//进行层序遍历结果
    while(e < n1)
    {
        struct node tmp = Order1[f++];
        if(tmp.lc != '-') Order1[e++] = Node1[tmp.lc - '0'];
        if(tmp.rc != '-') Order1[e++] = Node1[tmp.rc - '0'];
    }
    f = 0, e = 0;//第二个数据信息,同样处理,但是层次遍历时,左右子树颠倒
    cin>>n2;
    for(int i = 0; i < n2; ++i)
        isRoot2[i] = true;
    for(int i = 0; i < n2; ++i)
    {
        cin>>Node2[i].data>>Node2[i].lc>>Node2[i].rc;
        if(Node2[i].lc != '-') isRoot2[Node2[i].lc - '0'] = false;
        if(Node2[i].rc != '-') isRoot2[Node2[i].rc - '0'] = false;
    }
    for(int i = 0; i < n2; ++i)
        if(isRoot2[i]) root2= i;
    Order2[e++] = Node2[root2];
    while(e < n2)
    {
        struct node tmp = Order2[f++];
        if(tmp.rc != '-') Order2[e++] = Node2[tmp.rc - '0'];
        if(tmp.lc != '-') Order2[e++] = Node2[tmp.lc - '0'];
    }
    if(n1 == n2)//如果两者节点总数相同,则判断是否同一遍历顺序
    {
        for(int i = 0; i < n1 && Yes; ++i)
        {
            if(Order1[i].data != Order2[i].data) Yes = false;
        }
    }
    else Yes = false;
    if(Yes) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

List Leaves   (25分)

Given a tree, you are supposed to list all the leaves in the order of top down, and left to right.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integerNNN (≤10\le 1010) which is the total number of nodes in the tree -- and hence the nodes are numbered from 0 toN−1N-1N1. Then NNN lines follow, each corresponds to a node, and gives the indices of the left and right children of the node. If the child does not exist, a "-" will be put at the position. Any pair of children are separated by a space.

Output Specification:

For each test case, print in one line all the leaves' indices in the order of top down, and left to right. There must be exactly one space between any adjacent numbers, and no extra space at the end of the line.

Sample Input:

8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6

Sample Output:

4 1 5

解题思路:

本题建树后,考虑做层次遍历,遍历同时将叶节点输出即可

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 12;
struct infi{
    int data,lc, rc;
}node[MAXN], tmp, q[MAXN];//树结点
bool isroot[MAXN];

int main()
{
    int n;
    char ch1, ch2;
    int f = 0, e = 0;
    cin>>n;
    for(int i = 0; i < n; ++i)//读入输入信息
    {
        cin>>ch1>>ch2;
        node[i].data = i;
        if(ch1 != '-') node[i].lc = ch1 - '0', isroot[ch1 - '0'] = true;
        else node[i].lc = -1;
        if(ch2 != '-') node[i].rc = ch2 - '0', isroot[ch2 - '0'] = true;
        else node[i].rc = -1;
    }
    int root = 0, cnt = 0;
    for(root = 0; isroot[root]; ++root) ; //循环体为空的for语句,寻找根结点
    q[e++] = node[root];//层次遍历
    while(f < e)
    {
        tmp = q[f++];
        if(tmp.lc != -1) q[e++] = node[tmp.lc];
        if(tmp.rc != -1) q[e++] = node[tmp.rc];
        if(tmp.lc == -1 && tmp.rc == -1)//将叶结点输出
        {
            if(cnt) cout<<' ';
            cout<<tmp.data;
            cnt++;
        }
    }
    return 0;
}

Tree Traversals Again   (25分)

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure 1) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.


Figure 1

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integerNNN (≤30\le 3030) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 toNNN). Then 2N2N2N lines follow, each describes a stack operation in the format: "Push X" where X is the index of the node being pushed onto the stack; or "Pop" meaning to pop one node from the stack.

Output Specification:

For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

6
Push 1
Push 2
Push 3
Pop
Pop
Push 4
Pop
Pop
Push 5
Push 6
Pop
Pop

Sample Output:

3 4 2 6 5 1

解题思路:

题意是将堆栈的操作可以建立二叉树,再将对二叉树后序遍历输出

堆栈的操作看似麻烦,对照题中的例子可以发现,题中所谓的堆栈操作实则前序遍历。

因此可以使用前序遍历的方式建树

建完之后,后序输出便是。

提交代码:

编译器:g++

#include <iostream>
using namespace std;
typedef struct TNode tnode;
struct TNode{
    int data;
    tnode *lc, *rc;
};
int cnt = 0;//纪录堆栈操作数
tnode *Create(int n);//建立含有n个结点的树
void posttra(tnode *bt);//后序遍历
int main()
{
    int n;
    cin>>n;
    tnode *bt = Create(n);
    cnt = 0;
    posttra(bt);
    return 0;
}
tnode *Create(int n)
{
    string str = "";
    tnode *t = NULL;
    cin>>str;
    if(str == "Push")//如果是进栈,那么作为树结点
    {
        t = new tnode;
        cin>>t->data;
        t->lc = Create(n);
        t->rc = Create(n);
    }
    return t;
}
void posttra(tnode *bt)
{
    if(bt)
    {
        posttra(bt->lc);
        posttra(bt->rc);
        if(cnt) cout<<' ';
        cout<<bt->data;
        cnt++;
    }
}


二叉搜索树

是否同一棵二叉搜索树   (25分)

给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。

输入格式:

输入包含若干组测试数据。每组数据的第1行给出两个正整数NNN (≤10\le 1010)和LLL,分别是每个序列插入元素的个数和需要检查的序列个数。第2行给出NNN个以空格分隔的正整数,作为初始插入序列。最后LLL行,每行给出NNN个插入的元素,属于LLL个需要检查的序列。

简单起见,我们保证每个插入序列都是1到NNN的一个排列。当读到NNN为0时,标志输入结束,这组数据不要处理。

输出格式:

对每一组需要检查的序列,如果其生成的二叉搜索树跟对应的初始序列生成的一样,输出“Yes”,否则输出“No”。

输入样例:

4 2
3 1 4 2
3 4 1 2
3 2 4 1
2 1
2 1
1 2
0

输出样例:

Yes
No
No

解题思路:

本题先要建立二叉搜索树,之后进行遍历操作,然后比较两个树是否一致。

其中的遍历操作此处使用的是层次遍历,其他的遍历未尝试。

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 12;
typedef struct Tnode tnode;
struct Tnode{
    int data;
    tnode *lc, *rc;
};
tnode *Create(int n);//建立n个结点的树
tnode *Gettree(tnode *t, int Data);
bool issimlar(tnode *T, tnode *xT, int n);//判断是否是同一个二叉搜索树
int main()
{
    int n, l;
    while(cin>>n, n)
    {
        cin>>l;
        tnode *T = Create(n);
        for(int i = 0; i < l; ++i)
        {
            tnode *xT = Create(n);
            if(issimlar(T, xT, n)) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
    return 0;
}
tnode *Create(int n)
{
    tnode *t = NULL;
    int Data;
    for(int i = 0; i < n; ++i)
    {
        cin>>Data;
        t = Gettree(t, Data);
    }
    return t;
}
tnode *Gettree(tnode *t, int Data)
{
    if(t == NULL)//如果没有这个值,则建立新结点存储
    {
        tnode *tmpt = new tnode;
        tmpt->data = Data;
        tmpt->lc = tmpt->rc = NULL;
        t = tmpt;
    }
    else if(t->data > Data)//如果小于当前结点值,则存储于左子树
    {
        t->lc = Gettree(t->lc, Data);
    }
    else if(t->data < Data)//如果大于当前结点值,则存储于右子树
    {
        t->rc = Gettree(t->rc, Data);
    }
    return t;
}
bool issimlar(tnode *T, tnode *xT, int n)
{
    bool falg = true;
    tnode *q1[MAXN], *q2[MAXN], *t1 = T, *t2 = xT;
    int f1 = 0, e1 = 0, f2 = 0, e2 = 0;
    q1[e1++] = t1;
    q2[e2++] = t2;
    while(falg && f1 < e1 && f2 < e2)
    {
        t1 = q1[f1++], t2 = q2[f2++];
        if(t1->data != t2->data) falg = false;//如果当前的值不同,则返回不一致
        else
        {
            if(t1->lc) q1[e1++] = t1->lc;
            if(t1->rc) q1[e1++] = t1->rc;
            
            if(t2->lc) q2[e2++] = t2->lc;
            if(t2->rc) q2[e2++] = t2->rc;
        }
    }
    return falg;
}

树种统计   (25分)

随着卫星成像技术的应用,自然资源研究机构可以识别每一棵树的种类。请编写程序帮助研究人员统计每种树的数量,计算每种树占总数的百分比。

输入格式:

输入首先给出正整数N(≤105\le 10^5105),随后N行,每行给出卫星观测到的一棵树的种类名称。种类名称由不超过30个英文字母和空格组成(大小写不区分)。

输出格式:

按字典序递增输出各种树的种类名称及其所占总数的百分比,其间以空格分隔,保留小数点后4位。

输入样例:

29
Red Alder
Ash
Aspen
Basswood
Ash
Beech
Yellow Birch
Ash
Cherry
Cottonwood
Ash
Cypress
Red Elm
Gum
Hackberry
White Oak
Hickory
Pecan
Hard Maple
White Oak
Soft Maple
Red Oak
Red Oak
White Oak
Poplan
Sassafras
Sycamore
Black Walnut
Willow

输出样例:

Ash 13.7931%
Aspen 3.4483%
Basswood 3.4483%
Beech 3.4483%
Black Walnut 3.4483%
Cherry 3.4483%
Cottonwood 3.4483%
Cypress 3.4483%
Gum 3.4483%
Hackberry 3.4483%
Hard Maple 3.4483%
Hickory 3.4483%
Pecan 3.4483%
Poplan 3.4483%
Red Alder 3.4483%
Red Elm 3.4483%
Red Oak 6.8966%
Sassafras 3.4483%
Soft Maple 3.4483%
Sycamore 3.4483%
White Oak 10.3448%
Willow 3.4483%
Yellow Birch 3.4483%

解题思路:

本题一个是建立二叉搜索树,同时做好统计工作。

之后利用二叉搜索树的特性——中序遍历得到的序列即能得到有序序列

提交代码:

编译器:g++

#include <iostream>
#include <string.h>
using namespace std;
struct Tnode{
    char str[33];
    int cnt;
    struct Tnode *lc, *rc;
};
struct Tnode *buildTree(struct Tnode *t, char *str);
void LDR(struct Tnode *t, int n);
int main()
{
    int n;
    char str[33], ch;
    Tnode *t = NULL;
    scanf("%d", &n);
    getchar();
    for(int i = 0, j = 0; i < n; ++i)//输入n个元素的值
    {
        while((ch = getchar()) != '\n')
            str[j++] = ch;
        str[j] = 0, j = 0;
        t = buildTree(t, str);
    }
    LDR(t, n);
    return 0;
}
struct Tnode *buildTree(struct Tnode *t, char *str)
{
    if(t == NULL)
    {
        t = new struct Tnode;
        t->cnt = 1;
        t->lc = t->rc = NULL;
        strcpy(t->str, str);
    }
    else if(strcmp(t->str, str) > 0)
        t->lc = buildTree(t->lc, str);
    else if(strcmp(t->str, str) < 0)
        t->rc = buildTree(t->rc, str);
    else   //当读到相同的元素时,计数器累加
        t->cnt++;
    return t;
}
void LDR(struct Tnode *t, int n)//中序遍历输出,注意百分号的输出格式
{
    if(t)
    {
        LDR(t->lc, n);
        printf("%s %.4lf%%\n", t->str, 100.0 * t->cnt / n);
        LDR(t->rc, n);
    }
}

搜索树判断   (25分)

对于二叉搜索树,我们规定任一结点的左子树仅包含严格小于该结点的键值,而其右子树包含大于或等于该结点的键值。如果我们交换每个节点的左子树和右子树,得到的树叫做镜像二叉搜索树。

现在我们给出一个整数键值序列,请编写程序判断该序列是否为某棵二叉搜索树或某镜像二叉搜索树的前序遍历序列,如果是,则输出对应二叉树的后序遍历序列。

输入格式:

输入的第一行包含一个正整数N(≤\le1000),第二行包含N个整数,为给出的整数键值序列,数字间以空格分隔。

输出格式:

输出的第一行首先给出判断结果,如果输入的序列是某棵二叉搜索树或某镜像二叉搜索树的前序遍历序列,则输出YES,否侧输出NO。如果判断结果是YES,下一行输出对应二叉树的后序遍历序列。数字间以空格分隔,但行尾不能有多余的空格。

输入样例1:

7
8 6 5 7 10 8 11

输出样例1:

YES
5 7 6 8 11 10 8

输入样例2:

7
8 6 8 5 10 9 11

输出样例2:

NO

解题思路:

1)本题需要建二叉搜索树

2)对该树进行先序遍历,如果先序遍历结果与输入数据相同,则后序遍历该树结束;否则进入3)

3)先对建的树转化为镜像,然后执行先序遍历,如果与输入数据相同,则后序遍历该树结束;否则输出NO

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 1002;
struct Tnode{
    int num;
    struct Tnode *lc, *rc;
};
int num[MAXN], DLRnum[MAXN], Index = 0;
struct Tnode *buildTree(struct Tnode *t, int num); //建树
void DLR(struct Tnode *t); //前序遍历
void LRD1(struct Tnode *t); //后序遍历
void LRD2(struct Tnode *t); //对镜像后序遍历
void exChange(struct Tnode *t); //将树转换为镜像
bool issimial(int n); //判断是否是前序遍历或镜像的前序遍历
int main()
{
    int n;
    Tnode *t = NULL;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    {
        scanf("%d", &num[i]);
        t = buildTree(t, num[i]);
    }
    DLR(t);
    if(issimial(n))
    {
        Index = n;
        printf("YES\n");
        LRD1(t);
    }
    else
    {
        Index = 0;
        exChange(t);
        if(issimial(n))
        {
            Index = n;
            printf("YES\n");
            LRD2(t);
        }
        else printf("NO\n");
    }
    return 0;
}
struct Tnode *buildTree(struct Tnode *t, int num)
{
    if(t == NULL)
    {
        t = new struct Tnode;
        t->lc = t->rc = NULL;
        t->num = num;
    }
    else if(t->num > num)
        t->lc = buildTree(t->lc, num);
    else
        t->rc = buildTree(t->rc, num);
    return t;
}
void DLR(struct Tnode *t)
{
    if(t)
    {
        DLRnum[Index++] = t->num;
        DLR(t->lc);
        DLR(t->rc);
    }
}
void LRD1(struct Tnode *t)
{
    if(t)
    {
        LRD1(t->lc);
        LRD1(t->rc);
        printf("%d", t->num);
        Index--;
        if(Index > 0)
            printf(" ");
    }
}
void LRD2(struct Tnode *t)
{
    if(t)
    {
        LRD2(t->rc);
        LRD2(t->lc);
        printf("%d", t->num);
        Index--;
        if(Index > 0)
            printf(" ");
    }
}
void exChange(struct Tnode *t)
{
    if(t)
    {
        DLRnum[Index++] = t->num;
        exChange(t->rc);//只要将访问左右子树的顺序调换就可以,生成镜像
        exChange(t->lc);
    }
}
bool issimial(int n)
{
    for(int i = 0; i < n; ++i)
        if(num[i] != DLRnum[i])
            return false;
    return true;
}

笛卡尔树   (25分)

笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字满足优先队列(不妨设为最小堆)的顺序要求,即该结点的K2值比其子树中所有结点的K2值小。给定一棵二叉树,请判断该树是否笛卡尔树。

输入格式:

输入首先给出正整数N(≤\le1000),为树中结点的个数。随后N行,每行给出一个结点的信息,包括:结点的K1值、K2值、左孩子结点编号、右孩子结点编号。设结点从0~(N-1)顺序编号。若某结点不存在孩子结点,则该位置给出−1-11

输出格式:

输出YES如果该树是一棵笛卡尔树;否则输出NO

输入样例1:

6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 21 -1 4
15 22 -1 -1
5 35 -1 -1

输出样例1:

YES

输入样例2:

6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 11 -1 4
15 22 -1 -1
50 35 -1 -1

输出样例2:

NO

解题思路:

根据题中所定义的笛卡尔树,对所给的信息进行建树

优先判断是否具有二叉搜索树的性质

然后再判断是否具有最小堆的性质

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 1002;
struct infi{
    int k1, k2;
    int lc, rc;
}node[MAXN];
int LDRnum[MAXN];
void LDR(int root, int &index); //中序遍历得到中序序列
bool isBST(int n); //判断是否是二叉搜索树
bool DDD(int index); //判断是否具有堆的性质
int FindRoot(int n); //找根结点
int main()
{
    int n, index = 0;
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
        scanf("%d%d%d%d", &node[i].k1, &node[i].k2, &node[i].lc, &node[i].rc);
    int root = FindRoot(n);
    LDR(root, index);
    if(isBST(n))
    {
        if(DDD(root))
            printf("YES\n");
        else
            printf("NO\n");
    }
    else
        printf("NO\n");
    return 0;
}
void LDR(int root, int &index)
{
    if(node[root].lc != -1)
        LDR(node[root].lc, index);
    LDRnum[index++] = node[root].k1;
    if(node[root].rc != -1)
        LDR(node[root].rc, index);
}
bool isBST(int n)//通过对中序序列判断是否有序,判断是否具有二叉搜索树性质
{
    for(int i = 0; i < n; ++i)
        for(int j = 0; j < i; ++j)
            if(LDRnum[i] < LDRnum[j])
                return false;
    return true;
}
bool DDD(int index)//通过层次遍历,判断是否有序。如无序,则不具有最小堆性质
{
    struct infi q[MAXN], tmp;
    int f = 0, e = 0;
    bool islaw = true;
    q[e++] = node[index];
    while(f < e && islaw)
    {
        tmp = q[f++];
        if(tmp.lc != -1)
        {
            if(tmp.k2 >= node[tmp.lc].k2)
                islaw = false;
            else
                q[e++] = node[tmp.lc];
        }
        if(tmp.rc != -1)
        {
            if(tmp.k2 >= node[tmp.rc].k2)
                islaw = false;
            else
                q[e++] = node[tmp.rc];
        }
    }
    return islaw;
}
int FindRoot(int n)
{
    int index = 0;
    bool haveroot[MAXN] = {false};
    for(int i = 0; i < n; ++i)
    {
        if(node[i].lc != -1) haveroot[node[i].lc] = true;
        if(node[i].rc != -1) haveroot[node[i].rc] = true;
    }
    for(int i = 0; i < MAXN; ++i)
        if(!haveroot[i])
        {
            index = i;
            break;
        }
    return index;
}


平衡二叉树

Root of AVL Tree   (25分)

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.

Now given a sequence of insertions, you are supposed to tell the root of the resulting AVL tree.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integerNNN (≤20\le 2020) which is the total number of keys to be inserted. Then NNN distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print the root of the resulting AVL tree in one line.

Sample Input 1:

5
88 70 61 96 120

Sample Output 1:

70

Sample Input 2:

7
88 70 61 96 120 90 65

Sample Output 2:

88

此处直接参考陈越姥姥网易云课堂的平衡二叉树代码

#include <stdlib.h>
#include <stdio.h>
typedef struct AVLTreeNode *AVLTree;
struct AVLTreeNode{
	int Data;
	AVLTree Left;
	AVLTree Right;
	int Height;
};
int Max(int a, int b)
{
	return a > b? a: b;
}
int GetHeight(AVLTree T)
{
	if(!T) return 0;
	else return T->Height;
}
AVLTree SingleLeftRotation(AVLTree A)
{
	AVLTree B = A->Left;
	A->Left = B->Right;
	B->Right = A;
	A->Height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
	B->Height = Max(GetHeight(B->Left), A->Height) + 1;
	return B;
}
AVLTree SingleRightRotation(AVLTree A)
{
	AVLTree B = A->Right;
	A->Right = B->Left;
	B->Left = A;
	A->Height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
	B->Height = Max(GetHeight(B->Right), A->Height) + 1;
	return B;
}
AVLTree DoubleLeftRightRotation(AVLTree A)
{
	A->Left = SingleRightRotation(A->Left);
	return SingleLeftRotation(A);
}
AVLTree DoubleRightLeftRotation(AVLTree A)
{
	A->Right = SingleLeftRotation(A->Right);
	return SingleRightRotation(A);
}
AVLTree AVL_Insertion(int X, AVLTree T)
{
	if(!T)
	{
		T = (AVLTree)malloc(sizeof(struct AVLTreeNode));
		T->Data = X;
		T->Height = 0;
		T->Left = T->Right = NULL;
	}
	else if(X < T->Data)
	{
		T->Left = AVL_Insertion(X, T->Left);
		if(GetHeight(T->Left) - GetHeight(T->Right) == 2)
		{
			if(X < T->Left->Data) T = SingleLeftRotation(T);
			else T = DoubleLeftRightRotation(T);
		}
	}
	else if(X > T->Data)
	{
		T->Right = AVL_Insertion(X, T->Right);
		if(GetHeight(T->Left) - GetHeight(T->Right) == -2)
		{
			if(X > T->Right->Data)  T = SingleRightRotation(T);
			else T = DoubleRightLeftRotation(T);
		}
	}
	T->Height = Max(GetHeight(T->Left), GetHeight(T->Right)) + 1;
}
int main()
{
	int NodeNum, tmpkey;
	AVLTree tree = NULL;
	scanf("%d", &NodeNum);
	for(int i = 0; i < NodeNum; ++i)
	{
		scanf("%d", &tmpkey);
		tree = AVL_Insertion(tmpkey, tree);
	}
	printf("%d\n", tree->Data);
	return 0;
} 


哈夫曼树

修理牧场   (25分)

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要NNN块木头,每块木头长度为整数LiL_iLi个长度单位,于是他购买了一条很长的、能锯成NNN块的木头,即该木头的长度是LiL_iLi的总和。

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。

请编写程序帮助农夫计算将木头锯成NNN块的最少花费。

输入格式:

输入首先给出正整数NNN≤104\le 10^4104),表示要将木头锯成NNN块。第二行给出NNN个正整数(≤50\le 5050),表示每段木块的长度。

输出格式:

输出一个整数,即将木头锯成NNN块的最少花费。

输入样例:

8
4 5 1 2 1 3 1 1

输出样例:

49

解题思路:

本题中所给的输入数据是已经切完的木块,也就是一块木头将切过之后所得到的木块

也就是对所给的数据按题中所描述的例子进行逆操作。

该逆操作利用贪心策略,具体是哈夫曼树解决的。

这里并没有建树,只是利用最小堆做贪心操作(对于堆的知识此处略有欠缺)

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 10002;
int Hcode[MAXN];
void buildHeap(int n); //建立最小堆
void adjustHeap(int index, int n); //调整堆
int PopHeap(int &n); //将最小堆的最小元素弹出
void InsertHeap(int x, int &n); //将元素插入到最小队中
int main()
{
    int n, index, ans = 0;
    scanf("%d", &n);
    index = n;
    for(int i = 1; i <= n; ++i)
        scanf("%d", &Hcode[i]);
    buildHeap(n);
    while(index > 1)
    {
        int sum = 0;
        sum += PopHeap(index);
        sum += PopHeap(index);
        ans += sum;
        InsertHeap(sum, index);
    }
    printf("%d\n", ans);
    return 0;
}
void buildHeap(int n)
{
    for(int i = n / 2; i >= 1; --i)//先从后开始建立最小堆
        adjustHeap(i, n);
}
void adjustHeap(int index, int n)
{
    while((index << 1) <= n)//确保在队中调整堆
    {
        int tmp = index << 1;
        if(tmp + 1 <= n)
            if(Hcode[tmp] > Hcode[tmp + 1])
                tmp++;
        if(Hcode[tmp] < Hcode[index])//选择最小的子节点,与父节点比较,判断是否交换两个元素
        {
            int t = Hcode[tmp];
            Hcode[tmp] = Hcode[index];
            Hcode[index] = t;
            index = tmp;
        }
        else
            break;
    }
}
int PopHeap(int &n)
{
    int x;
    if(n >= 1)
    {
        x = Hcode[1];
        Hcode[1] = Hcode[n--];//将最小值从堆中舍弃
        adjustHeap(1, n);//从顶开始调整堆
    }
    return x;
}
void InsertHeap(int x, int &n)
{
    int index = n + 1;
    n++;
    Hcode[n] = x;//在堆末添加元素
    while((index >> 1) > 0 && Hcode[index] < Hcode[index >> 1])//从低开始调整堆
    {
        int t = Hcode[index];//调整时,将最小元素提前
        Hcode[index] = Hcode[index >> 1];
        Hcode[index >> 1] = t;
        index >>= 1;
    }
}

Huffman Codes   (30分)

In 1953, David A. Huffman published his paper "A Method for the Construction of Minimum-Redundancy Codes", and hence printed his name in the history of computer science. As a professor who gives the final exam problem on Huffman codes, I am encountering a big problem: the Huffman codes are NOT unique. For example, given a string "aaaxuaxz", we can observe that the frequencies of the characters 'a', 'x', 'u' and 'z' are 4, 2, 1 and 1, respectively. We may either encode the symbols as {'a'=0, 'x'=10, 'u'=110, 'z'=111}, or in another way as {'a'=1, 'x'=01, 'u'=001, 'z'=000}, both compress the string into 14 bits. Another set of code can be given as {'a'=0, 'x'=11, 'u'=100, 'z'=101}, but {'a'=0, 'x'=01, 'u'=011, 'z'=001} is NOT correct since "aaaxuaxz" and "aazuaxax" can both be decoded from the code 00001011001001. The students are submitting all kinds of codes, and I need a computer program to help me determine which ones are correct and which ones are not.

Input Specification:

Each input file contains one test case. For each case, the first line gives an integerNNN (2≤N≤632\le N\le 632N63), then followed by a line that contains all the NNN distinct characters and their frequencies in the following format:

c[1] f[1] c[2] f[2] ... c[N] f[N]

where c[i] is a character chosen from {'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}, andf[i] is the frequency ofc[i] and is an integer no more than 1000. The next line gives a positive integerMMM (≤1000\le 10001000), then followed by MMM student submissions. Each student submission consists of NNN lines, each in the format:

c[i] code[i]

where c[i] is the i-th character and code[i] is an non-empty string of no more than 63 '0's and '1's.

Output Specification:

For each test case, print in each line either "Yes" if the student's submission is correct, or "No" if not.

Note: The optimal solution is not necessarily generated by Huffman algorithm. Any prefix code with code length being optimal is considered correct.

Sample Input:

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

Sample Output:

Yes
Yes
No
No

解题思路:

题中所要求的编码方式的检验,涉及两方面:

1)WPL值得比较

2)是否是前缀编码

对于2)容易忽略,但可用函数实现。

至于1)此处,是建立Huffman树之后,利用先序遍历对长度进行计算,然后将所得到长度与输入所给的频率运算得到。

此处的建树,还是利用了最小堆(对于堆的知识此处略有欠缺)。

提交代码:

编译器:g++

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int MAXN = 64;
typedef struct Tnode tnode;
struct Tnode{
	int fre;
	char data;
	tnode *lc, *rc;
}; //Huffman树的结点
struct Heap{
	int fre;
	int data;
	tnode *add;
}h[(MAXN << 1) - 1]; //最小堆的结点,其中的数据分别用于记录字符频率,字符,建树需要的参数
int len[(MAXN << 2)]; //长度
int fre[(MAXN << 2)]; //频率
char code[MAXN][MAXN]; //输入的编码方式
void buildHeap(int n); 
void adjust(int index, int n);
struct Heap popHeap(int n);
void pushHeap(tnode *t, int n);
void Swap(struct Heap &a, struct Heap &b); //以上函数用于建立最小堆
void buildHuffmanTree(int n);
tnode *getAdd(struct Heap node); //添加元素到Huffman树上
int getWPL(void); //求WPL值
void DLR(tnode *t, int l); //求len值
bool islaw(int n); //是否是前缀编码
int main()
{
	int n, m;
	char ch;
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		getchar();
		scanf("%c %d", &h[i].data, &h[i].fre);
		h[i].add = NULL; //表示为添加到树上
		fre[h[i].data] = h[i].fre;
	}
	buildHeap(n);
	buildHuffmanTree(n);
	int wpl0 = getWPL();
	
	scanf("%d", &m);//一下是判断所给数据,是否是Huffman编码
	for(int i = 0; i < m; ++i)
	{
		for(int j = 0; j < n; ++j)
		{
			getchar();
			scanf("%c %s", &ch, code[j]);
			len[ch] = strlen(code[j]);
		}
		bool Islaw = islaw(n);
		if(Islaw) //如果是前缀编码
		{
			int wpl1 = getWPL();
			if(wpl1 == wpl0) printf("Yes\n"); //并且WPL值相同,则是Huffman编码
			else printf("No\n");
		}
		else printf("No\n");
	}
	return 0;
}
void buildHeap(int n)
{
	for(int i = n >> 1; i >= 1; --i)
		adjust(i, n);
}
void adjust(int index, int n) //维护最小堆
{
	while((index << 1) <= n)
	{
		int tmp = index << 1;
		if(tmp + 1 <= n && h[tmp].fre > h[tmp + 1].fre)
			tmp = tmp + 1;
		if(h[tmp].fre < h[index].fre)
		{
			Swap(h[index], h[tmp]);
			index = tmp;
		}
		else break;
	}
}
struct Heap popHeap(int n) //弹出最小值
{
	struct Heap tmp = h[1];
	Swap(h[1], h[n]);
	adjust(1, n - 1);
	return tmp;
}
void Swap(struct Heap &a, struct Heap &b)
{
	struct Heap tmp = a;
	a = b;
	b = tmp;
}
void pushHeap(tnode *t, int n) //将元素压入最小堆
{
	h[n].data = t->data, h[n].fre = t->fre;
	h[n].add = t; //表示已在树上,表示已有孩子结点
	int index = n;
	struct Heap tmp = h[n];
	while((index >> 1) > 1)//维护最小堆
	{
		if(tmp.fre < h[index >> 1].fre)
		{
			h[index] = h[index >> 1];
			index >>= 1;
		}
		else break;
	}
	h[index] = tmp;
}
void buildHuffmanTree(int n)
{
	tnode *T = NULL;
	while(n > 1)
	{
		struct Heap node1 = popHeap(n);
		n--;
		struct Heap node2 = popHeap(n);
		tnode *t1 = getAdd(node1); //从最小堆中得到的最小值
		tnode *t2 = getAdd(node2);
		tnode *t = new tnode;
		t->fre = t1->fre + t2->fre; //将频率累加
		t->data = ' '; //用空字符代替
		t->lc = t1, t->rc = t2;
		pushHeap(t, n); //将该父节点压入最小堆
		if(n == 1) T = t; //如果只有一个元素,则作为根结点
	}
	DLR(T, 0);
}
tnode *getAdd(struct Heap node)
{
	if(node.add == NULL) //如果为加入到树中,则作为叶结点
	{
		tnode *tmp = new tnode;
		tmp->fre = node.fre;
		tmp->data = node.data;
		tmp->lc = tmp->rc = NULL;
		return tmp;
	}
	else return node.add; 否则将父结点作为新的子结点,返回
}
int getWPL(void)
{
	int wpl = 0;
	for(int i = 0; i < MAXN << 1; ++i) //计算WPL
		wpl += len[i] * fre[i];
	for(int i = 0; i < MAXN << 1; ++i) //将len数组清空
		len[i] = 0;
	return wpl;
}
void DLR(tnode *t, int l)
{
	if(t)
	{
		if(t->data != ' ')
			len[t->data] = l;
		DLR(t->lc, l + 1);
		DLR(t->rc, l + 1);
	}
}
bool islaw(int n)
{
	for(int i = 0; i < n; ++i)
	{
		for(int j = i + 1; j < n; ++j)
		{
			int l1 = strlen(code[i]);
			int l2 = strlen(code[j]);
			int l;
			for(l = 0; l < l1 && l < l2; ++l)
				if(code[i][l] != code[j][l]) break;
			if(l >= l1 || l >= l2) return false;
		}
	}
	return true;
}

Windows消息队列   (25分)

消息队列是Windows系统的基础。对于每个进程,系统维护一个消息队列。如果在进程中有特定事件发生,如点击鼠标、文字改变等,系统将把这个消息加到队列当中。同时,如果队列不是空的,这一进程循环地从队列中按照优先级获取消息。请注意优先级值低意味着优先级高。请编辑程序模拟消息队列,将消息加到队列中以及从队列中获取消息。

输入格式:

输入首先给出正整数N(≤105\le 10^5105),随后N行,每行给出一个指令——GETPUT,分别表示从队列中取出消息或将消息添加到队列中。如果指令是PUT,后面就有一个消息名称、以及一个正整数表示消息的优先级,此数越小表示优先级越高。消息名称是长度不超过10个字符且不含空格的字符串;题目保证队列中消息的优先级无重复,且输入至少有一个GET

输出格式:

对于每个GET指令,在一行中输出消息队列中优先级最高的消息的名称和参数。如果消息队列中没有消息,输出EMPTY QUEUE!。对于PUT指令则没有输出。

输入样例:

9
PUT msg1 5
PUT msg2 4
GET
PUT msg3 2
PUT msg4 4
GET
GET
GET
GET

输出样例:

msg2
msg3
msg4
msg1
EMPTY QUEUE!

解题思路:

根据最小堆的定义,建立堆。

同时当堆栈,发生不稳定时,进行必要的维护。

如有不明白,建议先了解堆的定义和堆的操作。

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 100002;
struct infi{
    char str[12];
    int Priority;
}Heap[MAXN];
void buildHeap(int index);
void Swap(struct infi &a, struct infi &b);
void adjust(int index);
int main()
{
    int n, index = 0;
    char com[5];
    scanf("%d", &n);
    for(int i = 0; i < n; ++i)
    {
        scanf("%s", com);
        if(com[0] == 'P')
        {
            index++;
            scanf("%s %d", Heap[index].str, &Heap[index].Priority);
            buildHeap(index);
        }
        else if(com[0] == 'G')
        {
            if(index >= 1)
            {
                struct infi tmp = Heap[1];
                Swap(Heap[1], Heap[index]);
                index--;
                adjust(index);
                printf("%s\n", tmp.str);
            }
            else
                printf("EMPTY QUEUE!\n");
        }
    }
    return 0;
}
void buildHeap(int index)
{
    while((index >> 1) > 0 && Heap[index].Priority < Heap[index >> 1].Priority)
    {
        Swap(Heap[index], Heap[index >> 1]);
        index >>= 1;
    }
}
void Swap(struct infi &a, struct infi &b)
{
    struct infi tmp = a;
    a = b;
    b = tmp;
}
void adjust(int n)
{
    int index = 1;
    while((index << 1) < n)
    {
        int tmp = index << 1;
        if(tmp + 1 < n)
            if(Heap[tmp].Priority > Heap[tmp + 1].Priority)
                tmp++;
        if(Heap[tmp].Priority < Heap[index].Priority)
        {
            Swap(Heap[tmp], Heap[index]);
            index = tmp;
        }
        else
        {
            break;
        }
    }
}

堆中的路径   (25分)

将一系列给定数字插入一个初始为空的小顶堆H[]。随后对任意给定的下标i,打印从H[i]到根结点的路径。

输入格式:

每组测试第1行包含2个正整数NNNMMM(≤1000\le 10001000),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间[-10000, 10000]内的NNN个要被插入一个初始为空的小顶堆的整数。最后一行给出MMM个下标。

输出格式:

对输入中给出的每个下标i,在一行中输出从H[i]到根结点的路径上的数据。数字间以1个空格分隔,行末不得有多余空格。

输入样例:

5 3
46 23 26 24 10
5 4 3

输出样例:

24 23 10
46 23 10
26 10

解题思路:

直接建堆,然后根据堆的性质,输出结果

提交代码:

编译器:g++

#include <iostream>
using namespace std;
const int MAXN = 1002;
int H[MAXN];
void Create(int n);
void Output(int index);
int main()
{
    int n, k, index;
    cin>>n>>k;
    Create(n);
    for(int i = 0; i < k; ++i)
    {
        cin>>index;
        Output(index);
    }
    return 0;
}
void Create(int n)
{
    int cnt = 1,index = 0;
    for(int i = 0; i < n; ++i)
    {
        index = cnt;
        cin>>H[cnt++];
        while((index>>1) > 0 && H[index] < H[index>>1])
        {
            int tmp = H[index];
            H[index] = H[index>>1];
            H[index>>1] = tmp;
            index >>= 1;
        }
    }
}
void Output(int index)
{
    int cnt = 0;
    while(index > 0)
    {
        if(cnt) cout<<' ';
        cout<<H[index];
        index >>= 1;
        cnt++;
    }
    cout<<endl;
}

并查集

朋友圈   (25分)

某学校有N个学生,形成M个俱乐部。每个俱乐部里的学生有着一定相似的兴趣爱好,形成一个朋友圈。一个学生可以同时属于若干个不同的俱乐部。根据“我的朋友的朋友也是我的朋友”这个推论可以得出,如果A和B是朋友,且B和C是朋友,则A和C也是朋友。请编写程序计算最大朋友圈中有多少人。

输入格式:

输入的第一行包含两个正整数N(≤\le30000)和M(≤\le1000),分别代表学校的学生总数和俱乐部的个数。后面的M行每行按以下格式给出1个俱乐部的信息,其中学生从1~N编号:

第i个俱乐部的人数Mi(空格)学生1(空格)学生2 … 学生Mi

输出格式:

输出给出一个整数,表示在最大朋友圈中有多少人。

输入样例:

7 4
3 1 2 3
2 1 4
3 5 6 7
1 6

输出样例:

4

解题思路:

在一个圈子里假定一个组长,该组长有一个该组内任意人都可复制的符号

当输入的人员是某一组的成员时,由该组成员复制符号给该人员

最后利用这个特殊符号统计各组人员多少

(此处的还未对并查集知识点进行整理)

提交代码:

编译器:g++

#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 30001;
int root[MAXN];
int Find(int x) //查找函数,用于将符号复制
{
    if(x != root[x])
        root[x] = Find(root[x]);
    return root[x];
}
int main()
{
    int n, m, k;
    int a, b;
    scanf("%d%d", &n, &m);
    for(int i = 0; i <= n; ++i)
        root[i] = i;
    for(int i = 0; i < m; ++i)
    {
        scanf("%d %d", &k, &a);
        int x = Find(a);
        for(int j = 1; j < k; ++j)
        {
            scanf("%d", &b);
            int y = Find(b);
            if(x != y) //如果同一个圈子内的人有不同的符号,则统一为一个符号
                root[y] = x;
        }
    }
    for(int i = 1; i <= n; ++i)
        Find(i);
    sort(root + 1, root + n + 1);
    int Max = 0, cnt = 0, tmp = root[1];
    for(int i = 1; i <= n; ++i) //统计各组内人数,并记录最大值
    {
        if(root[i] == tmp)
            cnt++;
        else
        {
            if(Max < cnt)
                Max = cnt;
            tmp = root[i], cnt = 1;
        }
    }
    printf("%d\n", Max);
    return 0;
}

File Transfer   (25分)

We have a network of computers and a list of bi-directional connections. Each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other?

Input Specification:

Each input file contains one test case. For each test case, the first line containsNNN (2≤N≤1042\le N\le 10^42N104), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 andNNN. Then in the following lines, the input is given in the format:

I c1 c2

where I stands for inputting a connection between c1 andc2; or

C c1 c2

where C stands for checking if it is possible to transfer files betweenc1 andc2; or

S

where S stands for stopping this case.

Output Specification:

For each C case, print in one line the word "yes" or "no" if it is possible or impossible to transfer files betweenc1 andc2, respectively. At the end of each case, print in one line "The network is connected." if there is a path between any pair of computers; or "There arek components." wherek is the number of connected components in this network.

Sample Input 1:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
S

Sample Output 1:

no
no
yes
There are 2 components.

Sample Input 2:

5
C 3 2
I 3 2
C 1 5
I 4 5
I 2 4
C 3 5
I 1 3
C 1 5
S

Sample Output 2:

no
no
yes
yes
The network is connected.

解题思路:

依旧假定一个组长,之后对输入的数据进行模拟并判断是否是同一组的人员。

如果是同一组的,则总人数在总人数中减少一,表示剩下n - 1个不同的组。

直到n == 1的时候,只有一个组,表示网络联通。

(此处的还未对并查集知识点进行整理)

提交代码:

编译器:g++

#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int MAXN = 10002;
int root[MAXN];
int Find(int x);
int main()
{
	int n;
	char ch;
	int a, b;
	scanf("%d", &n);
	getchar();
	for(int i = 1; i <= n; ++i)
		root[i] = i;
	while((ch = getchar()) != 'S')
	{
		scanf("%d%d", &a, &b);
		getchar();
		int x = Find(a);
		int y = Find(b);
		if(ch == 'C')
		{
			if(x != y)
				printf("no\n");
			else
				printf("yes\n");
		}
		else if(ch == 'I')
		{
			if(x != y)
				root[x] = y, n--;
		}
	}
	if(n == 1)
		printf("The network is connected.\n");
	else
		printf("There are %d components.", n);
	return 0;
}
int Find(int x)
{
	if(x != root[x])
		root[x] = Find(root[x]);
	return root[x];
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值