每周一道数据结构(三)树、二叉树、最优二叉树

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

 

树的相关定义

  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;
}

  


本文 由 cococo点点 创作,采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,请注明出处:
转载自:cococo点点 http://www.cnblogs.com/coder2012

时间: 2024-09-14 12:27:41

每周一道数据结构(三)树、二叉树、最优二叉树的相关文章

每周一道数据结构(二)排序总结

排序 所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来. 排序是数据处理中经常使用的一种重要运算.在计算机及其应用系统中,花费在排序上的时间在系统运行时间中占有很大比重,并且排序本身对推动算法分析的发展也起很大作用.目前已有上百种排序方法,但并没有一个万能的排序方法来解决所有问题,接下来介绍几种常用的排序方法,并对它们进行分析和比较.   分类 1.按是否涉及数据的内.外存交换 内排序 在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内.外存交换,则称之为内

每周一道数据结构(四)A*算法&amp;博弈树α-β剪枝

前阵子考试学了A*算法.博弈树和回溯,自己真是愚蠢至极,根本没就搞明白这些,所以对于这些算法问道的话就不能说清楚,也记不住,所以才有了这篇笔记.在这里感谢面试我的那位工程师~~ A*算法 一些重要的概念 启发式信息:用于帮助减少搜索量的与问题有关的信息或知识. 启发式搜索:使用启发信息指导的搜索过程叫做启发式搜索. 估价函数:定义在状态空间上的实值函数. open表:未扩展的节点 close表:已扩展或正在扩展的节点 用f(n)表示节点n的估价函数:    1. f(n)表示从起点到目标,经由节

数据结构之树、森林和二叉树的转换

树转换为二叉树 (1)加线.在所有兄弟结点之间加一条连线. (2)去线.树中的每个结点,只保留它与第一个孩子结点的连线,删除它与其它孩子结点之间的连线. (3)层次调整.以树的根节点为轴心,将整棵树顺时针旋转一定角度,使之结构层次分明.(注意第一个孩子是结点的左孩子,兄弟转换过来的孩子是结点的右孩子) 森林转换为二叉树 (1)把每棵树转换为二叉树. (2)第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树的根结点的右孩子,用线连接起来. 二叉树转换为树 是树转换为二

每周一道数据结构(一)图

图的定义   图是由结点的有穷集合V和边的集合E组成.其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条 边,就表示这两个顶点具有相邻关系. 图分为两类,一个是有向图,即每条边都有方向,另一个是无向图,即每条边都没有方向.     相关问题 图的遍历问题 最小生成树问题 单源最短路径问题 拓扑排序问题 关键路径   图的遍历方法   和树的遍历类似,图的遍历也是从某个顶点出发,沿着某条搜索路径对图中每个顶点各做一次且仅做一次访问.它是许多图的算

大话数据结构十六:哈夫曼树(最优二叉树)

1. 引子 当前素质教育的背景下,小学的学科成绩都改为了优秀.良好.及格.不及格这样的模糊词语,而不再通报具体的分数. 用代码可以表示如下: if( a < 60 ) System.out.print("不及格"); else if( a < 70 ) System.out.print("及格"); else if( a < 90 ) System.out.print("良好"); else System.out.print(&

@数据结构大神:递归遍历二叉树,建立树的代码 为什么错?

问题描述 @数据结构大神:递归遍历二叉树,建立树的代码 为什么错? //创建-输入-打印-递归 # include<stdio.h> # include<stdlib.h> # include<malloc.h> typedef struct Node{ char data; struct Node *Lchild; struct Node *Rchild; }BiTNode,*BiTree; BiTree CreateBiTree(BiTree bt) { char

如何判断一个树是不是另外一个二叉树的子树呢?

问题描述 如何判断一个树是不是另外一个二叉树的子树呢? 请教大神们一个数据结构的问题,才学,琢磨很久没想出来 解决方案 //判断root2是不是root1开头的子结构 boolIsSubTree(BiTreeNode *root1BiTreeNode *root2) { //先判断root2 if(root2==NULL) return true; if(root1==NULL) return false; if(root1->data!=root2->data) return false;

C++中的树、二叉树、二叉树遍历、二叉树前序、中序、后序遍历相互求法

本博文来总结下树.二叉树以及二叉树前序.中序.后序遍历相互求法,即如果知道两个的遍历,如何求第三种遍历方法,比较笨的方法是画出来二叉树,然后根据各种遍历不同的特性来求,也可以编程求出,下面我们分别说明. 1.什么是树?什么是二叉树? 树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合. 二叉树是指结点的度不超过2的有序树. (结点的度:树中的一个结点拥有的子树数目.) 2.二叉树的前序.中序.后序遍历的特性  二叉树前序遍历特性:   (1).访问根节点  (2).前序遍

最优二叉树算法

练习做一下构造最优二叉树的算法,不过如何计算WPL呢? 本次未能实现,希望懂的人可以帮我解决一下这个问题,不胜感激! // // 头文件:HuffmanTree.h // // 叶子结点的最大数量 #define LEAVES_COUNT 4 // // 二叉树的最大结点总数 #define NODES_COUNT (2 * LEAVES_COUNT - 1) // // 哈夫曼树的结点结构体 typedef struct tagHuffmanTreeNode { float weight; /