浙大数据结构第五周之树的进一步运用

题目详情:05-树7 堆中的路径

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

输入格式:

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

输出格式:

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

输入样例:

5 3
46 23 26 24 10
5 4 3

输出样例:

24 23 10
46 23 10
26 10

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

主要思路:

就是实现课本上小顶堆

第一次写错误:

插入过程循环条件里是根节点比要插入元素大,才要上滤

代码实现:

#include 
#define MaxSize 10005
#define MinData -100005
typedef int ElementType;
typedef struct Heap MinHeap;
struct Heap{
    /* data */
    ElementType Data[MaxSize];
    int Size;
};
MinHeap minHeap;
int IsFull() {
    if(minHeap.Size > MaxSize - 1) return 0;
    return 1;
}
void Insert(ElementType x) {
    if(!IsFull) {
        printf("The minheap is full\n");
        return;
    }

    int i = ++(minHeap.Size);
    // printf("x = %d i = %d minheap.data[i] = %d\n", x, i, minHeap.Data[i]);
    // printf("------------------\n");
    for( ; minHeap.Data[i / 2] > x; i /= 2) {   //Data[0]设置为哨兵(最小),保证最后一定能退出循环
        minHeap.Data[i] = minHeap.Data[i / 2]; 
    }
    minHeap.Data[i] = x;
    return; 
}
void PrintPath(int index) {
    for(int i = index; i > 0; i /= 2) {
        printf("%d", minHeap.Data[i]);
        if(i != 1) printf(" ");
    }
    return;
}
int main() {
    minHeap.Size = 0;
    minHeap.Data[0] = MinData; 
    int N, M;
    scanf("%d %d", &N, &M);
    for(int i = 0; i < N; i++) {
        int tmp;
        scanf("%d", &tmp);
        Insert(tmp);
    }
    for(int i = 0; i < M; i++) {
        int tmp;
        scanf("%d", &tmp);
        PrintPath(tmp);
        if(i != M - 1) printf("\n");
    }
    return 0;
}

题目详情:05-树8 File Transfer

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 contains N (2≤N≤104), the total number of computers in a network. Each computer in the network is then represented by a positive integer between 1 and N. Then in the following lines, the input is given in the format:

I c1 c2  

where I stands for inputting a connection between c1 and c2; or

C c1 c2    

where C stands for checking if it is possible to transfer files between c1 and c2; 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 between c1 and c2, 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 are k components." where k 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.

代码长度限制

16 KB

Go (go)

时间限制

400 ms

内存限制

64 MB

Java (javac)

时间限制

400 ms

内存限制

64 MB

Python (python3)

时间限制

400 ms

内存限制

64 MB

其他编译器

时间限制

150 ms

内存限制

64 MB

简单翻译:

先给一个正整数N,表示的电脑编号从1~N,然后给出三种操作:检查两台电脑有没有连接,连接两台电脑,结束,最后还要看这1~ N台电脑连成了几个局域网,如果全部连接就打印全部连接,否则就打印有几个局域网

主要思路:

考察的其实是并查集,

检查两台电脑有没有连接就是看两台电脑所在集合根节点是否相同,注意集合构件树的时候指针是从孩子指向根节点

连接两台电脑就是合并两个集合,为了控制树的高度选择将小集合加到大集合下面,根节点数组值的绝对值代表这个集合里有多少个节点,非根节点数组值代表根节点

检查集合数就是数根节点数量

代码实现:

#include 
#include 
#include 
// #include 
#define MAXNUM 100005
#define STOP 'S'
#define CHECK 'C'
#define INPUT 'I'
int Set[MAXNUM];
int FindRoot(int a) {
    for(; Set[a] >= 0; a = Set[a]);
    return a;    
}
int Check(int a, int b) {
    int ret = 1;
    int roota = FindRoot(a);
    int rootb = FindRoot(b);
    if(roota != rootb) ret = 0;
    return ret;
}
void Connect(int a, int b) {
    int roota = FindRoot(a);
    int rootb = FindRoot(b);
    if(abs(Set[roota]) >= abs(Set[rootb])) {    //加绝对值表示以此为根节点的节点数
        Set[roota] += Set[rootb];
        Set[rootb] = roota;
    }
    else if(abs(Set[roota]) < abs(Set[rootb])) {
        Set[rootb] += Set[roota];
        Set[roota] = rootb;
    }
    return;
}
int Count(int N) {
    int rootNum = 0;
    for(int i = 1; i <= N; i++) {
        if(Set[i] < 0) rootNum++;
        // printf("Set[%d] = %d  ", i, Set[i]);
        // if(i % 10 == 0) printf("\n");
    }
    return rootNum;
}
int main() {
    int num;
    scanf("%d", &num);
    for(int i = 1; i <= num; i++) Set[i] = -1;
    char execute;
    getchar();
    while((execute = getchar()) != STOP) {
        int a, b;
        scanf("%d %d", &a, &b);
        getchar();
        if(execute == CHECK) {
            if(Check(a, b)) {
                printf("yes\n");
            }
            else printf("no\n");
        }
        else if(execute == INPUT) {
            Connect(a, b);
        }
    }

    int totalRoot = Count(num);
    if(totalRoot == 1) printf("The network is connected.\n");
    else printf("There are %d components.\n", totalRoot);
    // system("pause");
    return 0;
}

题目详情:05-树9 Huffman Codes

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 integer N (2≤N≤63), then followed by a line that contains all the N 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', '_'}, and f[i] is the frequency of c[i] and is an integer no more than 1000. The next line gives a positive integer M (≤1000), then followed by M student submissions. Each student submission consists of N 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

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

简单翻译:

依据给出字母的权重进行哈夫曼编码,再判断每个学生对每个字母的编码是否正确

主要思路:

分为两部分

(一)实现哈夫曼编码

(1)建立一个小顶堆,注意小顶堆里放的是指向结构体数组的指针

(2)然后依据每个字母的权重构造小顶堆,构造过程其实是插入,插入过程是上滤

(3)接着每次从小顶堆里弹出最小和次小,注意弹出的过程不止是删除,还要重构小顶堆,重构过程是下滤,构成一棵二叉树,再把这棵二叉树放进小顶堆,如此循环直到小顶堆里只有一个指针,那个指针即是哈夫曼树根节点

(二)检查学生答案

这里要注意学生可以不用哈夫曼编码得到与哈夫曼编码一样的答案

检查分两部分

(1)WPL是否相同

哈夫曼的WPL计算:递归

学生给的答案的WPL的计算:利用权重 * 每个编码长度再求和

(2)是否会出现前缀码
就用学生给的答案建树,如果是0就向左边建树;如果是1就向右边建树,每个字母编码建树完毕将当前current指针指向位置的权重设置为-1,这样在以后建树过程中如果发现下一层的权重是-1,说明之前一个字母的编码是当前字母编码的前缀,就不满足;另外如果建完树发现current不是叶子结点,也不满足

第一次写错误:

这题有亿点点复杂,还没彻底搞明白,自己写的测试点只能过一半

代码实现:

#include 
#include 
#include 
#define HeapCapacity 64
typedef struct TreeNode* HuffmanTree;
struct TreeNode
{
    int Weight;
    HuffmanTree Left;
    HuffmanTree Right;
};

 
typedef struct HeapNode *MinHeap;
struct HeapNode
{
    HuffmanTree *Data; //堆中存放TreeNode结点的数组
    int Size;
};
 
MinHeap createHeap(); //创建最小堆
HuffmanTree createHuffman(); //创建Huffman树
MinHeap initHeap(int N, int F[]); //读取数据,并将数据插入到最小堆中
HuffmanTree deleteMin(MinHeap H); //堆的删除,获取最小堆中的最小值
void insertHeap(MinHeap H, HuffmanTree huff); //堆的插入
HuffmanTree huffman(MinHeap H);//Huffman树的构造
int WPL(HuffmanTree huff, int depth);//计算Huffman树的编码长度
int checkSubmit(char code[], HuffmanTree current); //检查学生的提交
int main()
{
    int N;
    scanf("%d", &N);
    int F[N];
    MinHeap H = initHeap(N, F);
    HuffmanTree huff = huffman(H);
    int codeLen = WPL(huff, 0);
    int M;
    scanf("%d", &M);
    char ch;
    char code[N+1];
    int i;
    for(i = 0; i < M; i++) {
        int counter = 0;
        int result = 1;
        int flag = 0;
        HuffmanTree head = createHuffman();
        HuffmanTree current;
        int k;
        for(k = 0; k < N; k++) {
            current = head;
            getchar();
            scanf("%c", &ch);
            scanf("%s", code);
            counter += strlen(code) * F[k]; //记录总的编码长度
            if(flag == 0) {
                result = checkSubmit(code, current);
                if(result == 0) flag = 1;
            }
        }
        if(counter == codeLen && result == 1) {
            printf("Yes\n");
        } else {
            printf("No\n");
        }
    }
    return 0;
}
 
MinHeap createHeap()
{
    MinHeap H = (MinHeap)malloc(sizeof(struct HeapNode));
    H->Data = (HuffmanTree*)malloc(sizeof(struct TreeNode) * HeapCapacity); //最小堆存储数据是从下标为1开始的
    H->Size = 0;
    HuffmanTree huff = createHuffman();
    H->Data[0] = huff;
    return H;
}
 
HuffmanTree createHuffman()
{
    HuffmanTree T = (HuffmanTree)malloc(sizeof(struct TreeNode));
    T->Weight = 0;
    T->Left = NULL;
    T->Right = NULL;
    return T;
}
 
MinHeap initHeap(int N, int F[])
{
    MinHeap H = createHeap();
    HuffmanTree huff;
    char c;
    int f;
    int i;
    for(i = 0; i < N; i++) {
        getchar();
        scanf("%c %d", &c, &f);
        F[i] = f; //将频率保存到数组freq中
        huff = createHuffman();
        huff->Weight = f;
        insertHeap(H, huff); //将频率插入到堆中
    }
    return H;
}
 
HuffmanTree deleteMin(MinHeap H)
{
    HuffmanTree minItem = H->Data[1];
    HuffmanTree temp = H->Data[H->Size--];
    int parent, child;
    for(parent = 1; parent * 2 <= H->Size; parent = child) {
        child = parent * 2;
        if((child != H->Size) && (H->Data[child]->Weight > H->Data[child + 1]->Weight))
            child++;
        if(temp->Weight <= H->Data[child]->Weight)
            break;
        H->Data[parent] = H->Data[child];
    }
    H->Data[parent] = temp;
    return minItem;
}
 
 
void insertHeap(MinHeap H,HuffmanTree huff) //堆的插入操作
{
    int i = ++H->Size;
    for(; H->Data[i/2]->Weight > huff->Weight; i/=2) {
        H->Data[i] = H->Data[i/2];
    }
    H->Data[i] = huff;
}
 
 
HuffmanTree huffman(MinHeap H) //形成Huffman树
{
    HuffmanTree huff;
    int i;
    int times = H->Size; //一定要用临时变量记录,因为在deleteMin函数执行时,H->Size发生了变化
    for(i = 1; i < times; i++) {
        huff = createHuffman();
        huff->Left = deleteMin(H);
        huff->Right = deleteMin(H);
        huff->Weight = huff->Left->Weight + huff->Right->Weight;
        insertHeap(H, huff); //重新插入堆中
    }
    huff = deleteMin(H);
    return huff;
}
 
int WPL(HuffmanTree huff, int depth)
{
    if(huff->Left == NULL && huff->Right == NULL)
        return depth * huff->Weight;
    else
        return WPL(huff->Left,depth + 1) + WPL(huff->Right, depth + 1);
}
 
int checkSubmit(char code[], HuffmanTree current) //建树的过程中检查是否满足前缀码要求
{
    int i;
    for(i = 0; i < strlen(code); i++) {
        if(code[i] == '0') {//向左拓展一层
            if(current->Left == NULL) {
                current->Left = createHuffman();
            }
            if(current->Left->Weight == -1) { //已经访问到之前访问过的结点,即之前访问过的结点编码是当前字符串的前缀
                return 0;
            }
            current = current->Left;
        } else if (code[i] == '1') {
            if(current->Right == NULL) {
                current->Right = createHuffman();
            }
            if(current->Right->Weight == -1) {
                return 0;
            }
            current = current->Right;
        }
    }
    current->Weight = -1; //当前字符串遍历结束后,给当前结点赋值-1,那么后续一旦访问到结点的Weight = -1,就表明之前访问的某个字符串是当前字符串编码的前缀,就可以直接判断结果
    if(current->Left == NULL && current->Right == NULL) //如果字符串遍历结束时,当前是叶子结点,那么当前字符串的编码是正确的
        return 1;
    else
        return 0;
}

 参考文章

你可能感兴趣的:(数据结构,数据结构)