数据结构总结(树和二叉树)

  树是数据结构的重点,更是算法的重点。这一章的知识还是不少的,需要多看,多思考,尤其是一些递归的代码,怎样想到递归,返回值是啥,参数为什么这样写,递归出口如何设置都需要搞清楚,弄明白。重点是第四部分,二叉树的存储结构及实现有很多细节。

 一,树的逻辑结构

  这里所说的树都是有序树,树的其中一个重要功能便是遍历,分为前序(根)遍历、后序(根)遍历和层序(次)遍历。

是根据访问根节点的时机不同来区分的,注意,还有一个中序遍历,这是二叉树特有的遍历。

  树的度注意与图的度区分。

 二,树的存储结构

  1,双亲表示法。用一维数组存储树的各个节点

data存储树中结点的数据信息

parent存储该结点的双亲在数组中的下标

2,孩子表示法-多重链表表示法

方案一:指针域的个数等于树的度 (浪费空间)

data:数据域,存放该结点的数据信息;

            child1~childd指针域,指向该结点的孩子。

方案二: 指针域的个数等于该结点的度(节点结构不一致)

 

data:数据域,存放该结点的数据信息;

            degree:度域,存放该结点的度;

            child1~childd指针域,指向该结点的孩子。

 3,孩子表示法-孩子链表表示法(每个节点创建一个单链表)

把每个结点的孩子排列起来,看成是一个线性表,且以单链表存储,则n个结点共有 n 个孩子链表。

n 个单链表共有 n 个头指针,这 n 个头指针又组成了一个线性表。

为了便于进行查找采用顺序存储存储每个链表的头指针。

最后,将存放 n 个头指针的数组和存放n个结点的数组结合起来,构成孩子链表的表头数组。  

4,孩子兄弟表示法

设置两个分别指向该结点的第一个孩子和右兄弟的指针

 

data:数据域,存储该结点的数据信息;

firstchild指针域,指向该结点第一个孩子;

rightsib指针域,指向该结点的右兄弟结点

三,二叉树的逻辑结构

1,斜树

     1. 在斜树中,每一层只有一个结点;

     2.斜树的结点个数与其深度相同

2,满二叉树

  1.叶子只能出现在最下一层;
  2.只有度为0和度为2的结点。 

满二叉树在同样深度的二叉树中结点个数最多

满二叉树在同样深度的二叉树中叶子结点个数最多

   3,完全二叉树  

    1. 叶子结点只能出现在最下两层,且最下层的叶子结点都集中在二叉树的左部;

     2. 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。

      3. 深度为k的完全二叉树在k-1层上一定是满二叉树。

4,二叉树的基本性质

    二叉树的第i层上最多有2的i-1次方个结点(i1)。 

 

一棵深度为k的二叉树中,最多有2的k次方-1个结点,最少有k个结点。

深度为k且具有2k-1个结点的二叉树一定是满二叉树,

深度为k且具有k个结点的二叉树不一定是斜树。

  在一棵二叉树中,如果叶子结点数为n0度为2的结点数为n2则有: n0n2+1。

5,完全二叉树的基本性质

具有n个结点的完全二叉树的深度为 log2n向下取整  +1。

对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i(1≤in的结点(简称为结点i),有:

(1)如果i>1,

  则结点i的双亲结点的序号为  i/2;如果i=1,

  则结点i是根结点,无双亲结点。

(2)如果2in

  则结点i的左孩子的序号为2i

  如果2in则结点i无左孩子。

(3)如果2i+1≤n,

  则结点i的右孩子的序号为2i+1;如果2i+1>n则结点 i无右孩子。

  四,二叉树的存储结构及实现

   1,二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位(下标)应能体现结点之间的逻辑关系——父子关系。完全二叉树满二叉树中结点的序号可以唯一地反映出结点之间的逻辑关系

  中序+前(后)序遍历可确定一棵二叉树。下面是中序和前序的例子:

#include<bits/stdc++.h>
using namespace std;
typedef struct nnode{
char data;
struct nnode *lchild,*rchild;
}nnode,*btree;                  //此处用到typedef将nnode* 重命名为btree。可进行等价替换
                                //struct nnode{
                                 // char data;
                                 //struct nnode *lchild,*rchild;
                                 //};
                                  //nnode *btree;
string porder,iorder;
btree Build(string porder,string iorder,int s1,int e1,int s2,int e2)   //nnode* Build(
{
    btree t =new nnode();                  //nnode  *t =new nnode();
    t->data=porder[s1];
    int root;
    for(int i=s2;i<=e2;i++)
    {
        if(porder[s1]==iorder[i])
        {
            root=i;break;
        }
    }
    int llen = root - s2;
    int rlen = e2 - root;
    if(llen != 0)    {
    t -> lchild = new nnode();
    t -> lchild = Build(porder,iorder,s1 + 1,s1 + llen,s2,s2 + llen - 1);
            }
    else
    t -> lchild = NULL;    if(rlen != 0)    {
    t -> rchild = new nnode();
    t-> rchild = Build(porder,iorder,e1 - rlen + 1,e1,e2 - rlen + 1,e2);
            }
    else    {
        t -> rchild = NULL;
            }
    return t;

}
void horder(btree t){       //void horder(nnode *t){
if(t!=NULL)
{
    horder(t->lchild);
    horder(t->rchild);
    cout<<t->data;
}
}
int main(){
      btree t=NULL;        //nnode *t=NULL;
     cin>>porder>>iorder;
      t=Build(porder,iorder,0,porder.length()-1,0,iorder.length()-1);
      horder(t);


}

  2,二叉链表

       标准二叉链表(拓展二叉树构建的树)结构表示如下:

#include<iostream>
#include<string.h>
using namespace std;
struct bnode{
    char data;
    bnode *lchild,*rchild;

};
class btree{
    private:
    bnode *creat();
    bnode *root;
    void inorder(bnode *bt);
     void porder(bnode *bt);
      void horder(bnode *bt);
    public:
    btree(){ root=creat(); }
    void inorder(){inorder(root);}
    void porder(){porder(root);}
    void horder(){horder(root);}        //在函数有返回值时 要加return
    };
bnode *btree::creat(){
    bnode *bt;
    char a;
    cin>>a;
    if(a=='#') bt=NULL;
    else{
    bt= new bnode;
    bt->data=a;
    bt->lchild=creat();
    bt->rchild=creat();
    }
    return bt;

}
void btree::inorder(bnode *bt){
    if(bt==NULL) return;
    else{
inorder(bt->lchild);
cout<<bt->data;
        inorder(bt->rchild);
    }
////后序遍历的方法析构,将输出改为delete root

 遍历还可以不使用递归算法,前,中,后,可用栈,中序可用队列实现。

前序遍历:每个节点只进栈一次,在进栈前访问节点

中序遍历:每个节点进栈一次,在出栈时访问节点

后序遍历:每个节点进栈两次,在第二次出栈时访问节点(要做标记,比较麻烦)

    / /前序遍历基本思想/ /
while (root!=NULL | | !s.empty())     {
         while (root!= NULL)         {
             cout<<root->data;
             s.push(root);
             root=root->lchild;  
         }
         if (!s.empty()) { 
             root=s.top();
                s.pop();
             root=root->rchild;  
         }
     }

 层序遍历,非递归

1.队列Q初始化;

2. 如果二叉树非空,将根指针入队;

3.  循环直到队列Q空  (重复队首出队左右儿子入队)

      3.1 q=队列Q的队头元素出队;

      3.2 访问结点q的数据域;

      3.3 结点q存在左孩子,则将左孩子指针入队;

      3.4 结点q存在右孩子,则将右孩子指针入队;

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章