数据结构学习笔记-树

树是n个结点的有限集,当n为0的时候,该树为空树。在任意一个非空树中,有且仅有一个根结点(root)。当n>1时,其余结点可分为m个互不相交的有限集。其中每一个集合本身又是一棵树,并且成为根(root)的子树(subtree)。

数据结构学习笔记-树_第1张图片

树的结点包含一个数据元素和若干个指向其子树的分支结点拥有的子树的数量称为结点度(degree)度为0的节点称为叶子(leaf)结点或者终端结点度不为0的节点成为非终端结点分支结点。下面这个树的度就是3.

数据结构学习笔记-树_第2张图片

树中随意一个结点他下面的子树称为他的孩子(child)他的孩子称它为双亲(parent)跟这个孩子同属于一个双亲并且跟这个孩子同级的就是这个孩子的兄弟结点的祖先是从根开始到这个结点途经分支上的所有结点。反之,以这个结点为根的子树中任意结点都是该根结点的子孙

数据结构学习笔记-树_第3张图片

有两个结点,他们的双亲在同意层级上,他们互为堂兄弟树中结点的最大层次成为树的深度或高度

数据结构学习笔记-树_第4张图片


树可以利用顺序存储和链式存储的各自特点进行表示。主要有三种表示法:双亲表示法、孩子表示法、孩子兄弟表示法。

双亲表示法:因为每个结点都会有双亲结点,所以定义结点,结点包括结点数据和双亲位置。使用双亲表示法结构定义如下。

#define MAXSIZE 20//定义树的最大结点数
typedef int Eletype;
typedef struct TreeNode{//树的结点结构。还可以根据实际情况定义长子、兄弟之类的属性。
    Eletype data;
    int parent;//双亲位置
}TreeNode;
typedef struct Tree{//树结构
    TreeNode nodes[MAXSIZE];
    int r;//根节点
    int s;//结点数
}Tree;

孩子表示法:就是将每个结点(相当于头指针)的孩子排列起来,使用单链表作为存储结构,如果是叶子结点,那么他的单链表就是空。再将这些头指针组成顺序存储的线性表,存放进一维数组中。因为孩子表示法存在缺陷,通常使用双亲孩子表示法。如图所示:

数据结构学习笔记-树_第5张图片

使用孩子表示法结构定义如下:

//因为需要使用单链表和顺序存储分别表示孩子结点和指向孩子结点的头指针,所以需要定义两个存储结构
#define MAXSIZE 100//树的最大结点数
//定义孩子结点,因为最终是使用数组进行存储的,所以孩子结点的数据域存放的是数组下标
typedef struct CTNode{//孩子结点
    int child;//用来存放某孩子个结点在数组中的下标
    strut *next;//指向下一个孩子结点
}CTNode;
typedef CTNode *CTNodeP;//定义孩子结点的头指针
typedef struct firstChild{//表头结构,也是树结构中的具体属性
    int data;//结点数据
    CTNodeP firstchild;//该结点的孩子链表
    int parent;//该结点的双亲在数组中位置
}firstChild;
typedef struct Tree{//树结构
    firstChild nodes[MAXSIZE];
    int r,s;//定义根结点和结点数
}Tree;

孩子兄弟表示法:对于任意一棵树,他的结点的第一个孩子如果存在,那么firstchild就是唯一的。同理,如果这个结点存在右兄弟,那么这个右兄弟rightbrother也是唯一的。到此为止,树的结点需要指定两个指针,分别指向firstchild和rightbrother。结构代码如下:

typedef int TEletype;
typedef struct TNode{
    TEletype data;//树结点存放的数据
    struct TNode *firstchild;//存放他的第一个孩子的内存地址
    struct TNode *rightchild;//存放他的右兄弟的内存地址
}TNode,*TP;

这样一来,一棵二叉树就成形了。左边存放孩子、右边存放兄弟。

二叉树的结点度最多只能是2.二叉树可分为5种形态:

1、空二叉树

2、只有一个根结点的二叉树

3、根结点只有左子树(又叫左斜树)

4、根结点只有右子树(又叫右斜树)

5、根结点有左子树和右子树

满二叉树:一棵二叉树中如果他的所有结点都存在左结点和右结点,并且叶子结点都在同一层上,则称之为满二叉树。

完全二叉树:完全二叉树的结点排序一定是连续的。叶子结点只存在于最后两层、最后一层的最后一个结点必定是左结点、倒数第二层如果存在叶子结点,那么这层的最后一个叶子结点一定是右结点、同样结点数的两颗二叉树,完全二叉树的深度最小。(*满二叉树一定是一颗完全二叉树,反之则不一定成立)

二叉树性质:

1、在二叉树的第i层上最多有2的i-1次方个结点一颗完全二叉树,他的深度是i,则他的节点数最少也是2的i-1次方

2、一颗深度为k的二叉树,他的结点最多有2的k次方-1

3、对于任意一刻二叉树,他的度为一的第一个结点序号等于他的度为2的最后一个结点加上1.


二叉树的遍历:(前序以根结点开始,以右子树的最右边的叶子结点结束。中序以左子树的最左边叶子开始,以右子树的最右边叶子结束。后序以左子树的最下边叶子开始,以根结点结束

前序遍历:以根节点为起点,从左至右先遍历左子树,再从右子树的第一个结点开始遍历右子树。

数据结构学习笔记-树_第6张图片

中序遍历:以二叉树的左子树的最左边的结点为起点,如果这个结点存在右兄弟,如果这个结点存在孩子就去遍历孩子结点,如果没有,就先遍历该结点的双亲,再通过双亲遍历兄弟,如果没有兄弟,就向上遍历双亲。当遍历到该二叉树的根结点时,再遍历右子树的最左边的结点,如果这个结点存在孩子就去遍历孩子结点,如果没有,就像上面遍历左子树一样遍历即可。

数据结构学习笔记-树_第7张图片

后序遍历:以二叉树的左子树的最下边的叶子开始,如果有兄弟就先遍历兄弟,没兄弟就先遍历双亲。最后遍历根结点。

数据结构学习笔记-树_第8张图片

还有层序遍历:一棵二叉树,从左至右从上至下逐层遍历。

以下是二叉链表的结构定义:

#define int TEletype;
typedef struct TNode{
    TEletype data;
    struct TNode *leftChild;
    struct TNode *rightChild;
}TNode,*TNodeP;

二叉树的前序遍历需要使用递归,代码如下:

void bianli(TNodeP tnode){
    if(tnode==NULL){
       return ;
    }else{
       printf("%d",tnode->data);
       bianli(tnode->leftChild);
       bianli(tnode->rightChild);
    }
}

二叉树的中序遍历也需要递归,代码如下:

void bianli(TNodeP tnode){
    if(tnode==NULL){
       return ;
    }else{
       bianli(tnode->leftChild);
       printf("%d",tnode->data);
       bianli(tnode->rightChild);
    }
}

二叉树的后序遍历同样是递归,代码如下:

void bianli(TNodeP tnode){
    if(tnode==NULL){
       return ;
    }else{
       bianli(tnode->leftChild);
       bianli(tnode->rightChild);
       printf("%d",tnode->data);
    }
}

像上面这样定义的二叉树结构的叶子结点会出现空指针域的情况,线索二叉树就是将结点的前驱和后继存放进这些空的指针域。这些存放前驱和后继的指针域就成为线索。加上线索的二叉链表就称为二叉链表。线索化的过程就是在遍历的过程中修改空指针的过程。


普通树和二叉树之间也可以相互转换:

1、将普通树的兄弟之间连线。

2、再将它和第一个孩子之外的所有连线去除。(之前的连线不算)

3、将层次适当调整。结点的第一个孩子作为结点的左孩子,兄弟转换过来的孩子作为这个左孩子的右孩子

数据结构学习笔记-树_第9张图片

森林转换二叉树:

先将森林中的每棵树转换成二叉树,然后第一棵树不动,从第二颗树开始,依次将根结点作为上一棵树的根结点的右孩子

二叉树转换为树,就是将上面步骤反过来坐。

1、加线,若某结点的左孩子存在,就将这个左孩子的右孩子结点、右孩子的右孩子结点。。。。。。都作为此结点的孩子。

2、去线,删除二叉树中所有结点和右孩子结点的连线

3、调整层次

数据结构学习笔记-树_第10张图片


树和森林是不存在后序遍历的,树和森林的前序遍历和中序遍历分别对应二叉树的前序遍历和中序遍历。

关于赫夫曼树的定义:从一棵树上的某一结点到另一结点之间的分支(连的线)数目叫做路径长度。树的路径长度就是从树根到每一个结点的路径长度的和。‘权’指的就是对叶子结点赋予一个有意义的值。(简单来说就是叶子上的数字)树的带权路径长度就是根结点到每一个叶子结点的路径长度乘以对应的‘权’,再把这些得到的乘积相加,就称为树的带权路径长度。带权路径WPL长度最小的二叉树就称为赫夫曼树

赫夫曼树的算法描述:给一棵带权的二叉树,想要将它变成赫夫曼树,先选取两个最小的权值作为二叉树的左右孩子,他俩的根结点为俩孩子的权值的和,再用这个跟结点替换掉这两个最小的权值,在选取两个最小的权值。。。循环此操作,直到只剩下最后一颗树,这棵树就是赫夫曼树




你可能感兴趣的:(数据结构)