C语言数据结构-树

来源:互联网 发布:u盘维护系统 数据恢复 编辑:程序博客网 时间:2024/05/16 01:37


树是一些节点的集合,可以是空集也可以不是,树的最开始节点为根,其它的为儿子,一个节点可以有多个儿子也可以没有。
1、结点的度
  树中的一个结点拥有的子树数称为该结点的度;一棵树的度是指该树中结点的最大度数。度为零的结点称为叶子(Leaf)或终端结点。度不为零的结点称分支结点或非
2、路径
  若树中存在一个结点序列k1,k2,…,ki,使得ki是ki+1的双亲,则称该结点序列是从kl到kj的一条路径,路径的长度指路径所经过的边(即连接两个结点的线段)的数目。
3、节点的深度
 结点的深度为根到节点路径的长,根的深度为0,其余结点的深度等于其双亲结点的深度加1。树中结点的最大深度称为树的高度(Height)或深度(Depth)。

二叉树
二叉树是一种比较特殊的数型结构,它的每个节点最多只能有两个子节点。如下图:

这里写图片描述

二叉树的存储:
这里只介绍二叉树的链式存储结构,其每个节点结构有一个数据,一个指向左子节点的指针和一个指向右子节点的指针;
具体如下图:

这里写图片描述

满二叉树
  一棵深度为k且有2k-1个结点的二又树称为满二叉树。
  满二叉树的特点:
  (1) 每一层上的结点数都达到最大值。即对给定的高度,它是具有最多结点数的二叉树。
  (2) 满二叉树中不存在度数为1的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层上。
  
完全二叉树
  若一棵二叉树至多只有最下面的两层上结点的度数可以小于2,并且最下一层上的结点都集中在该层最左边的若干位置上,则此二叉树称为完全二叉树。
  特点:
  (1) 满二叉树是完全二叉树,完全二叉树不一定是满二叉树。
  (2) 在满二叉树的最下一层上,从最右边开始连续删去若干结点后得到的二叉树仍然是一棵完全二叉树。
  (3) 在完全二叉树中,若某个结点没有左孩子,则它一定没有右孩子,即该结点必是叶结点。
  
二叉树的遍历:
  二叉树主要有三种遍历方式(由根节点遍历的顺序来决定是哪一种遍历方式,左节点始终在右节点之前):
1.中序遍历
若二叉树非空,则依次执行如下操作:
(1)遍历左子树;
(2)访问根结点;
(3)遍历右子树。

2.先序遍历
若二叉树非空,则依次执行如下操作:
(1) 访问根结点;
(2) 遍历左子树;
(3)遍历右子树。

3.后序遍历
若二叉树非空,则依次执行如下操作:
(1)遍历左子树;
(2)遍历右子树;
(3)访问根结点。
下面通过程序实现二叉树的定义和相关一些运算:
有关二叉树的各种运算基本都利用了递归函数,队二叉树进行分层运算;

#include <stdio.h>#include <stdlib.h>#define DataType int//二叉树结点构造typedef struct node{    DataType data;    struct node *lchild;    struct node *rchild;}BinNode;typedef BinNode *BinTree;//二叉树遍历(先序)void TreeTraversal1(BinTree T){    if(T)    {        printf("%d\t",T->data);  //  输出节点值        TreeTraversal1(T->lchild);      // 遍历左子节点        TreeTraversal1(T->rchild);          //遍历右子节点    }}//二叉树遍历(中序)void TreeTraversal2(BinTree T){    if(T)    {        TreeTraversal2(T->lchild);      //        printf("%d\t",T->data);       //        TreeTraversal2(T->rchild);          //    }}//二叉树遍历(后序)void TreeTraversal3(BinTree T){    if(T)    {        TreeTraversal3(T->lchild);      //        TreeTraversal3(T->rchild);          //        printf("%d\t",T->data);       //    }}//二叉树节点总数int CountNode(BinTree T){    int cnt;    if(T)    {        if(T->lchild == NULL && T->rchild == NULL)            cnt = 1;        else            cnt = CountNode(T->lchild) + CountNode(T->rchild) + 1;    }    else        cnt = 0;    return cnt;}//二叉树叶子数int CountLeaf(BinTree T){    int cnt;    if(T)    {        if(T->lchild == NULL && T->rchild == NULL)            cnt = 1;        else            cnt = CountLeaf(T->lchild) + CountLeaf(T->rchild); //与节点数唯一的区别;    }    else        cnt = 0;    return cnt;}//二叉树的高度计算(数中节点最大层数)int HightTree(BinTree T){    int hl,hr;    if(T)    {        if(T->lchild == NULL && T->rchild == NULL)            return 1;        else        {            hl = HightTree(T->lchild);            hr = HightTree(T->rchild);            if(hl>=hr)                return hl+1;            else                return hr+1;        }    }    else        return 0;}//交换二叉树左右子数void ChangeTree(BinTree T){    BinTree temp;    if(T)    {        ChangeTree(T->lchild);        ChangeTree(T->rchild);        temp = T->lchild;        T->lchild = T->rchild;        T->rchild = temp;    }}//拷贝二叉树void CopyTree(BinTree T,BinTree *Nt)     //??{    if(T)//BinNode *T1;    {        (*Nt) = (BinNode *)malloc(sizeof(BinNode)); // BinNode *        (*Nt)->data = T->data;        CopyTree(T->lchild,&(*Nt)->lchild);        CopyTree(T->rchild,&(*Nt)->rchild);    }    else        *Nt = NULL;} /*        构造一个如下简单二叉树                                                 1                                            /     \                                          2         3                                         / \      /                                          4     5   6*///测试int main(){    //构造一个简单二叉树T,如上图    BinNode T,T1,T2,T3,T4,T5;   BinTree Tc ;    T.data = 1;  T.lchild = &T1;  T.rchild = &T2;    T1.lchild = &T3;  T1.rchild = &T4;    T2.lchild = &T5;  T2.rchild = NULL;    T3.lchild = T3.rchild = NULL;    T4.lchild = T4.rchild = NULL;    T5.lchild = T5.rchild = NULL;    T1.data = 2;  T2.data = 3;  T3.data = 4;    T4.data = 5;  T5.data = 6;    //输出T的几种遍历    printf("\nAll the data of the tree(前序):\n");    TreeTraversal1(&T);    printf("\nAll the data of the tree(中序):\n");    TreeTraversal2(&T);    printf("\nAll the data of the tree(后序):\n");    TreeTraversal3(&T);    //计算二叉树各值    printf("\nThe node number of the Tree:%d\n", CountNode(&T));    printf("\nThe leaf number of the Tree:%d\n", CountLeaf(&T));    printf("\nThe hight of the Tree:%d\n", HightTree(&T));    //二叉树左右变换    ChangeTree(&T);    printf("\nAll the data of the tree(前序):\n");    TreeTraversal1(&T);    //复制一个二叉树    CopyTree(&T,&Tc);    printf("\nAll the data of the tree(前序):\n");    TreeTraversal1((Tc));    return 0;}

输出结果:

这里写图片描述

二叉查找树

二叉查找树(BinarySearch Tree,也叫二叉搜索树,或称二叉排序树Binary Sort Tree)或者是一棵空树,或者是具有下列性质的二叉树:
  (1)、若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值;
  (2)、若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值;
  (3)、它的左、右子树也分别为二叉查找树。
  
二叉查找树的平均深度为O(logN),根据二叉树的性质可知,二叉树中序遍历获得的序列是一个由小到大排列好的序列。由于每一个二叉树儿子又可以当作一个二叉树,因此针对二叉树的操作具有递归性,

二叉树的插入:插入操作比较简单,根据待插入的值按照二叉树遍历(小于在左边,大于在右边),找到遍历路径的末端,进行插入即可。
二叉树的删除:删除操作较麻烦,如果删除的是叶子节点,那么就直接删除;如果删除的节点有一个儿子,那么将儿子的值赋给父亲,删除儿子节点;如果节点有两个儿子,找到该节点右侧节点中所有节点中值最小的那个,将该节点的值作为待删除节点的值,然后再删除保存最小值的那个节点,这里一般都需要递归运算。
具体实现代码参考《数据结构与算法分析C++描述》。

AVL树

  AVL树是带有平衡条件的二叉查找树,在AVL树中任何节点的两个子树的高度最大差别为一,树的深度为O(logN),所以它也被称为高度平衡树,查找、插入和删除在平均和最坏情况下都是O(logN),这里的N是树中元素的数目。
  
AVL树本质上还是一棵二叉搜索树,它的特点是:
 1.本身首先是一棵二叉搜索树。
 2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

AVL树必须保持其结构特征,当进行数据插入或删除时,若改变了树的结构,使某个节点左右子树的高度差等于2,那么需要对树进行旋转来使之达到平衡条件,根据不同的情况,分为单旋转和双旋转两种,这一的操作一遍是通过递归来完成的。

伸展树

  伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。
  在伸展树上的一般操作都基于伸展操作:假设想要对一个二叉查找树执行一系列的查找操作,为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。伸展树应运而生。伸展树是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。它的优势在于不需要记录用于平衡树的冗余信息,最显著的缺点是它有可能会变成一条链。

红黑树

  红黑树(Red Black Tree) 是一种自平衡二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。一棵含有n个节点的红黑树的高度至多为2lg(n+1)。
  
红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色具体有如下5个性质:
  性质1. 节点是红色或黑色。
  性质2. 根节点是黑色。
  性质3 每个叶节点(NIL节点,空节点)是黑色的。
  性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
  这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。同AVL树一样,红黑树进行数据插入和删除过程中也是需要进行旋转操作来保持结构特性的。
  详细参考:红黑树(一)之 原理和算法详细介绍

B树

  B树是一种多叉查找树,多用于数据库和文件系统,当数据在磁盘存储时访问是十分费时的,为了减少查询数据的时间,可以降低数据的查找深度,从而减少查询次数,本质上它是一种一个节点可以拥有多于2个子节点的二叉查找树。
  M阶B树具有如下性质:
  1、根节点至少有两个子节点
  2、每个节点有M-1个key,并且以升序排列(j个孩子的节点有j-1个key)
  3、位于M-1和M key的子节点的值位于M-1 和M key对应的Value之间
  4、其它节点至少有[ceil(M / 2)]个子节点,最多有M个子节点
  5、所有叶子结点都出现在同一层
  
  B和B+树的区别在于,B+树的非叶子结点只包含导航信息,不包含实际的值,所有的叶子结点和相连的节点使用链表相连,便于区间查找和遍历。
  由于B+树在内部节点上不好含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子几点上关联的数据也具有更好的缓存命中率。
  B+树的叶子结点都是相链的,因此对整棵树的便利只需要一次线性遍历叶子结点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
  B树优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。
  参考:
  浅谈算法和数据结构: 十 平衡查找树之B树
  MySQL索引背后的数据结构及算法原理
  从B树、B+树、B*树谈到R 树

0 0
原创粉丝点击