使用链表的存储方式是完全可以存树的
但是为了方便处理树的一些特性,我们将链表的存储结构进行了适当的修改
针对着一般二叉树,完全二叉树,我们也有着不同的存储策略
首先我们介绍对于一般二叉树而言的数组存储和指针存储,完全二叉树将在后面提到
对于数组存储而言,我们定义这样一个结构
struct Tree { int fa; int ch[2]; long long x; }t[maxn]; int root; int tot=0;
使用t数组来存树中每一个流水节点,在这一点上和链表的处理方式类似
为了便于组织,我们把以前表示节点的若干个数组组织成了一个结构体
fa和ch[]数组里存的都是标号,也就是t数组中的下标,也可以说成是键因为其具有唯一性
x里面存的是树的节点值
root用来指示这个树的根节点是谁
tot用来存t数组中的元素个数,其中包含已删除的节点
怎样去输入这棵树呢?因题而异
cin>>n; for(int i=1;i<=n;i++) { tot++; t[tot].x=i; } for(int i=1;i<=n;i++) { cin>>t[i].ch[0]>>t[i].ch[1]; } root=1;
这里举一个例子,对于包含n个节点的树,每个树的值为i,对于第i个树,其左右孩子节点由输入给出,给出的形式为节点在t数组中的下标
在这里我们展示树的五种遍历形式,分别是前序中序后序遍历以及DFS和BFS,在这里前序遍历和DFS的形式一致
前序遍历:
void pre_order(int o) { cout<<t[o].x<<" "; if(t[o].ch[0]) pre_order(t[o].ch[0]); if(t[o].ch[1]) pre_order(t[o].ch[1]); }
中序遍历:
void in_order(int o) { if(t[o].ch[0]) in_order(t[o].ch[0]); cout<<t[o].x<<" "; if(t[o].ch[1]) in_order(t[o].ch[1]); }
后序遍历:
void post_order(int o) { if(t[o].ch[0]) post_order(t[o].ch[0]); if(t[o].ch[1]) post_order(t[o].ch[1]); cout<<t[o].x<<" "; }
DFS:
int vis[maxn]; void dfs(int dp,int o) { cout<<t[o].x<<" "; if(t[o].ch[0]&&!vis[t[o].ch[0]]) { vis[t[o].ch[0]]=1; dfs(dp+1,t[o].ch[0]); } if(t[o].ch[1]&&!vis[t[o].ch[1]]) { vis[t[o].ch[1]]=1; dfs(dp+1,t[o].ch[1]); } }
在这里的vis数组无意义,因为按搜索树遍历不存在重复的情况
BFS:
int q[maxn]; void bfs(int o) { int h=0,_t=1; q[_t]=o; while(h!=_t) { h=h%maxn+1; cout<<t[q[h]].x<<" "; if(t[q[h]].ch[0]&&!vis[t[q[h]].ch[0]]) { _t=_t%maxn+1; vis[t[q[h]].ch[0]]=1; q[_t]=t[q[h]].ch[0]; } if(t[q[h]].ch[1]&&!vis[t[q[h]].ch[1]]) { _t=_t%maxn+1; vis[t[q[h]].ch[1]]=1; q[_t]=t[q[h]].ch[1]; } } }
下面给出完整的实现:
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 const int maxn=1005; 5 int n; 6 struct Tree 7 { 8 int fa; 9 int ch[2]; 10 long long x; 11 }t[maxn]; 12 int root; 13 int tot=0; 14 void pre_order(int o) 15 { 16 cout<<t[o].x<<" "; 17 if(t[o].ch[0]) 18 pre_order(t[o].ch[0]); 19 if(t[o].ch[1]) 20 pre_order(t[o].ch[1]); 21 } 22 void in_order(int o) 23 { 24 if(t[o].ch[0]) 25 in_order(t[o].ch[0]); 26 cout<<t[o].x<<" "; 27 if(t[o].ch[1]) 28 in_order(t[o].ch[1]); 29 } 30 void post_order(int o) 31 { 32 if(t[o].ch[0]) 33 post_order(t[o].ch[0]); 34 if(t[o].ch[1]) 35 post_order(t[o].ch[1]); 36 cout<<t[o].x<<" "; 37 } 38 int vis[maxn]; 39 void dfs(int dp,int o) 40 { 41 cout<<t[o].x<<" "; 42 if(t[o].ch[0]&&!vis[t[o].ch[0]]) 43 { 44 vis[t[o].ch[0]]=1; 45 dfs(dp+1,t[o].ch[0]); 46 } 47 if(t[o].ch[1]&&!vis[t[o].ch[1]]) 48 { 49 vis[t[o].ch[1]]=1; 50 dfs(dp+1,t[o].ch[1]); 51 } 52 } 53 int q[maxn]; 54 void bfs(int o) 55 { 56 int h=0,_t=1; 57 q[_t]=o; 58 while(h!=_t) 59 { 60 h=h%maxn+1; 61 cout<<t[q[h]].x<<" "; 62 if(t[q[h]].ch[0]&&!vis[t[q[h]].ch[0]]) 63 { 64 _t=_t%maxn+1; 65 vis[t[q[h]].ch[0]]=1; 66 q[_t]=t[q[h]].ch[0]; 67 } 68 if(t[q[h]].ch[1]&&!vis[t[q[h]].ch[1]]) 69 { 70 _t=_t%maxn+1; 71 vis[t[q[h]].ch[1]]=1; 72 q[_t]=t[q[h]].ch[1]; 73 } 74 } 75 } 76 int main() 77 { 78 cin>>n; 79 for(int i=1;i<=n;i++) 80 { 81 tot++; 82 t[tot].x=i; 83 } 84 for(int i=1;i<=n;i++) 85 { 86 cin>>t[i].ch[0]>>t[i].ch[1]; 87 } 88 root=1; 89 pre_order(root); 90 cout<<endl; 91 in_order(root); 92 cout<<endl; 93 post_order(root); 94 cout<<endl; 95 dfs(1,root); 96 cout<<endl; 97 memset(vis,0,sizeof(vis)); 98 bfs(root); 99 return 0; 100 }
对于指针存储而言,我们定义这样一个结构:
int n; struct Node { long long x; Node *ch[2]; bool vis; }; Node *node[maxn]; Node *root; int tot=0;
在这里每一个节点就是一个Node类型的元素,其中左右孩子以指针形式来存,并且每一个节点都自带一个vis标记,当有其他标记填充时,直接以这种形式扩展
指针数组node维护所有的节点
root来指向树根
树中所有元素数记做tot
我们同样给出指针存储的情况下所有的遍历形式:
前序遍历:
void pre_order(Node* o) { cout<<o->x<<" "; if(o->ch[0]!=NULL) pre_order(o->ch[0]); if(o->ch[1]!=NULL) pre_order(o->ch[1]); }
中序遍历:
void in_order(Node* o) { if(o->ch[0]!=NULL) in_order(o->ch[0]); cout<<o->x<<" "; if(o->ch[1]!=NULL) in_order(o->ch[1]); }
后序遍历:
void post_order(Node* o) { if(o->ch[0]!=NULL) post_order(o->ch[0]); if(o->ch[1]!=NULL) post_order(o->ch[1]); cout<<o->x<<" "; }
DFS:
void dfs(int dp,Node* o) { cout<<o->x<<" "; if(o->ch[0]!=NULL&&!o->ch[0]->vis) { o->ch[0]->vis=1; dfs(dp+1,o->ch[0]); } if(o->ch[1]!=NULL&&!o->ch[1]->vis) { o->ch[1]->vis=1; dfs(dp+1,o->ch[1]); } }
BFS:
Node *q[maxn]; void bfs(Node* o) { int h=0,_t=1; q[_t]=o; while(h!=_t) { h=h%maxn+1; cout<<q[h]->x<<" "; if(q[h]->ch[0]!=NULL&&!q[h]->ch[0]->vis) { _t=_t%maxn+1; q[h]->ch[0]->vis=1; q[_t]=q[h]->ch[0]; } if(q[h]->ch[1]!=NULL&&!q[h]->ch[1]->vis) { _t=_t%maxn+1; q[h]->ch[1]->vis=1; q[_t]=q[h]->ch[1]; } } }
最后我们给出完整的实现:
1 #include<iostream> 2 using namespace std; 3 const int maxn=1005; 4 int n; 5 struct Node 6 { 7 long long x; 8 Node *ch[2]; 9 bool vis; 10 }; 11 Node *node[maxn]; 12 Node *root; 13 int tot=0; 14 void pre_order(Node* o) 15 { 16 cout<<o->x<<" "; 17 if(o->ch[0]!=NULL) 18 pre_order(o->ch[0]); 19 if(o->ch[1]!=NULL) 20 pre_order(o->ch[1]); 21 } 22 void in_order(Node* o) 23 { 24 if(o->ch[0]!=NULL) 25 in_order(o->ch[0]); 26 cout<<o->x<<" "; 27 if(o->ch[1]!=NULL) 28 in_order(o->ch[1]); 29 } 30 void post_order(Node* o) 31 { 32 if(o->ch[0]!=NULL) 33 post_order(o->ch[0]); 34 if(o->ch[1]!=NULL) 35 post_order(o->ch[1]); 36 cout<<o->x<<" "; 37 } 38 void dfs(int dp,Node* o) 39 { 40 cout<<o->x<<" "; 41 if(o->ch[0]!=NULL&&!o->ch[0]->vis) 42 { 43 o->ch[0]->vis=1; 44 dfs(dp+1,o->ch[0]); 45 } 46 if(o->ch[1]!=NULL&&!o->ch[1]->vis) 47 { 48 o->ch[1]->vis=1; 49 dfs(dp+1,o->ch[1]); 50 } 51 } 52 Node *q[maxn]; 53 void bfs(Node* o) 54 { 55 int h=0,_t=1; 56 q[_t]=o; 57 while(h!=_t) 58 { 59 h=h%maxn+1; 60 cout<<q[h]->x<<" "; 61 if(q[h]->ch[0]!=NULL&&!q[h]->ch[0]->vis) 62 { 63 _t=_t%maxn+1; 64 q[h]->ch[0]->vis=1; 65 q[_t]=q[h]->ch[0]; 66 } 67 if(q[h]->ch[1]!=NULL&&!q[h]->ch[1]->vis) 68 { 69 _t=_t%maxn+1; 70 q[h]->ch[1]->vis=1; 71 q[_t]=q[h]->ch[1]; 72 } 73 } 74 } 75 int main() 76 { 77 cin>>n; 78 for(int i=1;i<=n;i++) 79 { 80 tot++; 81 node[tot]=new Node(); 82 node[tot]->x=i; 83 } 84 for(int i=1;i<=n;i++) 85 { 86 int l,r; 87 cin>>l>>r; 88 node[i]->ch[0]=node[l]; 89 node[i]->ch[1]=node[r]; 90 } 91 root=node[1]; 92 pre_order(root); 93 cout<<endl; 94 in_order(root); 95 cout<<endl; 96 post_order(root); 97 cout<<endl; 98 dfs(1,root); 99 cout<<endl; 100 for(int i=1;i<=tot;i++) 101 node[i]->vis=0; 102 bfs(root); 103 return 0; 104 }
最后,针对完全二叉树、满二叉树这样的密集型的树,由于其上节点和左右子节点之间存在下标的运算关系,所以建议以数组形式存储
其实数组存储和链式存储最大的区别是,一个是存在连续的空间里,一个是存在分立的空间里(至少你看上去是分立的)
然而,一般二叉树儿完全二叉树在使用数组存储时的区别是,后者是逻辑有序的
这里以线段树的存储形式为例,介绍完全二叉树的数组存储形式
struct tree { long long sum,lazy; long long _max,_min; bool v; int l,r; }t[4*maxn];
在本文中可以完全忽略结构体里面的所有的字段,仅看数组的定义(其实本质上完全二叉树直接用数组存就可以了)
在取用左右孩子的时候:
int ch=o*2; t[o]._max=max(t[ch]._max,t[ch+1]._max);
以这样的形式就可以了
另外,对于一些特殊的树(这里指的是那些不是二叉树的树),一般直接存成图,但是有的树结构Tire树可以直接在本文介绍的第一种形式:二叉树的数组存储中,把ch数组改大就可以了
具体可以参考字符串分类中关于Tire树的介绍,这里不再叙述。