one. 二叉树(Binary Tree)
由根结点(root)、左子树(left subtree)和右子树(right subtree)组成,
而左、右子树分别是一棵二叉树。注意在计算机中,树一般是“倒置”的,即根在上,叶子在下。
树(tree)和二叉树类似,区别在于每个结点不一定只有两棵子树。
书的内容也是树状结构,根结点有12棵子树:第1章、第2章、第3章、……、第12章,而第1章又有5棵子树:1.1、1.2、……、1.5。
不管是二叉树还是树,每个非根结点都有一个父亲(father),也称父结点。
1.二叉树的编号
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <sstream>
using namespace std;
/*【小球下落】Uva 679(模拟)
有一棵二叉树,最大深度为D,且所有叶子的深度都相同。
结点从上到下从左到右编号1, 2, 3,…, 2^D-1。在结点1处放一个小球,会下落。
每个内结点上都有一个开关,初始关闭;有小球落到一个开关上时,开关状态改变。
当小球到达一个内结点时,如果该结点上的开关关闭,则往左走,否则往右走,直到走到叶子结点。
一些小球从结点1处依次开始下落,最后一个小球将会落到哪里呢?
输入叶子深度D和小球个数I,输出第I个小球最后在的叶子编号。D≤20。输入最多包含1000组数据。 */
/* 提示:给定一棵包含2d个结点(其中d为树的高度)的完全二叉树,
如果把结点从上到下从左到右编号为1,2,3……,则结点k的左右子结点编号分别为{2k和2k+1}。 */
int id[1<<20];//2^20
int main(){
int D,I;
while(scanf("%d%d",&D,&I)==2){
memset(id,0,sizeof(id)); //开关全闭合
int k, n=(1<<D)-1 ; //n是最大节点的编号
for(int i=0;i<I;i++){
k=1; while(k<=n){ //枚举节点
//(因为要找最后一层,所以出界之前 最后一次放大的影响可以用最终除二来消除 )
id[k]=!id[k];
k=id[k]? k*2 : k*2+1 ; //每次进行放大处理进入下一层
}
}
printf("%d\n",k/2);
}
return 0;
}
//但,这是一份超时代码qwq
------------以上代码用“while(k<=n)”的方法判断“出界”更具一般性,但运算量太大。-----------------
每个小球都会落在根结点上,因此前两个小球必然是一个在左子树,一个在右子树。
一般地,只需看小球编号的奇偶性,就能知道它是最终在哪棵子树中。对于那些落入根结点左子树的小球来说,只需知道该小球是第几个落在根的左子树里的,就可以知道它下一步往左还是往右了。依此类推,直到小球落到叶子上。
如果使用题目中给出的编号I,则当I是奇数时,它是往左走的第(I+1)/2个小球;当I是偶数时,它是往右走的第I/2个小球。
这样,直接模拟最后一个小球的路线:
while(scanf("%d%d", &D, &I) == 2){ int k = 1; for(int i = 0; i<D-1; i++) if(I%2) { k = k*2; I = (I+1)/2; } else { k = k*2+1; I /= 2; } printf("%d\n", k); }
这样,程序的运算量就与小球编号无关了,而且节省了一个巨大的id数组。
int main(){ //uva679
int t,D,I; cin>>t;
while(t!=-1&&t--){
scanf("%d%d", &D, &I);
int k = 1;
for(int i = 0; i<D-1; i++){
if(I%2) { k = k*2; I = (I+1)/2; }
else { k = k*2+1; I /= 2; }
}
printf("%d\n", k);
}
return 0;
}
2.二叉树的层次遍历
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <queue>
#include <vector>
#include <cmath>
#include <map>
#include <sstream>
using namespace std;
/*【树的层次遍历】(UVa 122)//紫书上讲的太难辣qaq,以后再看吧
输入一棵二叉树,结点个数不超过256。按从上到下、从左到右的顺序输出各结点值。
每个结点都按照从根结点到它的移动序列给出(L表示左,R表示右)。
在输入中,每个结点的左括号和右括号之间没有空格,相邻结点之间用一个空格隔开。
每棵树的输入用一对空括号“()”结束(这对括号本身不代表一个结点)。
注意,如果路径上有的结点在输入中{没有给出},或者{给出超过一次},输出-1。 */
/*输入:(11,LL) (7,LLL) (8,R) (5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
(3,L) (4,R) () 输出: 5 4 8 11 13 4 7 2 1 // -1 */
const int MAXN=10010;
int ans[MAXN],flot,k;
bool failed;
struct Node{
bool h_v;
int v;
Node *L,*R;
Node():h_v(false),L(NULL),R(NULL){}
}*head;
void addnode(int v,char *s){
//printf("%s %d\n",s,v);
Node *cur=head;
for(int i=0;s[i];i++){
if(s[i]=='L'){
if(cur->L==NULL)cur->L=new Node();
cur=cur->L;
}
else if(s[i]=='R'){
if(cur->R==NULL)cur->R=new Node();
cur=cur->R;
}
}
if(cur->h_v)failed=true;//注意要加上failed判断没有出现数字的情况;
cur->v=v;
cur->h_v=true;
}
void print(){
queue<Node*>q;
Node *cur=head;
q.push(head);
while(!q.empty()){
cur=q.front();
q.pop();
if(cur->h_v==0)flot=0;
if(!flot)break;
ans[k++]=cur->v;
if(cur->L!=NULL)q.push(cur->L);
if(cur->R!=NULL)q.push(cur->R);
}
if(flot&&!failed)for(int i=0;i<k;i++){
if(i)printf(" ");
printf("%d",ans[i]);
}
else printf("not complete");
puts("");
}
void freenode(Node *cur){
if(cur==NULL)return;
freenode(cur->L);
freenode(cur->R);
free(cur);
}
int main(){
char s[MAXN];
head=new Node();
failed=false;
while(~scanf("%s",s)){
if(!strcmp(s,"()")){
flot=1;
k=0;
print();
freenode(head);
head=new Node();
failed=false;
continue;
}
int v;
sscanf(&s[1],"%d",&v);
addnode(v,strchr(s,',')+1);
}
return 0;
}