04-1. Root of AVL Tree
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 integer N (<=20) which is the total number of keys to be inserted. Then N 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 ythe root of the resulting AVL tree in one line.
Sample Input 1:5 88 70 61 96 120Sample Output 1:
70Sample Input 2:
7 88 70 61 96 120 90 65Sample Output 2:
88
关于AVL树,何老师详细讲解了其操作过程,不过并没有讲如何用代码实现,所以需要自己研究一下代码。AVL树的插入一共分为四种:左单旋(LL旋转),右单旋(RR旋转),左右旋转(LR旋转),右左旋转(RL旋转)。关键步骤是实现左单旋和右单旋,左右旋转和右左旋转可以分别各用一次左单旋和右单旋来实现。
下面先来讲解一下如何右单旋。如图所示:
假设我们在节点B的右边插入了一个数据,树不平衡了。插入的数据在A的右孩子的右孩子那里,所以进行右单旋。旋转的结果是把B放在最上面,把B的左孩子挂在A的右孩子位置,这样就完成了。那么这一过程如何用代码写出来呢?其实只要想一下就会明白,我们先让A的右孩子指向B的左孩子,再让B的左孩子指向A就可以了。可以参考下图:
当然实际操作时,B是用 A->right 来表示的,所以还需要借助一个临时指针来记录B节点。伪码描述过程如下:
temp = A->right;
A->right = temp->left;
temp->left = A;
这便是整个右单旋的操作。左单旋与此类似,如果插入的数据在左孩子的左孩子那里,导致树不平衡,就进行左单旋。而假如插入的数在左孩子的右孩子那里,就应该进行左右旋转了。如下图所示,进行左右旋转的结果是,C在最上面,B和A分别成C的左孩子和右孩子,C原来的左孩子和右孩子分别成为B和A的右孩子和左孩子。
这个过程似乎比较复杂,其实分别做一次左旋转和右旋转就可以完成,这也是为什么它叫左右旋转。我们来看一下分解动作:
这样一看我们就对这个过程很清楚了。另外一种操作右左旋转与此类似。
在AVL树中插入数据时,如果把某一棵子树调整平衡了,但是整个树有可能还是不平衡的,所以需要逐层往上调平衡。这需要用递归方式来实现。另外,为了能更方便地判断树是否平衡,我们可以在节点中加入关于树的高度信息,每一次插入都要更新树的高度。我们规定只有一个节点的树高度为0。如何求树的高度呢?这个问题何老师也讲过了,还是可以用递归的方法,某个树的高度是它左右子树最大的高度加上1。
最后,按照题目的要求,输出根节点的数就完成了。下面是完整的代码:
#include<iostream>
using namespace std;
struct BinTree //二叉树
{
int num;
int height;
BinTree* left;
BinTree* right;
};
//--求二叉树的高度--
int heightOfTree( BinTree* root )
{
if( !root )
return -1;
else
{
int h1, h2;
h1 = heightOfTree( root->left );
h2 = heightOfTree( root->right );
return h1>h2 ? h1+1 : h2+1;
}
}
//--左单旋--
BinTree* leftRotation( BinTree* root )
{
BinTree* temp = root->left;
root->left = temp->right;
temp->right = root;
int h1 = heightOfTree(root->left);
int h2 = heightOfTree(root->right);
int h3 = heightOfTree(temp->left);
root->height = h1>h2? h1+1 : h2+1;
temp->height = root->height>h3? root->height+1 : h3+1;
return temp;
}
//--右单旋--
BinTree* rightRotation( BinTree* root )
{
BinTree* temp = root->right;
root->right = temp->left;
temp->left = root;
int h1 = heightOfTree(root->left);
int h2 = heightOfTree(root->right);
int h3 = heightOfTree(temp->right);
root->height = h1>h2? h1+1: h2+1;
temp->height = root->height>h3? root->height+1: h3+1;
return temp;
}
//--左右旋转--
BinTree* leftRightRotation( BinTree* root )
{
root->left = rightRotation( root->left );
return leftRotation( root );
}
//--右左旋转--
BinTree* rightLeftRotation( BinTree* root )
{
root->right = leftRotation( root->right );
return rightRotation( root );
}
//--插入元素--
void insertTree( BinTree* &root, int k )
{
if( !root )
{
root = new BinTree;
root->num = k;
root->height = 0;
root->left = nullptr;
root->right = nullptr;
}
else
//插入左子树
if( k<root->num )
{
insertTree( root->left, k );
if( heightOfTree(root->left)-heightOfTree(root->right) == 2 ) //左旋
{
if( k < root->left->num )
root = leftRotation( root ); //LL旋转
else
root = leftRightRotation( root ); //LR旋转
}
}
//插入右子树
else if( k>root->num )
{
insertTree( root->right, k );
if( heightOfTree(root->right)-heightOfTree(root->left) == 2 ) //右旋
{
if( k > root->right->num )
root = rightRotation( root ); //RR旋转
else
root = rightLeftRotation( root ); //RL旋转
}
}
int h1 = heightOfTree( root->left );
int h2 = heightOfTree( root->right );
root->height = h1>h2 ? h1+1 : h2+1;
}
int main()
{
int n, k;
cin >> n;
BinTree* root = nullptr;
for( int i=0; i<n; ++i )
{
cin >> k;
insertTree( root, k );
}
cout << root->num;
return 0;
}
04-2. 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." 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 SSample 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 SSample Output 2:
no no yes yes The network is connected.
输入中先给出一个正整数N表示一共有N个结点,注意节点的编号是从1开始的,而不是0。在接下来的输入中,I表示将两个结点连接,其实就是把两个结点合并为一个集合;C表示检查这两个结点是否已连接,或者说检查它们是否在同一个集合里;S表示结束输入。这里我们可以用数组表示集合,数组的下标对应结点编号,需要注意数组下标是从0开始的;数组元素的值表示它的父结点编号,对于根结点则用负数来表示,这个负数的绝对值代表这个集合里的元素个数。这样一来这个问题的关键就在于以何种方式合并两个结点。至于要判断两个结点是否已连接,只要判断它们的父结点是否一样就可以了。让我们以一个具体的例子来说明:
如图,合并的结点可以分成很多种情况:1和3合并,代表两个根结点合并;7和3合并,一个是根结点,另一个是非根结点;7和5合并,两个都不是根结点。为了使问题更具有代表性,我们假设要合并结点7和5这种情况。这里有多种方式可以选择:将某一个结点的父结点直接挂在另一个结点下面,例如把3挂在7的下面,或者把1挂在5的下面,但是这样可能会让树的高度变得很大,当需要查找它们的祖宗结点时,就会很耗时;另一种方式是保持树的高度始终只为2,例如可以把3挂在1的下面,然后把5和8也都直接挂在1的下面,这样树就可以很矮了,但是这样的操作也会很耗时,尤其是我可能要遍历整个数组才能把结点3的两个孩子5和8找到;还有一种折衷的方法,把一个根结点挂在另一个根结点下面,其它的不动,例如直接把1挂在3的下面,这样树会相对高一些,但也节省了调整其它子结点的时间。不过,以上三种方法,在PAT里提交后都会超时……=_=|| 怎么办呢?其实仔细思考一下第三种方法,发现还是可以进一步优化的。我们假设有另一种情况,看下面的图:
这个时候如果要合并结点5和7,按照第三种方法,应该是什么结果呢?我们可能会把1挂在3的下面,这样整个树的高度是5,而且有更多的结点在较深层。而其实我们可以选择把3挂在1的下面,这样树的高度是4,并且更多的结点在较浅层。所以在我们合并两个结点时,只要加上判断,把小树挂在大树下面,就有可能进一步减小树的高度。在我的代码中实际上并不是比较两个树的高度,而是它们的结点个数,不过这样也能达到一样的效果,而且它最后确实通过了。下面是完整的代码:
#include<iostream>
//using namespace std;
using std::cout;
using std::cin;
int n;
int* a;
void connect( int x, int y )
{
int root_x = x-1;
int root_y = y-1;
while( a[root_x]>=0 )
root_x = a[root_x];
while( a[root_y]>=0 )
root_y = a[root_y];
if( a[root_x]<a[root_y] )
{
a[root_x] += a[root_y];
a[root_y] = root_x;
}
else
{
a[root_y] += a[root_x];
a[root_x] = root_y;
}
}
void judge( int x, int y )
{
int root_x = x-1;
int root_y = y-1;
while( a[root_x]>=0 )
root_x = a[root_x];
while( a[root_y]>=0 )
root_y = a[root_y];
if( root_x==root_y )
cout << "yes\n";
else
cout << "no\n";
}
int main()
{
cin >> n;
a = new int [n];
for( int i=0; i<n; ++i )
a[i] = -1;
//--input data, and output--
char ch;
cin >> ch;
int x, y;
while( ch!='S' )
{
cin >> x >> y;
if( ch=='I' )
connect( x, y );
else if( ch=='C' )
judge( x, y );
cin >> ch;
}
int node = 0;
for( int i=0; i<n; ++i )
{
if( a[i]<0 )
++node;
}
if( node==1 )
cout << "The network is connected.\n";
else
cout << "There are " << node << " components.\n";
return 0;
}
04-3. 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 a string of '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.
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 11Sample Output:
Yes Yes No No
这一题考查的是哈夫曼树。哈夫曼编码是最短的,但是它的编码方式并不唯一。例如,假设有a和b两个字母,我们可用0表示a,用10或11表示b;也可以用1表示a,用00或01表示b。对于有n个要编码的字符,一共有2n种编码,这是一种比n2还要快非常非常多倍的增长方式。所以如果想把所有的情况都列出来,那估计要等到猴年马月去了。我们可能利用一些哈夫曼树特点来简化这个问题:任何字符的编码都不是其它字符编码的前缀;哈夫曼树的所有结点,要么有两个孩子,要么是叶结点;所有编码的字符都只出现在叶结点位置;整个树的带权路径长度,即WPL,是最小的。
这里我使用的方法是根据学生的编码方式来还原哈夫曼树,然后判断所有字符是否都在叶结点上。所以一个结点里需要存储两个信息,权重和字符,如果是空字符,则用'\0'表示。然后判断所有编码的字符是否都在叶结点上,同时也顺便计算一下它的带权路径长度,之后要把它和正确的WPL值进行比较。要计算出正确的WPL,似乎没有现成的公式能直接算,必须把哈夫曼树还原出来。惟一可以用到的技巧是,WPL值等于所有非叶结点的权重和。这个大家可以自己去验证一下。这里我们只为了计算WPL,可以不用真的建树,用一个数组把它模拟出来就行了。我会告诉你其实我当时根本不会建树吗…>_<…后来才想起来可以用最小堆来实现……所以我的这段求WPL的函数比较麻烦,大家可以无视,弄个最小堆就可以轻松搞定。好了,基本就这么多,下面上代码:
#include <iostream>
using namespace std;
struct HuffTree
{
char num;
char ch;
HuffTree* left;
HuffTree* right;
};
int n;
char* a;
int* b;
int* c;
int ans_deep = 0;
int my_deep = 0;
void buildHuffTree( HuffTree* root, const char &chans )
{
char temp = cin.get();
if( temp=='0' )
{
if( !root->left )
{
root->left = new HuffTree;
root->left->num = '0';
root->left->ch = '\0';
root->left->left = nullptr;
root->left->right = nullptr;
}
buildHuffTree( root->left, chans );
}
else if( temp=='1' )
{
if( !root->right )
{
root->right = new HuffTree;
root->right->num = '1';
root->right->ch = '\0';
root->right->left = nullptr;
root->right->right = nullptr;
}
buildHuffTree( root->right, chans );
}
else
root->ch = chans;
}
void judgeHuffTree( HuffTree* root, bool& correct, int deep )
{
if( (root->left && !root->right) || (!root->left && root->right) )
{
correct = false;
return;
}
else if( root->ch!='\0' && ( root->left || root->right ) )
{
correct = false;
return;
}
if( root->ch!='\0' )
{
int i;
for( i=0; i<n; ++i )
if( root->ch==a[i] ) break;
ans_deep += b[i]*deep;
}
if( root->left )
judgeHuffTree( root->left, correct, deep+1 );
if( root->right )
judgeHuffTree( root->right, correct, deep+1 );
}
int shortestLength( )
{
int i, j;
int len = 0;
int m1;
int m2;
for( int k=1; k<n; ++k )
{
m1 = m2 = k-1;
for( i=m1; i<n; ++i )
{
if( c[i]<0 ) continue;
if( c[i]<c[m1] ) m1=i;
}
if( m2==m1 ) ++m2;
for( j=m2; j<n; ++j )
{
if( j==m1 || c[j]<0 ) continue;
if( c[j]<c[m2] ) m2=j;
}
len += c[m1] + c[m2];
c[m2] += c[m1];
c[m1] = c[k-1];
c[k-1] = -1;
}
return len;
}
int main()
{
cin >> n;
a = new char [n];
b = new int [n];
c = new int [n];
for( int i=0; i<n; ++i )
{
cin >> a[i] >> b[i];
c[i] = b[i];
}
//----------------------
int k; //number of answers
cin >> k;
bool correct;
cin.get();
int len = shortestLength();
for( int tid=0; tid<k; ++tid )
{
correct = true;
HuffTree* root = new HuffTree;
root->ch = '\0';
root->num = '\0';
root->left = nullptr;
root->right = nullptr;
char chans;
for( int i=0; i<n; ++i )
{
chans = cin.get();
cin.get();
buildHuffTree( root, chans );
}
judgeHuffTree( root, correct, 0 );
if( tid!=0 ) cout << endl;
if( correct )
{
if( ans_deep==len )
cout << "Yes";
else
cout << "No";
}
else
cout << "No";
//----clear---
delete root;
ans_deep = 0;
}
return 0;
}
PAT练习题 第四周:树(下)&spm=1001.2101.3001.5002&articleId=43526217&d=1&t=3&u=2b9f468008244fc79be8a702186fd2e3)
1万+

被折叠的 条评论
为什么被折叠?



