題目鏈接:http://codeforces.com/gym/102222/problem/E
In computer science, a 2-3-4 tree, as a search tree, is a self-balancing data structure that is commonly used to implement dictionaries. The numbers mean a tree where every node with children (internal node) has either two, three or four child nodes.
A 2-node is the same as a binary node, which has one data element, and if internal has two child nodes. A 3-node has two data elements, and if internal has three child nodes. A 4-node has three data elements, and if internal has four child nodes. All leaves are at the same depth, and all data is kept in sorted order.
Here we introduce the procedure of inserting a value into a 2-3-4 tree. If the number of values that have been inserted in is no more than 22, the 2-3-4 tree after the insertion has only one node (the root) with several data elements.
Otherwise, we start the following procedure at the root of the 2-3-4 tree.
- If the current node is a 4-node:
- Remove and save the middle value to get a 3-node
- Split the remaining 3-node up into a pair of 2-nodes (the now missing middle value is handled in the next step).
- If this is the root node (which thus has no parent):
- the middle value becomes the new root 2-node and the tree height increases by 1. Ascend into the root (which means that the current node becomes the new root node).
- Otherwise, push the middle value up into the parent node. Ascend into the parent node (which means that the current node becomes its parent node).
- If the current node is a leaf, insert the value into the node and finish.
- Otherwise,
- Find the child whose interval contains the value to be inserted.
- Descend into the child and repeat from step 1.
Now you are given a 2-3-4 tree which is empty at first. Then we insert nn values a1,a2,⋯,ana1,a2,⋯,an into the 2-3-4 tree one by one. You are asked to write a program to print the 2-3-4 tree after inserting all the given values along the pre-order traversal. For a node with one or more data elements, your program will print these data elements in one line in ascending order.
The input contains several test cases, and the first line is a positive integer TT indicating the number of test cases which is up to 5050.
For each test case, the first line contains an integer n (1≤n≤5000)n (1≤n≤5000) indicating the number of values that will be inserted into the 2-3-4 tree. The second line contains nn integers a1,a2,⋯,ana1,a2,⋯,an. We guarantee that a1,a2,⋯,ana1,a2,⋯,an is a permutation of 1,2,⋯,n1,2,⋯,n.
For each test case, output a line containing Case #x: at first, where x is the test case number starting from 11. The following several lines describe the 2-3-4 tree along the pre-order traversal, each line of which describes a node with several integers indicating the data elements of a node in the 2-3-4 tree.
3
4
1 2 3 4
4
4 3 2 1
17
6 3 5 7 1 10 2 9 4 8 11 12 13 14 15 16 17
Case #1:
2
1
3 4
Case #2:
3
1 2
4
Case #3:
5 9
2
1
3 4
7
6
8
11 13 15
10
12
14
16 17
題目大意:就是要你要找2-3-4數的插入方法,並且最后按照前序遍歷的方法輸出每個節點的數據
思路:第一次接觸2-3-4樹,看了挺久的,到現在也不是很懂這個代碼,可能自己拍還是拍不出來吧,先放上來好了,有機會接觸這種題再復習
看代碼:
#include<iostream> #include<algorithm> #include<stack> #include<cstdio> #include<map> #include<queue> #include<cstring> using namespace std; typedef long long LL; const int maxn=1e5+5; int a[maxn]; int cnt,rt; struct Node { int f; vector<int>d,ch; void init(int fa,int x)//f是該節點的父親? x是當前的結點擁有的值? { f=fa; d.clear(),ch.clear(); d.push_back(x);//當前節點擁有多少的所有的值 } }tr[maxn]; void ins(int p,int x)//(1,x) { // cout<<"rt:"<<rt<<endl; if(tr[p].d.size()==3)//代表有三個元素?? 也就是當前節點插滿了不能再插進元素了 { int f=tr[p].f;//父親是誰 vector<int>cpy_d,cpy_ch; cpy_d.swap(tr[p].d),cpy_ch.swap(tr[p].ch);//swap ?? 這時候cop_d 和cpy_ch擁有了ty[p].d和ty[p].ch的內容 而這兩個變為空? if(p==rt)//如果p和rt相等??代表已經是根節點了 上面沒有結點 { //相當於中間結點往上提 0 1 2三個結點 中間的自然是1 變為其它兩個節點的父親 tr[rt=++cnt].init(0,cpy_d[1]);//新的結點 父親變為0 插入d[1]? tr[++cnt].init(rt,cpy_d[0]);//新的結點 父親變為rt 插入d[0]? 相當於成為左兒子 tr[p].init(rt,cpy_d[2]);//原來的結點父親是rt 只剩下d[2]? 相當於成為右兒子 tr[rt].ch.push_back(cnt),tr[rt].ch.push_back(p);//把這兩個結點加入到他們的父親節點的孩子中 if(cpy_ch.size())//如果當前節點有孩子 自然孩子也需要進行操作 { //左邊兩個分到左邊 右邊兩個分到右邊 大概是為了平衡吧。。。 tr[cnt].ch.push_back(cpy_ch[0]),tr[cpy_ch[0]].f=cnt;//原來的左兒子自然也就成為現在左兒子的左兒子了 tr[cnt].ch.push_back(cpy_ch[1]),tr[cpy_ch[1]].f=cnt;//為什么把1也歸為左兒子的兒子? tr[p].ch.push_back(cpy_ch[2]),tr[cpy_ch[2]].f=p;//為什么把2 3歸為右兒子的兒子? tr[p].ch.push_back(cpy_ch[3]),tr[cpy_ch[3]].f=p; // cout<<cpy_ch[0]<<" "<<cpy_ch[1]<<" "<<cpy_ch[2]<<" "<<cpy_ch[3]<<endl; } p=rt;//現在的根節點已經變為rt了 } else//不是根節點 上面有節點 { tr[p].init(f,cpy_d[0]);//左邊結點 tr[++cnt].init(f,cpy_d[2]);//新建右邊結點 tr[f].d.push_back(cpy_d[1]);//把中間結點提上去 sort(tr[f].d.begin(),tr[f].d.end());//提上元素之后顯然要排序 tr[f].ch.push_back(cnt);//多加了個孩子 for(int i=tr[f].ch.size()-1;i>1;i--)//??為什么要交換? 懂了 因為你要保證孩子也是有序的呀 所以要把新插入的結點換到p之后 { if(tr[f].ch[i-1]!=p) swap(tr[f].ch[i-1],tr[f].ch[i]); else break; } if(cpy_ch.size()) { tr[p].ch.push_back(cpy_ch[0]),tr[cpy_ch[0]].f=p; tr[p].ch.push_back(cpy_ch[1]),tr[cpy_ch[1]].f=p; tr[cnt].ch.push_back((cpy_ch[2])),tr[cpy_ch[2]].f=cnt; tr[cnt].ch.push_back(cpy_ch[3]),tr[cpy_ch[3]].f=cnt; } p=f; } } // cout<<"*"<<tr[p].d.size()<<endl; //保證當前節點只有兩個元素??錯的 會有三個 if(tr[p].ch.size()==0)//沒有孩子?? 也就不需要在孩子中進行比較和插入了 直接放進去即可 { tr[p].d.push_back(x);//作為本節點的元素 sort(tr[p].d.begin(),tr[p].d.end());//你必須保證里面的有序的 所有每次插入元素都需要排序??? } else//有孩子 插入元素就必須和孩子比較了 { if(x<tr[p].d[0]) ins(tr[p].ch[0],x);//如果x<第一個元素 那么很顯然 肯定要插入到做兒子中 (因為元素是有序的)所以遞歸左兒子 else if(x>tr[p].d[tr[p].d.size()-1]) ins(tr[p].ch[tr[p].ch.size()-1],x);//如果大於最后一個元素 顯然是插到右孩子中的 遞歸右兒子 else//不比第一個小 不比最后一個大 則肯定插入到中間 { for(int i=1;i<tr[p].d.size();i++)//遍歷中間元素 if(x<tr[p].d[i]) {ins(tr[p].ch[i],x);break;}//小於誰就插入到誰的孩子中 } } } void dfs(int p) { for(int i=0;i<tr[p].d.size();i++) printf("%d%c",tr[p].d[i],i==tr[p].d.size()-1?'\n':' '); for(int i=0;i<tr[p].ch.size();i++) dfs(tr[p].ch[i]); } int main() { int T; cin>>T; int ca=1; while(T--) { int N; cin>>N; for(int i=1;i<=N;i++) cin>>a[i]; cnt=rt=1,tr[rt].init(0,a[1]); //第一個點的父親為0 擁有元素a[1] for(int i=2;i<=N;i++) ins(rt,a[i]);//rt不是一直是1嗎? printf("Case #%d:\n", ca++); dfs(rt); } return 0; }