基础回顾--基本树形结构运用
本篇只是非常基础的一些树形结构的入门。
树形结构与链表感觉很类似,只是有分叉的链表。内存不连续,内存之间(节点之间)的联系是通过内存的编号。
很多时候树的题目往往不需要构造出树,只需要就着树的性质灵活模拟。
下面就挑一道感觉比较经典的题~
题目描述:
我们可以把由“000”和“111”组成的字符串分为三类:全“000”串称为BBB串,全“111”串称为I串,既含“000”又含“111”的串则称为F串。
FBIFBIFBI树是一种二叉树,它的结点类型也包括FFF结点,BBB结点和I结点三种。由一个长度为2N2^N2N的“010101”串S可以构造出一棵FBIFBIFBI树TTT,递归的构造方法如下:
- TTT的根结点为RRR,其类型与串SSS的类型相同;
- 若串SSS的长度大于111,将串SSS从中间分开,分为等长的左右子串S1S_1S1和S2S_2S2;由左子串S1S_1S1构造R的左子树T1T_1T1,由右子串S2S_2S2构造RRR的右子树T2T_2T2。
现在给定一个长度为2N2^N2N的“010101”串,请用上述构造方法构造出一棵FBIFBIFBI树,并输出它的后序遍历序列。
解析:
这题的题目我读了好几遍才懂,阅读能力有待提升啊(如果还是英文就炸了)。大意就是可以直接将0看作B,将1看作I,一列出来就是完全二叉树的叶子节点了,之后再往回递归就能构造出整棵树。前序遍历左右根。
#include <iostream> #include <cstdio> using namespace std; int t[(1<<10) + 2]; int n, cnt; //左右根 void dp(int x1, int x2) { // 将x1--x2区间的点分成两部分继续递归 if(x2 > x1) { int mid = x1 + (x2-x1)/2; dp(x1, mid); //左子树 dp(mid+1, x2); // 右子树 } // 根输出 // 这里一开始可能有些难理解 // 由于此根是左右子树的汇聚点 // 所以左右子树的所有节点对此根有影响 // 可以试试从1个3个节点的二叉树推推试试 int b=0, i=0; for(int it=x1; it<=x2; ++it) { if(t[it] == 0) b += 1; else if(t[it] == 1) i += 1; } if(b && i) cout << 'F'; else if(b) cout << 'B'; else cout << 'I'; return; } int main() { scanf("%d", &n); cnt = (1 << n); for(int i=1; i<=cnt; ++i) { scanf("%1d", t+i); } dp(1, cnt); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <queue> using namespace std; const int mx = 1e6 + 4; const int mv = 2000; struct Tree { int l, r, w; }node[mx]; int ans; inline int max(int a, int b) { return a>b ? a:b; } int f[mx]; // 计算节点时记忆化 bool is_system = true; // 计算子树的节点数 // 由于多次使用且树是一定的,开记忆化 // @para rt 树根的编号 int cnt_node(int rt) { if(rt == -1) return 0; if(f[rt]) return f[rt]; else { f[rt] = 1; // 根节点本身占一个 f[rt] += cnt_node(node[rt].l) + cnt_node(node[rt].r); } return f[rt]; } // 递归判断对称性 // 由于要持续判断每一次的状态 // 递归的类型为void // 若是状态累加、特例终止等则考虑其他类型 // @para a b 两棵树节点的编号 // @para rt a和b的父节点 void judge(int a, int b) { if((a==-1 && b==-1)) return; else if(a==-1 || b==-1) { is_system = false; return; } // a b节点存在 if(node[a].w != node[b].w) { is_system = false; return; } else { judge(node[a].l, node[b].r); judge(node[a].r, node[b].l); } return; } int n, l, r; int main() { scanf("%d", &n); for(int i=1; i<=n; ++i) scanf("%d", &node[i].w); for(int i=1; i<=n; ++i) scanf("%d %d", &node[i].l, &node[i].r); //没有必要bfs,节点的编号全部已知且连续 for(int now=1; now<=n; ++now) { judge(node[now].r, node[now].l); if(is_system) ans = max(cnt_node(now), ans); else is_system = true; } printf("%d\n", ans); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; string pre; void find_r(string mid, string aft) { if(mid.size() == 0) return; char root = *(aft.rbegin()); pre += root; if(aft.size() <= 1 || mid.size() <= 1) return; int l; for(l=0; l<mid.size(); ++l) if(mid[l] == root) break; find_r(mid.substr(0,l), aft.substr(0, l)); find_r(mid.substr(l+1), aft.substr(l, aft.size()-1-l)); return; } int main() { string mid, aft; cin >> mid >> aft; find_r(mid, aft); cout << pre << endl; return 0; }