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; }