数据结构:树

使用链表的存储方式是完全可以存树的

但是为了方便处理树的一些特性,我们将链表的存储结构进行了适当的修改

针对着一般二叉树,完全二叉树,我们也有着不同的存储策略

首先我们介绍对于一般二叉树而言的数组存储和指针存储,完全二叉树将在后面提到

对于数组存储而言,我们定义这样一个结构

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树的介绍,这里不再叙述。

posted @ 2018-07-17 15:24  静听风吟。  阅读(301)  评论(0编辑  收藏  举报