Data Structure: Tree

Tree Class Minutes

basic tree concept

 

Nonlinear structure, one immediate predecessor, but possibly multiple immediate successors (1:n)

The definition of a tree is recursive, that is, there are trees within a tree

root leaf forest

ordered tree unordered tree

parents, children, brothers, cousins, ancestors, descendants

Node Node Degree Node Hierarchy Terminal Node Branch Node

The degree of the tree is the maximum value among the degrees of all nodes (Max{degree of each node}

The depth of the tree refers to the maximum level of all nodes (Max{level of each node}

(or height)

A note on disjoint subtrees

tree representation

Graphical representation

Generalized table notation

Left child-right sibling notation

parent child notation

The logical structure of the tree

One-to-many (1:n), there are multiple direct successors (such as family tree, directory tree, etc.), but there is only one root node, and the subtrees do not intersect with each other.

Generalized table notation

Left child-right sibling notation

 

tree storage

Sequential storage, chain storage

 

 

binary tree

1. Basic Concepts

The binary tree has the simplest structure and the strongest regularity

It can be proved that all trees can be converted into unique corresponding binary trees without loss of generality

Definition: It is a finite set of n (n≥0) nodes, consisting of a root node and two disjoint binary trees called left subtree and right subtree respectively.

binary tree properties

Property 1: There are at most 2 i-1 nodes on the i-th level of the binary tree (i>0)

Property 2: A binary tree of depth k has at most 2k -1 nodes (k>0)

Property 3: For any binary tree, if there are n 2 nodes of degree 2 , the number of leaves (n 0 ) must be n 2 +1 (that is, n 0 =n 2 +1)

满二叉树:一棵深度为k 且有2k -1个结点的二叉树。
(特点:每层都"充满"了结点)

完全二叉树:深度为k n个结点的二叉树,当且仅当其每一个结点都与深度为k 的满二叉树中编号从1至n的结点一一对应。

性质4: 具有n个结点的完全二叉树的深度必为ëlog2nû+1

性质5: 对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2i=1 时为根,除外)

二叉树的存储结构

一、顺序存储结构

按二叉树的结点"自上而下、从左至右"编号,用一组连续的存储单元存储。

答:一律转为完全二叉树!

讨论:不是完全二叉树怎么办?

方法很简单,将各层空缺处统统补上"虚结点",其内容为空

二、链式存储结构

二叉树结点数据类型定义:

typedef struct node *tree_pointer;

typedef struct node {

int data;

tree_pointer left_child, right_child;

} node;

树的三叉链表表示

2、遍历二叉树

树的性质确认

树的遍历引申

 
 

3、二叉树编程实践

typedef struct node{

int data;

struct node *lchild,*rchild;

} NODE;

NODE *root;

DLR(NODE *root )

{

if (root) //非空二叉树

{

printf("%d",root->data); //访问D

DLR(root->lchild); //递归遍历左子树

DLR(root->rchild); //递归遍历右子树

}

}

中序遍历算法

LDR(NODE *root)

{ if(root !=NULL)

{ LDR(root->lchild);

printf("%d",root->data);

LDR(root->rchild);

} }

后序遍历算法

LRD (NODE *root)

{if(root !=NULL)

{LRD(root->lchild);

LRD(root->rchild);

printf("%d",root->data);

} }

练习

例:编写递归算法,计算二叉树中叶子结点的数目

DLR_CountLeafNum(NODE *root)//采用中序遍历的递归算法

{

if ( root) //非空二叉树条件,还可写成if(root !=NULL )

{ if(!root->lchild&&!root->rchild) //是叶子结点则统计并打印

{ sum++; printf("%d\n",root->data); }

DLR_CountLeafNum(root->lchild); //递归遍历左子树,直到叶子处;

DLR_CountLeafNum(root->rchild);}//递归遍历右子树,直到叶子处;

} return(0);

}

 

 

前序遍历

思路:利用前序遍历来建树(结点值陆续从键盘输入,用DLR为宜)

Bintree createBTpre( )

{ Bintree T; char ch;

scanf("%c",&ch);

if(ch=='#') T=NULL;

else

{ T=( Bintree )malloc(sizeof(BinTNode));

T->data=ch;

T->lchild=createBTpre();

T->rchild=createBTpre();

}

return T;

}

后序遍历销毁一个数

结论:通过中序遍历和先序遍历可以确定一个树

通过中序遍历和后续遍历可以确定一个数。

通过先序遍历和后序遍历确定不了一个数。

 

 

4、二叉线索树

概念

普通二叉树只能找到结点的左右孩子信息,而该结点的直接前驱和直接后继只能在遍历过程中获得。

若可将遍历后对应的有关前驱和后继预存起来,则从第一个结点开始就能很快"顺藤摸瓜"而遍历整个树了。

 

 

线索化过程就是在遍历过程(假设是中序遍历)中修改空指针的过程:

将空的lchild改为结点的直接前驱;

将空的rchild改为结点的直接后继。

 

二叉树线索化算法

void InTreading(BiThrTree p)

//中序遍历进行中序线索化

{

if (p)

{

    InThreading(p->lchild); /*左子树线索化*/

    if (!p->lchild) /*前驱线索*/

        { p->ltag=1; p->lchild=pre; }

    if (!pre->rchild) /*后继线索*/

        { pre->rtag=1; pre->rchild=p; }

    pre=p;

    InThreading(p->rchild); /*右子树线索化*/

  }

}

二叉树线索化遍历算法

程序注解 (非递归,且不用栈):

P=T->lchild; //从头结点进入到根结点;

while( p!=T)

{ while(p->LTag==link)p=p->lchild; //先找到中序遍历起点

if(!visit(p->data)) return ERROR; //若起点值为空则出错告警

while(p->RTag==Thread ……){ p=p->rchild; Visit(p->data);}

//若有后继标志,则直接提取p->rchild中线索并访问后继结点;

p=p->rchild; //当前结点右域不空或已经找好了后继,则一律从结点的右子树开始重复{ }的全部过程。

}

Return OK;

 

5、霍夫曼树

对于文本"BADCADFEED"的传输而言,因为重复出现的只有

"ABCDEF"这6个字符,因此可以用下面的方式编码:

接收方可以根据每3个bit进行一次字符解码的方式还原文本信息。

这样的编码方式需要30个bit位才能表示10个字符

那么当传输一篇500个字符的情报时,需要15000个bit位

在战争年代,这种编码方式对于情报的发送和接受是很低效且容易出错的。

如何提高收发效率?

要提高效率,必然要从编码方式的改进入手,要避免每个字符都占用相同的bit位

准则:任一字符的编码都不是另一个字符编码的前缀!

霍夫曼树

1.给定n个数值{ v1, v2, …, vn}

2.根据这n个数值构造二叉树集合F

F = { T1, T2, …, Tn}

Ti的数据域为vi,左右子树为空

3.在F中选取两棵根结点的值最小的树作为左右子树构造一棵新的二叉树,这棵二叉树的根结点中的值为左右子树根结点中的值之和

4.在F中删除这两棵子树,并将构造的新二叉树加入F中

5.重复3和4,直到F中只剩下一个树为止。这棵树即霍夫曼树

假设经过统计ABCDEF在需要传输的报文中出现的概率如下

霍夫曼树是一种特殊的二叉树

霍夫曼树应用于信息编码和数据压缩领域

霍夫曼树是现代压缩算法的基础

 

 

二叉树的遍历-中序遍历非递归算法

中序 遍历的几种情况

分析1:什么时候访问根、什么时候访问左子树、什么访问右子树

当左子树为空或者左子树已经访问完毕以后,再访问根

访问完毕根以后,再访问右子树。

分析2:为什么是栈,而不是其他队列。

先走到的后访问、后走到的先访问,显然是栈结构

分析3:结点所有路径情况

步骤1:结点的所有路径情况

如果结点有左子树,该结点入栈;

如果结点没有左子树,访问该结点;

分析3:路径所有情况

如果结点有右子树,重复步骤1;

如果结点没有右子树(结点访问完毕),回退,让栈顶元素出栈,访问栈顶元素,并访问右子树,重复步骤1

如果栈为空,表示遍历结束。

注意:入栈的结点表示,本身没有被访问过,同时右子树也没有被访问过。

分析4:有一个一直往左走入栈的操作

 
 

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325196506&siteId=291194637