(转)数据结构:树、二叉树、最优二叉树

来源:http://www.cnblogs.com/coder2012/archive/2013/06/05/3102868.html

  树形结构是一类非常重要的非线性结构,它可以很好地描述客观世界中广泛存在的具有分支关系或层次特性的对象,因此在计算机领域里有着广泛应用,如操作系统中的文件管理、编译程序中的语法结构和数据库系统信息组织形式等。

树的相关定义

  1. 节点的度:一个节点含有的子树的个数称为该节点的度;
  2. 树的度:一棵树中,最大的节点的度称为树的度;
  3. 叶节点终端节点:度为零的节点;
  4. 非终端节点分支节点:度不为零的节点;
  5. 双亲节点父节点:若一个结点含有子节点,则这个节点称为其子节点的父节点;
  6. 孩子节点子节点:一个节点含有的子树的根节点称为该节点的子节点;
  7. 兄弟节点:具有相同父节点的节点互称为兄弟节点;
  8. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  9. 树的高度深度:树中节点的最大层次;
  10. 堂兄弟节点:双亲在同一层的节点互为堂兄弟;
  11. 节点的祖先:从根到该节点所经分支上的所有节点;
  12. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
  13. 森林:由m(m>=0)棵互不相交的树的集合称为森林;

二叉树

 二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。

  二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树右子树的二叉树组成。

  完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树。

1.二叉树的遍历

   所谓遍历二叉树,就是遵从某种次序,访问二叉树中的所有结点,使得每个结点仅被访问一次。

  二叉树的遍历包括三种:

  • DLR称为前根遍历(前序遍历)

    •   访问结点的操作发生在遍历其左右子树之前
  • LDR称为中根遍历(中序遍历)
    •   访问结点的操作发生在遍历其左右子树之中(间)
  • LRD称为后根遍历(后序遍历)
    •   访问结点的操作发生在遍历其左右子树之后

递归实现:

void preorder(NODE *p)
{
if(p!=NULL)
{printf(“%d ”,p->data);
preorder(p->lchild);
preorder (p->rchild);}
}

void InOrder(BinTree T)
{
     if(T) { // 如果二叉树非空
      InOrder(T->lchild);
      printf("%c",T->data); // 访问结点
     InOrder(T->rchild);
    }
} // InOrder

void posorder(NODE *p)
{
   if(p!=NULL)
   {
       posorder(p->lchild);
       posorder (p->rchild);
       printf(“%d ”,p->data);
    }
}

非递归实现:

/*算法思想:

利用队列基本操作
1.初始化:根结点入队列
2.while(队列非空)
{
a.队首元素出队列
b.原队首元素对应的左、右孩子(非空)入队列
}*/

void traverse(NODE *T)
{
   NODE *q[100];
   int head,tail, i;
   q[0]=T;head=0;tail=1;
   while(head<tail)
   {
      p=q[head++];
      printf(“%c”,T->data);
      if(p->lchild!=NULL)
         q[tail++]=p->lchild;
      if(p->rchild!=NULL)
         q[tail++]=p->rchild;
     }
}

2.二叉树的构造

  基于先序遍历的构造,即以二叉树的先序序列为输入构造。

void CreateBinTree (BinTree *T)
{
        char ch;
        if((ch=getchar())==‘‘)
               *T=NULL; //读人空格,将相应指针置空
        else{ //读人非空格
              *T=(BinTNode *)malloc(sizeof(BinTNode)); //生成结点
              (*T)->data=ch;
              CreateBinTree(&(*T)->lchild); //构造左子树
              CreateBinTree(&(*T)->rchild); //构造右子树
         }
 }

最优二叉树

  在权为wl,w2,…,wn的n个叶子所构成的所有二叉树中,带权路径长度最小(即代价最小)的二叉树称为最优二叉树哈夫曼树

  假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:

  1. 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
  2. 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
  3. 从森林中删除选取的两棵树,并将新树加入森林;
  4. 重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树。

  具体代码:

#include "stdio.h"
#include "stdlib.h"
#define m 100

struct ptree              //定义二叉树结点类型
{
int w;                   //定义结点权值
struct ptree *lchild;     //定义左子结点指针
struct ptree *rchild;     //定义右子结点指针
};

struct pforest              //定义链表结点类型
{
struct pforest *link;
struct ptree *root;
};

int WPL=0;                  //初始化WTL为0
struct ptree *hafm();
void travel();
struct pforest *inforest(struct pforest *f,struct ptree *t);

void travel(struct ptree *head,int n)
{
//为验证harfm算法的正确性进行的遍历
struct ptree *p;
p=head;
if(p!=NULL)
 {
       if((p->lchild)==NULL && (p->rchild)==NULL) //如果是叶子结点
      {
              printf("%d ",p->w);
              printf("the hops of the node is: %d/n",n);
       WPL=WPL+n*(p->w);    //计算权值
         }//if
travel(p->lchild,n+1);
travel(p->rchild,n+1);
}//if
}//travel

struct ptree *hafm(int n, int w[m])
{
struct pforest *p1,*p2,*f;
struct ptree *ti,*t,*t1,*t2;
int i;
f=(pforest *)malloc(sizeof(pforest));
f->link=NULL;
for(i=1;i<=n;i++)          //产生n棵只有根结点的二叉树
  {
       ti=(ptree*)malloc(sizeof(ptree));//开辟新的结点空间
    ti->w=w[i];               //给结点赋权值
    ti->lchild=NULL;
    ti->rchild=NULL;
    f=inforest(f, ti);
       //按权值从小到大的顺序将结点从上到下地挂在一颗树上
  }//for
while(((f->link)->link)!=NULL)//至少有二棵二叉树
  {
  p1=f->link;
  p2=p1->link;
  f->link=p2->link;           //取出前两棵树
  t1=p1->root;
  t2=p2->root;
  free(p1);                 //释放p1
  free(p2);                 //释放p2
  t=(ptree *)malloc(sizeof(ptree));//开辟新的结点空间
  t->w = (t1->w)+(t2->w);         //权相加
  t->lchild=t1;
  t->rchild=t2;             //产生新二叉树
  f=inforest(f,t);          //每次构造一颗二叉树的时候,都要从新排列一下
  }//while
  p1=f->link;
  t=p1->root;
  free(f);
 return(t);                  //返回t
 }

pforest *inforest(struct pforest *f,struct ptree *t)
{
//按权值从小到大的顺序将结点从上到下地挂在一颗树上
struct pforest *p, *q, *r;
struct ptree *ti;
r=(pforest *)malloc(sizeof(pforest)); //开辟新的结点空间
r->root=t;
q=f;
p=f->link;
while (p!=NULL)            //寻找插入位置
 {
   ti=p->root;
   if(t->w > ti->w)          //如果t的权值大于ti的权值
     {
         q=p;
      p=p->link;             //p向后寻找
     }//if
   else
      p=NULL;                  //强迫退出循环
  }//while
r->link=q->link;
q->link=r;                 //r接在q的后面
return(f);                 //返回f
}

void InPut(int &n,int w[m])
{
printf("please input the sum of node/n"); //提示输入结点数
scanf("%d",&n);      //输入结点数
printf ("please input weight of every node/n"); //提示输入每个结点的权值
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);  //输入每个结点权值
}

int main( )
{
struct ptree *head;
int n,w[m];
InPut(n,w);
head=hafm(n,w);
travel(head,0);
printf("The length of the best path is WPL=%d", WPL);//输出最佳路径权值之和
return 1;
}

  

时间: 2024-08-26 04:31:56

(转)数据结构:树、二叉树、最优二叉树的相关文章

哈夫曼树(最优二叉树)及哈夫曼算法

哈夫曼树 在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUFFMAN)树和哈夫曼编码.哈夫曼编码是哈夫曼树的一个应用.哈夫曼编码应用广泛,如JPEG中就应用了哈夫曼编码. 首先介绍什么是哈夫曼树.哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数).树的带权路径长度记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权

哈夫曼树(最优二叉树)及其Java实现

一.定义 一些定义: 节点之间的路径长度:在树中从一个结点到另一个结点所经历的分支,构成了这两个结点间的路径上的经过的分支数称为它的路径长度 树的路径长度:从树的根节点到树中每一结点的路径长度之和.在结点数目相同的二叉树中,完全二叉树的路径长度最短. 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数. 结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积. 树的带权路径长度(Weighted Path Length of Tree:WPL):定义为树中所有叶子结点的带权路径长

数据结构之Huffman树与最优二叉树

最近在翻炒一些关于树的知识,发现一个比较有意思的二叉树,huffman树,对应到离散数学中的一种名为最优二叉树的路径结构,而Huffman的主要作用,最终可以归结到一种名为huffman编码的编码方式,使用huffman编码方式,我们可以以平均长度最短的码字来记录一串信息,且每个信息分子的编码唯一,独立.从而最终合成编码所对应的信息唯一,无歧义. huffman树的创建时基于每个信息分子都拥有其权重,权重越大,越靠近树根,即路径越短, 下面我们我们来以一个huffman树的例子为例:简单引入一下

哈夫曼树(最优二叉树)的创建

哈夫曼树是带权路径最小的一种特殊二叉树,所以也称最优二叉树. 在这里不讨论基本概念如怎样计算路径等,而仅仅着重于树的创建,详细过程让我们举例而言. 其主要的原理为:将全部节点一開始都视为森林.每次从森林中选取两个根节点权值最小的树合并为一棵新树,新树的根节点大小为两个子节点大小的和,并将这棵新树又一次增加到森林中. 如此一来每一轮操作都能够简化为两个基本操作:合并两棵树.插入新树,直到森林中仅仅剩下一棵树,即是哈夫曼树. 以7个节点的权值分别为 1 3 7 9 12 18 25而言 创建的第一步

树和二叉树-&gt;最优二叉树

文字描述 结点的路径长度 从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径,路径上的分支数目称作路径长度. 树的路径长度 从树根到每一个结点的路径长度之和叫树的路径长度. 结点的带权路径长度 从该结点到树根之间的路径长度与结点上权的乘积 树的带权路径长度 所有叶子结点的带权路径长度之和 最优二叉树或哈夫曼树 假设有n个权值{w1,w2, - ,wn},试构造一颗有n个叶子结点的二叉树,每个叶子结点带权wi,则其中带权路径长度WPL最小的二叉树称作最优二叉树或哈夫曼树. 哈夫曼算法 目

数据结构--树(上)-- 二叉树的遍历

二叉树的遍历 主要是以二叉树的链式存储来讲. 链表存储的结构:(下面会用到) typedef struct TreeNode *BinTree; typedef BinTree Position; struct TreeNode { ElementType Data; BinTree Left; BinTree Right; } 二叉树的递归遍历 1.前序遍历 访问顺序是根节点,左子树,右子树.这样的过程实际上就是一种递归.用递归实现 void PreOrderTraversal(BinTree

Huffman tree(赫夫曼树、霍夫曼树、哈夫曼树、最优二叉树)

flyfish 2015-8-1 Huffman tree因为翻译不同所以有其他的名字 赫夫曼树.霍夫曼树.哈夫曼树 定义引用自严蔚敏<数据结构> 路径 从树中一个结点到另一个结点之间的分支构成两个结点之间的路径. 路径长度 路径上的分支数目称作路径长度. 树的路径长度 树的路径长度就是从根节点到每一结点的路径长度之和. 结点的带权路径长度 结点的带权路径长度就是从该结点到根节点之间的路径长度与结点上权的乘积. 树的带权路径长度 树的带权路径长度就是树中所有叶子结点的带权路径长度之和,通常记做

数据结构--树(上)-- 二叉树及存储结构

二叉树及存储结构 二叉树的定义:一个有穷的结点集合.若不为空,则它是由根节点和称为其左子树和右子树的两个不想交的二叉树组成 一般的左右的树是没有左右之分的.二叉树有左右之分. 三种特殊的二叉树 斜二叉树         实质就可以是链表了. 完美二叉树 完全二叉树 二叉树的几个重要的性质 二叉树的抽象数据类型 对二叉树来讲,最重要的就是Traversal() 遍历,讲二叉树基本就讲遍历了. 二叉树的存储结构 顺序存储结构 顺序存储结构 可以存储完全二叉树:从上往下,从左往右,来进行便利.把这种树

赫夫曼树(最优二叉树)

.在选择最小s1s2的时候少了一个空语句分号..调试了好久..坑爹. 这个是最优的编码策略能达到最少的编码 1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 #include<math.h> 5 #include<queue> 6 #include<algorithm> 7 using namespace std; 8 typedef struct 9 {

Huffman树与最优二叉树续

OK,昨天我们对huffman数的基本知识,以及huffman树的创建做了一些简介,http://www.cnblogs.com/Frank-C/p/5017430.html 今天接着聊: huffman树创建完成之后,我们如何去得到huffman编码呢? 图12.4_1 huffman树形结构 图12.4_2  huffman存储结构(数组) 首先,以上面的树为例,我们必须明白几个要点: 1:从什么地方开始访问这颗树:根节点 , index 2n-1 = 15 2:访问的规则:向左为0  向右