数据结构丨【学习笔记】第七章 树

一、树的概念与性质

结点的度:结点拥有的子树的个数
树的度:所有结点度的最大值
子孙结点:每个结点的所有子树的结点
结点的层次
树的高度:所有结点的层次的最大值
有序树:是指从左到右都是有序的
性质1:结点总数等于所有结点的度之和+1.
性质2:m叉树上,第i层最多有m^(i-1)个结点。(根节点层次为1)


二、二叉树


1.基本性质


非空二叉树,第i层最有2^(i-1)个结点。
高度为h的二叉树结点总数最多为2^h-1。→满二叉树:高度为h且有2^h-1个结点。
叶子结点树为n0,度为2的结点树为n2,则n0=n2+1。
→若推广到i叉树,则n0=n2+2n3+……+(i-1)ni+1

例1:若某二叉树有20个叶子结点,有30个结点仅有一个孩子,问该二叉树的总结点数。
          答案是69。设有两个孩子的结点数为x,利用结点总数=结点的度+1求解出x,进一步算出。
例2:

完全二叉树:深度为h的二叉树,前h-1层是满的,并且第h层结点从左至右依次排列。

(满二叉树是底层数满的完全二叉树)

→特别性质:

1.度为1的结点仅有1个或0个(叶子节点尽可能往左排)

2.具有n个节点的完全二叉树的深度为 └log2n┘+ 1
3.叶子结点个数:

           当n1=0时(即度为1的节点为0个时,此时n为奇数)或者n为奇数时

              n0= (n+1)/2;

        当n1=1时(即度为1的节点为1个时,此时n为偶数)或者n为偶数

             n0= n/2;

例题:具有100个结点的完全二叉树,问其叶子结点数。
         

        答案是50。

        第i层至多有2^(i-1)个结点,若是满二叉树则有1+2+4+...2^(i-1)个结点即(2^i)-1个结点。当i=7时最接近101,所以是7层。故第7层有64-(128-101)=37个结点即第7层有37个叶子。而第6层有32个结点,故第6层有32-(37/2的最大整数)=13个叶子节点。所以总共50个叶子结点。

2.存储结构


顺序存储:

链式存储:


(1)二叉链表结构(常考、递归必用):

        结点结构中包含两个指针域分别指向左、右孩子。设有n个结点则有2n个指针域,其中只有n-1个指针域不为空(除根结点外都有指针指向结点)


(2)三叉链表结构:增加一个指针域包含双亲地址,方便向上检索。

3.操作算法

注意牢牢抓住递归思想


遍历(必考,重点)


  先序遍历:根到左到右 
  中序遍历:左到根到右 
  后序遍历:左到右到根
(总是从左到右,只是遍历根的位置不同(在前/在中/在后))
注意考虑在每个分支的二叉树也仍然遵循这样的先后次序
  层次遍历:从根节点从上到下逐层遍历,同一层从左到右依次遍历

  ·二叉树有唯一确定的遍历序列,而一个遍历序列并不能确定二叉树结构。
例题:(判断)带空指针前序遍历序列可以唯一确定一颗树。(√)
例题:(前中后序列知二求一)

构造


由单个遍历序列构造是重点:

//带空指针标记的先序序列构造二叉树
template<class T>
BinNode<T>*BinTree<T>::CreateByPre(vector<T>&pre,int&i)
{
   T e=pre[i];
   i++;
   if(e=='*')
      return NULL;
   BinNode<T>*p=new BinNode<T>;
   p->data=e;
   p->lchild=CreateByPre(pre,i);
   p->rchild=CreateByPre(pre,i);
   return p;
}

template<class T>
BinTree<T>::BinTree<T>(vector<T>&pre)
{
   int i=0;
   root=CreateByPre(pre,i);
}


拷贝构造:


析构:


计算二叉树的结点数(重要!)

template<class T>
int BiTree<T>::Count(BiNode<T>*p)
{
   if(p==NULL)
     return 0;
   int left=Count(p->lchild);
   int right=Count(p->rchild);
   return 1+left+right;
}
template<class T>
int BiTree<T>::Count()
{
   return Count(root);
}


计算二叉树的高度


根据关键值查找结点


查找该结点的双亲


例1:输入二叉树的带空指针的前序序列,建立一颗二叉树并求该树的叶子结点总数。

template<class T>
int BiTree<T>::TreeLeaf_Count(BiNode<T>*p)
{
   if(p==NULL)
     return 0;
   if (p->left == NULL && p->right == NULL) //说明根结点就是叶子结点
	  return 1;
   int left=TreeLeaf_Count(p->lchild);
   int right=TreeLeaf_Count(p->rchild);
   return left+right;
}
template<class T>
int BiTree<T>::TreeLeaf_Count()
{
   return TreeLeaf_Count(root);
}

→那求度为2的结点总数呢?

template<class T>
int BiTree<T>::Count2(BiNode<T>*p)
{
   if(p==NULL)
     return 0;

   else 
   {    
      int left=Count2(p->lchild);
      int right=Count2(p->rchild);
      if (p->left != NULL && p->right != NULL) //判断根节点的度数是否为2
	         return 1+left+right;
      else
             return left+right;
   }

}
template<class T>
int BiTree<T>::Count2()
{
   return Count2(root);
}

→求第k层结点数目

(以下都为核心代码即

 if(p==NULL)
     return 0;

   else 
   {  

   ...........

   }里的内容)


	if (k == 1)
		return 1;
	k--;
	return KLevel_Count(p->lchild, k) + KLevel_Count(p->rchild, k);

→那求单分支的结点个数?

    if ((p->lchild==NULL && p->rchild!=NULL) ||	(p->lchild!=NULL && p->rchild==NULL)
		return Single_Count(p->lchild)+ Single_Count(p->rchild)+1;//根结点就是单分支结点
	else
		return Single_Count(b->lchild)+ Single_Count(b->rchild);

→求所有双分支结点的个数?

if(p->lchild!=NULL && p->rchild!=NULL)
    return Double_Count(p->lchild)+Double_Count(p->rchild)+1;//根节点就是双分支
 else 
    return Double_Count(p->lchild)+Double_Count(p->rchild);
}

→二叉树中结点值等于某给定值结点个数。

template<class T>
int BiTree<T>::CountNode(BiNode<T> *p,T x)
{
 
    if(p != NULL)
    {
        if(p->data == x)
            return CountNode(p->lchild,x)+CountNode(p->rchild,x)+1;
        else
            return CountNode(p->lchild,x)+CountNode(p->rchild,x);
    }
    return 0;
}

例2: 设采用二叉链表存储二叉树,试编写算法在二叉树中查找data域为x的结点。

例3:设采用二叉链表存储二叉排序树,试编写算法在二叉排序树中求任意两个不同结点的共同祖先。

三、线索二叉树

1.基本概念:

     (为了遍历中快速查找结点的前驱和后继)
       二叉树有n个结点,则总的有2n个指针,有指向的有n-1个指针(根节点无指向)(或者说非空链域),则无指向的空指针有n+1个。

       为利用这些空指针域,引入线索(指向前驱或后继的指针)。结点中空的左孩子指针域lchild存放前驱地址,空的rchild指向后继地址。

     例1:判断:线索二叉树中,任一结点均有指向前驱和后继的线索。(×)得是空指针。


2.存储结构:

0指向孩子指针,1指向前驱或后继

3.操作算法:

例1:用非递归的方法实现中序遍历中序线索树。

template<class T>
void InBiThrTree<T>::Traverse()
{
   BiThrNode<T>*p=root;
while(p=>ltype==LINK)//1.找到中序遍历的起点,即最左下方的点
  p=p->lchild;
while(p!=NULL)
  {
    cout<<p->data<<" ";//2.访问当前你借点
    p=GetNext(p);//3.找到当前结点的后继结点
  }
}


四、树


1.存储结构

例1:树的存储方法有哪些?任画一个树,举例说明具体存储结构。


2.操作算法

构造:

依据序偶像结点添加结点和关系

计算树的高度:

计算所有结点的度:

例1:

遍历:

前根遍历:访问根结点,从左到右前根遍历每棵子树。

后根遍历:从左到右根后遍历每棵子树,再访问根结点。

     当多叉树采用孩子兄弟表示法存储为二叉链表结构时,可借助二叉链表的遍历算法实现多叉树的遍历算法。在这样的多叉树(逻辑结构)和二叉树(存储结构)的对应关系时,树的前根遍历等效于二叉树的先序遍历,树的后根遍历是先访问左子树(即树的所有子树)再访问根结点,等效于二叉树的中序遍历。


五、Huffman树


基本概念:

  
     无度为1的结点,n=n1+n2+n0=2n0-1
     小的往下沉,大的靠近根节点,注意大小别比错了!
     编码:左为0右为1

存储结构:

采用三叉链表结构
struct HuffmanNode
{
char data;
double weight;
int parent,lchild,rchild;//孩子结点的位置
}
 

操作算法:

构造函数:

编码、译码

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值