是一种非线性结构

结点的度:结点拥有的子树个数或分支个数

树的度:树中各节点度的最大值

根节点的高度为树的高度

如果将树中结点各子树看成从左至右是有次序的,不能互换的,称该树为有序树否则为无序树

 

树的存储结构-顺序存储结构

双亲存储结构

  int tree[maxsize];

  数组元素的内容表示该节点的双亲节点 ,根节点位置上的内容为-1

树的存储结构-链式存储结构

  孩子存储结构(图的邻接表)

  孩子兄弟存储结构()

 

二叉树

满二叉树与完全二叉树

二叉树的性质:

1、非空二叉树上叶子结点数等于双分支节点数+1 即n0=n2+1

推导:由总分支数=总结点数-1  得n0+n1+n2-1=n1+2n2

2、二叉树第i层上最多有2^(i-1)个结点

3、高度为K的二叉树最多有2^k-1个结点 ,即满二叉树前k层结点数为2^k-1

4、i为某结点a的编号,i/2、2i、2i+1 为双亲和左右孩子

5、给定n个结点, 构成  [(n+1)*(n+2)*......2n]/[1*2*.....(n+1)]种树

6、具有n个结点的完全二叉树高度h为   h-1<=log2 n<h

 

二叉树存储结构

  顺序存储结构

  适合于完全二叉树,存储一般二叉树会浪费空间

  链式存储结构

typedef struct BTNode
{
  char data;
  struct BTNode *lchild;
  struct BTNode *rchild;
} BTNode;

二叉树的遍历

前序遍历

void preorder(BTNode *p)
{
  if(p!=NULL)
  {
    Visit(p);
    preorder(p->lchild);
    preorder(p->rchild);
  }
}

中序遍历

void inorder(BTNode *p)
{
  if(p!=NULL)
  {
    inorder(p->lchild);
    Visit(p);
    inorder(p->rchild);
  }
}

后序遍历

void postorder(BTNode *p)
{
  if(p!=NULL)
  {
    postorder(p->lchild);
    postorder(p->rchild);
    Visit(p);
  }
}

层次遍历

void level(BTNode *p)
{
  int front,rear;
  BTNode *que[maxsize];
  front=rear=0;
  BTNode *q;
  if(p!=NULL)
  {
    rear=(rear+1)%maxsize;
    que[rear]=p;
    while(front!=rear)//队列不空时进行循环 
    {
      front=(front+1)%maxsize;
      q=que[front];
      Visit(q);
      if(q->child!=NULL)//左子树不空 左子树根节点入队
      {
        rear=(rear+1)%maxsize;
        que[rear]=q->lchild;
      }
      if(q->rchild!=NULL)//右子树不空 右子树根节点入队
      {
        rear=(rear+1)%maxsizr;
        que[rear]=q->rchild;
      }
    }
  }
}

改进递归的遍历算法

方法一:用户自定义栈代替系统栈

先序遍历非递归

void preorderNonrecursion(BTNode *bt)
{
  if(bt!=NULL)
  {
    BTNode *Stack[maxsize];
    int top=-1;
    BTNode *p;
    Stack[++top]=bt;
    while(top!=-1)//栈空则退出循环
    {
      p=Stack[top--];
      Visit(p);
      if(p->lchild!=NULL)
      {
        Stack[++top]=p->rchild;//右孩子先入栈 
      }
      if(p->rchild!=NULL)
      {
        Stack[++top]=p->lchild;//左孩子后入栈
      }
    }
  }
}

中序遍历非递归

void inorderNonrecursion(BTNode *bt)
{
  if(bt!=NULL)
  {
    BTNode *Stack[maxsize];
    int top=-1;
    BTNode *p;
    p=bt;
    while(top!=-1||p!=NULL)
    {
      while(p!=NULL)//左孩子存在 左孩子入栈
      {
        Stack[++top]=p;
        p=p->lchild;
      }
      if(top!=-1)//栈不空的情况下出栈
      {
        p=Stack[top--];
        Visit(p);
        p=p->child;
      }
    }
  }
}

后序遍历非递归

void postorderNonrecursion(BTNode *bt)
{
  if(bt!=NULL)
  {
    BTNode *Stack1[maxsize];
    int top1=-1;
    int top2=-1;
    BTNode *Stack2[maxsize];
    BTNode *p=NULL;
    Stack1[++top1]=bt;
    while(top1!=-1)
    {
      p=Stack1[top1--];
      Stack2[++top2]=p;
      if(p->lchild!=NULL)
      {
        Stack1[++top1]=p->lchild; //与先序相反 先左后右
      }
      if(p->rchild!=NULL)
      {
        Stack2[++top1]=p->rchild;
      }
    }
    while(top2!=-1)
    {
      p=Stack2[top2--];
      Visit(p);
    }
  }
}

方法二:线索二叉树

省掉栈,把二叉树的遍历过程线性化

n个结点的二叉树 一定有n-1个分支 有n+1个空链域

每个结点多了两个标识域ltag域rtag

如果ltag=0则表示lchild为指针 指向结点的左孩子;如果ltag=1则表示lchild为线索,指向结点的前驱

如果rtag=0则表示rchild为指针 指向结点的右孩子;如果rtag=1则表示rchild为线索,指向结点的后继

 

树转化为二叉树:1、每一层都横穿起来 2、每个父节点都只保留最左的分支

 

二叉树转化为树:上述过程逆置

森林转化为二叉树:每一棵树转化即可

二叉树转化为森林:断开右孩子 并转化为树

树的遍历:

  先序遍历:树的先序遍历对应二叉树的先序遍历

  后序遍历:树的后序遍历对应二叉树的中序遍历

森林的遍历:同样为先序遍历和后序遍历

赫夫曼树与赫夫曼编码

最优二叉树:带权路径最短

构造方法:从集合中选出权值最小的两个 作为左右子树 二者之和为新结点 以此类推

赫夫曼树特点:1、树中无度为1的点   2、权值越大,离根越近

赫夫曼编码:

    任一字符的编码都不是另一字符编码串的前缀编码

    左0右1

赫夫曼n叉树:(m-1)%(k-1)==0  k为叉数  m为补上0结点的结点个数

 _____________________________________________________

例题6-2 求一颗二叉树深度

int getDepth(BTNode *p)
{
  int LD,RD;
  if(p==NULL)
  {
    return 0;
  }
  else
  {
    LD=getDepth(p->lchild);
    RD=getDepth(p->rchild);
    return (LD>RD?LD:RD)+1;
  }
}

例题6-3 寻找data域值等于key的点

void search(BTNode *p,BTNode *&q,int key)
{
  if(p!=key)
  {
    if(p->data==key)
    {
      q=p;
    }
    else
    {
      search(p->lchild,q,key);
      if(q==NULL){//在左子树中没找到才到右子树查找
        search(p->rchild,q,key);
      }
    }
  }
}