前言
这波题的所有知识点如下:
- 二叉树的数组实现
- 二叉树的链表实现
- 链表实现的动态化静态方法(内存池)
- 二叉树的四种遍历
- 由二叉树的两种遍历序列(必含中序序列)
- 二叉搜索树
题解如下
UVA-679 - Dropping Balls
思路
有一棵二叉树,最大深度为D,且所有叶子的深度都相同。所有结点从上到下从左到右编号为1, 2, 3,…, 2 D -1。
在结点1处放一个小球,它会往下落。每个内结点上都有一个开关,初始全部关闭,当每次有小球落到一个开关上时,状态都会改变。当小球到达一个内结点时,如果该结点上的开关关闭,则往左走,否则往右走,直到走到叶子结点。
如果直接建树的话数组应该存不下,复杂度应该也说不过去,
但是如果从给出的编号的奇偶性上下手,思路就显而易见了
当编号I是奇数时,它是往左走的第(I+1)/2个小球;
当I是偶数时,它是往右走的第I/2个小球。
代码
int D, I;
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
int t; cin >>t;
while(t--) {
cin >> D >> I;
int ans = 1;
for(int i = 1; i < D; ++i) {
if(I&1) I = I/2+1, ans *= 2;
else I /= 2, ans = ans*2+1;
}
cout << ans <<endl;
}
scanf("-1");
return 0;
}
UVA-122 - Trees on the level
思路
输入一棵二叉树,你的任务是按从上到下、从左到右的顺序输出各个结点的值。每个结点都按照从根结点到它的移动序列给出(L表示左,R表示右)。
在输入中,每个结点的左括号和右括号之间没有空格,相邻结点之间用一个空格隔开。每棵树的输入用一对空括号“()”结束(这对括号本身不代表一个结点)
如果从根到某个叶结点的路径上有的结点没有在输入中给出,或者给出超过一次,应当输出-1。
个人认为此题的难点在于字符串的处理,其实也不难。
然后就是根据字符串建树,建好树bfs即可。
这里给出数组和链表两种写法。
代码
使用数组
string s;
const int root = 1;
int Left[maxn], Right[maxn], cnt;
int val[maxn];
void init() {
met(val, 0);
Left[root] = Right[root] = 0;
cnt = root;
}
int newnode() {
int u = ++cnt;
Left[u] = Right[u] = 0;
return u;
}
bool check() {
queue<int> q;
q.push(root);
while(!q.empty()) {
int t = q.front();q.pop();
if(val[t] == 0) return false;
if(Left[t] != 0) q.push(Left[t]);
if(Right[t] != 0) q.push(Right[t]);
}
return true;
}
void ot() {
queue<int> q;
q.push(root);
bool spc = 0;
while(!q.empty()) {
int t = q.front(); q.pop();
if(spc) cout << " "; if(!spc) spc = 1;
cout << val[t];
if(Left[t] != 0) q.push(Left[t]);
if(Right[t] != 0) q.push(Right[t]);
}
cout << endl;
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
init(); bool flag = 1;
while(cin >> s) {
if(s == "()") {
if(check() && flag ) ot();
else printf("not complete\n");
flag = 1; init(); continue;
}
int pos = s.find(','), a;
string s1 = s.substr(1,pos-1);
stringstream ss(s1); ss >> a;
string s2 = s.substr(pos+1, s.size()-2-pos);
int len = s2.size();
int cur = root;
for(int i = 0; i < len; ++i) {
if(s2[i] == 'L') {
if(Left[cur] == 0) Left[cur] = newnode();
cur = Left[cur];
}
else {
if(Right[cur] == 0) Right[cur] = newnode();
cur = Right[cur];
}
}
if(val[cur] != 0) flag = 0;
val[cur] = a;
}
return 0;
}
使用链表
string s;
struct node {
int val;
node *Left, *Right;
node():val(0), Left(NULL), Right(NULL){}
};
node* root;
queue<node* > freeNodes; //内存池
node Node[maxn];
void init() {
for(int i = 0; i < maxn; ++i) freeNodes.push(&Node[i]);
}
node* newnode() {
node* u = freeNodes.front();
u->val = 0; u->Left = u->Right = NULL;
freeNodes.pop();
return u;
}
void Delete(node* u) {
freeNodes.push(u);
}
bool check() {
queue<node> q;
q.push(*root);
while(!q.empty()) {
node t = q.front();q.pop();
if(t.val == 0) return false;
if(t.Left != NULL) q.push(*t.Left);
if(t.Right != NULL) q.push(*t.Right);
}
return true;
}
void ot() {
queue<node> q;
q.push(*root);
bool spc = 0;
while(!q.empty()) {
node t = q.front(); q.pop();
if(spc) cout << " "; if(!spc) spc = 1;
cout << t.val;
if(t.Left != NULL) q.push(*t.Left);
if(t.Right != NULL) q.push(*t.Right);
}
cout << endl;
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
init();
root = newnode(); bool flag = 1;
while(cin >> s) {
if(s == "()") {
if(check() && flag ) ot();
else printf("not complete\n");
flag = 1; root = newnode(); continue;
}
int pos = s.find(','), a;
string s1 = s.substr(1,pos-1);
stringstream ss(s1); ss >> a;
string s2 = s.substr(pos+1, s.size()-2-pos);
int len = s2.size();
node *cur = root;
for(int i = 0; i < len; ++i) {
if(s2[i] == 'L') {
if(cur->Left == NULL) cur->Left = newnode();
cur = cur->Left;
}
else {
if(cur->Right == NULL) cur->Right = newnode();
cur = cur->Right;
}
}
if(cur->val != 0) flag = 0;
cur->val = a;
}
Delete(root);
return 0;
}
UVA-548 - Tree
思路
给一棵点带权(权值各不相同,都是小于10000的正整数)的二叉树的中序和后序遍历,找一个叶子使得它到根的路径上的权和最小。如果有多解,该叶子本身的权应尽量小。输入中每两行表示一棵树,其中第一行为中序遍历,第二行为后序遍历。
由中序和后序建树即可。建好树题目就水的不行了。
代码
int in[maxn], po[maxn], a, ans, ax;
int Left[maxn], Right[maxn], root;
int build(int L1, int R1, int L2, int R2) {
//if(L1 == R1) ans = min(ans, in[L1]);
if(L1 > R1) return 0;
int rt = po[R2];
int p = L1;
while(in[p] != rt) ++p;
Left[rt] = build(L1, p-1, L2, L2 + p-L1 -1);
Right[rt] = build(p+1,R1, L2 + p-L1, R2-1);
return rt;
}
void dfs(int x, int val) {
if(Left[x] == 0 && Right[x] == 0) {
if(ans > val) ans = val, ax = x;
else if(ans == val && x < ax) ans = val, ax = x;
return;
}
if(Left[x] > 0) dfs(Left[x], val +Left[x]);
if(Right[x] > 0) dfs(Right[x], val + Right[x]);
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
string s;
while(getline(cin, s)) {
met(Left, 0); met(Right, 0);
stringstream ss(s);
int idx = 0, idy = 0;
while(ss >> a) in[++idx] = a;
getline(cin, s);
stringstream sss(s);
while(sss >> a) po[++idy] = a;
root = build(1, idx, 1, idy);
ans = INF, ax = INF;
dfs(root, root);
cout << ax <<endl;
}
return 0;
}
UVA-839 - Not so Mobile
思路
这两题和二叉树本身并无太深的联系,主要是递归输入,有一种二叉树的思想在里面。
代码
const int root = 1;
int Left[maxn], Right[maxn], cnt;
int W[maxn], D[maxn];
bool flag = 1;
void init() {
met(Left, 0); met(Right, 0);
Left[root] = Right[root] = 0;
cnt = root;
flag = 1;
}
int newnode() {
int u = ++cnt;
Left[u] = Right[u] = 0;
return u;
}
int read(int rt) {
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
if(Left[rt] == 0) Left[rt] = newnode();
if(Right[rt] == 0) Right[rt] = newnode();
D[Left[rt]] = b, D[Right[rt]] = d;
if(a == 0) a = read(Left[rt]);
if(c == 0) c = read(Right[rt]);
W[Left[rt]] = a, W[Right[rt]] = c;
W[rt] = a + c;
if(a*b != c*d) flag = 0;
return W[rt];
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
int t; cin >> t;
while(t--) {
init();
read(root);
if(flag) printf("YES\n");
else printf("NO\n");
if(t) printf("\n");
}
return 0;
}
UVA-699 - The Falling Leaves
思路
同样是考查递归输入的水题。
代码
bool read(int id) {
int a; scanf("%d", &a);
if(a == -1) return false;
read(id-1);
read(id+1);
ans[id]+=a;
return true;
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
int kase = 0;
met(ans, 0);
while(read(maxn/2)) {
aid = maxn/2;
cnt = 0, cnt2 = 0;
while(ans[aid]!=0) --aid; ++aid;
//bfs();
printf("Case %d:\n", ++kase);
printf("%d", ans[aid]);
for(int i = aid+1; ans[i]!=0; ++i) printf(" %d", ans[i]);
printf("\n\n");
met(ans, 0);
}
return 0;
}
UVA-297 - Quadtrees
思路
这一题叫做四分树。实际上和二叉树没多大区别。
注意最大层数为5层。
代码
char s1[maxn], s2[maxn];
int id, deep;
const int root = 1;
int Left[maxn], Right[maxn], Up[maxn], Down[maxn], cnt;
int val[maxn];
void init() {
Left[root] = Right[root] = Up[root] = Down[root] = 0;
cnt = root;
met(val, 0);
}
int newnode() {
int u = ++cnt;
Left[u] = Right[u] = Up[u] = Down[u] = 0;
return u;
}
void build(char s[], int rt, int depth) {
//deep = max(deep, depth);
if(s[id] == 'f') {
if(val[rt] == 0) val[rt] = 1;
Left[rt] = Right[rt] = Up[rt] = Down[rt] = 0;
}
else if(s[id] == 'e') {}
else {
if(val[rt] == 1) return;
if(Left[rt] == 0) Left[rt] = newnode();
++id; build(s, Left[rt], depth+1);
if(Right[rt] == 0) Right[rt] = newnode();
++id; build(s, Right[rt], depth+1);
if(Up[rt] == 0) Up[rt] = newnode();
++id; build(s, Up[rt], depth+1);
if(Down[rt] == 0) Down[rt] = newnode();
++id; build(s, Down[rt], depth+1);
}
}
int solve() {
int ans = 0;
queue<int> q, d;
q.push(root); d.push(0);
while(!q.empty()) {
int t = q.front(); q.pop();
int dp = d.front(); d.pop();
if(val[t] == 1) ans += pow(4,deep-dp);
if(Left[t] != 0) q.push(Left[t]), d.push(dp+1);
if(Right[t] != 0) q.push(Right[t]), d.push(dp+1);
if(Up[t] != 0) q.push(Up[t]), d.push(dp+1);
if(Down[t] != 0) q.push(Down[t]), d.push(dp+1);
}
return ans;
}
int main() {
#ifdef _LOCAL
IN;
#endif // _LOCAL
int t; cin >> t;
while(t--) {
deep = 5;init();
scanf("%s%s",s1, s2);
id = 0; build(s1, root, 0);
id = 0; build(s2, root, 0);
//cout << deep <<endl;
printf("There are %d black pixels.\n", solve());
}
return 0;
}
自己的代码略水,书上的代码好。如下:
#include<cstdio>
#include<cstring>
const int len = 32;
const int maxn = 1024 + 10;
char s[maxn];
int buf[len][len], cnt;
//把字符串s[p..]导出到以(r,c)为左上角,边长为w的缓冲区中
//2 1
//3 4
void draw(const char* s, int& p, int r, int c, int w) {
char ch = s[p++];
if(ch == 'p') {
draw(s, p, r, c+w/2, w/2); //1
draw(s, p, r, c , w/2); //2
draw(s, p, r+w/2, c , w/2); //3
draw(s, p, r+w/2, c+w/2, w/2); //4
}
else if(ch == 'f') { //画黑像素(白像素不画)
for(int i = r; i < r+w; i++) for(int j = c; j < c+w; j++)
if(buf[i][j] == 0) {
buf[i][j] = 1;
cnt++;
}
}
}
int main( ) {
int T;
scanf("%d", &T);
while(T--) {
memset(buf, 0, sizeof(buf));
cnt = 0;
for(int i = 0; i < 2; i++) {
scanf("%s", s);
int p = 0;
draw(s, p, 0, 0, len);
}
printf("There are %d black pixels.\n", cnt);
}
return 0;
}