zoukankan      html  css  js  c++  java
  • 数据结构:树

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

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

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

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

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

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

  • 相关阅读:
    C++中的string和stringstream用法1
    回调函数简析
    Qt界面设计更新
    C/C++中的类型转换
    桥接模式 bridge pattern
    装饰者模式
    适配器模式
    代理模型
    工厂类---抽象工厂(3)
    [效率神技]Intellij 的快捷键和效率技巧|系列一|常用快捷键
  • 原文地址:https://www.cnblogs.com/aininot260/p/9323574.html
Copyright © 2011-2022 走看看