数据结构-非线性-树-二叉树

//概述
//线性结构用于描述数据元素之间的线性关系,实际应用的数据元素之间的关系错综复杂,很难用线性关系描述,这就用到非线性结构,
//树它描述客观世界中事务之间的层次关系,一切具有层次关系的问题都可以用树来描述
//例如,家族的族谱,各种社会结构,(操作系统的文件系统中,用树表示目录结构)(编译程序中,用树表示源程序的语法结构)

#pragma mark --树的定义
//树,是有n(n>=0)个结点的有限集合,当n=0时称为空树,否则在任意非空树种
//1>必须有一个特定的成为根的结点
//2>剩下的结点被分成m>=0个互不相交集合T1,T2,...Tm,这些集合的每一个元素有事树,树T1,T2...Tm都成为根的子树
//特性.每个子树互不相交,每个结点只属于一棵树(或子树),只有一个双亲

#pragma mark --树的基本术语
//树包含若干个结点以及若干指向子树的分支
//度(degree) 结点拥有的子树成为结点的度 度为0的结点成为叶子(leaf)或者终端结点
//树的度是树中各个结点的最大数
//孩子(child) 双亲(parent) 兄弟(sibling) 祖父, 堂兄弟,子孙
//结点的层次(level)从根定义开始,跟为第一层,若某结点在C层,其子树的根在第C+1层
//深度(depth) 树中结点的最大层次
//有序树,即树的子树的位置不能互换,否则成为无序树
//森林(forest) 是有m(m>=0)颗互不相交的集合.对于树种每个结点而言,子树的集合就是森林

#pragma mark --树的基本操作
//1>初始化操作:INITATE(T).置T为空树
//2>求根函数 ROOT(T)或者ROOT(x).求树T的根或结点x所在树的根结点.若T是空或x不在任何一棵树上,函数的值为空
//3>求双亲函数 PARENT(T,x) 求树中结点x的双亲结点,若结点x是树T的根结点或结点x不在T中,则函数值为空
//4>求孩子结点的函数 Child(T,x,i);求树中结点x的第i个孩子结点,若结点x是树的叶子,或树无第i个孩子,或者结点x不在树上返回空
//5>求右兄弟函数 Right_Sibling(T,x),求树中结点x右边的兄弟,若x是树最右边的孩子,或者x不在t上返回空
//6建树函数 creatTree(x,F)生成一颗以结点x为根,以森林F为子树森林的树
//7,插入子树操作 Inser_Child(y,i,x)
//8.删除子树操作 Del_Child(x,i)
//9.遍历操作 traverse(T)//按照摸个顺序依次访问树中的各个结点,并使每个结点只被访问一次
//10清除树操作 Clear(T)将树置空

//由于树的形态很多,研究起来问题太复杂,所以研究讨论规范化的二叉树,二叉树的性质,存储结构和运算,然后给出二叉树和一般树的转换规则,这样就解决了树的存储结构和运算

//二叉树的概念
//二叉树是结点的有限集合,这个集合是空的或者只有一个根结点或者两两棵互不相交的左子树或者右子树组成
//二叉树的特点,每个结点最多有两个子树,度数<=2 ,二叉树子树有左二叉树和右二叉树之分,有次序的

#pragma mark 二叉树的5种基本形态
//1.空二叉树2.仅有根结点的二叉树3.右子树为空的二叉树,4左子树为空的二叉树5,左右均为非空的二叉树

//满二叉树,深度为K 且结点为2的k次方-1 的为满二叉树
//所有的结点从上往下,从左往右一次编号就得到了一个顺序表

//完全二叉树,是满二叉树的自己,且编号与满二叉树编号一致,只是数量不满

#pragma mark 二叉树的基本操作和树的基本操作基本一致

#pragma mark 二叉树的性质
//1>第i层 最多有 2的i次方 -1个结点(i>=1)
//2>深度为K的二叉树最多有2的k次方-1的结点(k>=1)
//3>对于任何一颗二叉树T,如果终端结点数为n0,度数为2的结点数为n2,则n0=n2+1
//4>具有n个结点的完全二叉树的深度为log2 n  +1
//5>具有n个结点的完全二叉树 n>=i>=1
//1..i=1 结点是二叉树的跟,无双亲 i>1则双亲时结点i/2
//2..2*i>n 结点i无左孩子,否则其左孩子是2*i
//3...2*i+1>n 结点无右孩子,否则其右孩子是结点2*i+1

#pragma mark 二叉树的顺序存储结构 //将编号放入一维数组
//1> 完全二叉树 1,2,3,4,5,6,7,8,9,10,11,12  tr[i]的双亲 tr[(i+1)/2-1],左右孩子tr[2i],tr[2i+1];
//2 一般二叉树  1,2,3,4,5,0,0,0,0,6,7 空间浪费

#pragma mark 二叉树的链式存储
//二叉链表(左右孩子指针+data)
//三叉链表(左右孩子+双亲+data)

#pragma mark 二叉树的遍历,
//按照一定的规则和次序走遍二叉树的所有结点,使每个结点都被访问一次,而且只被访问一次,遍历的目的,在于得到二叉树中各个结点的一种线性队列,使非线性的二叉树线性化
//1>DLR(先根序遍历)1,2,4,8,9,5,10,11,3,6,12,7
//2>LDR(中根序遍历)8,4,9,2,10,5,11,1,12,6,3,7
//3>LRD(后根序遍历)8,9,4,10,11,5,2,12,6,7,3,1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef char ElemType;

//通过第一个孩子和下一个兄弟来确定整个树
typedef struct TNode
{
    ElemType data;
    struct TNode *firstChild,*nextSibling;
}TNode,*Tree;

//初始化一个节点
Tree initTree(ElemType e){

    Tree pT;
    pT=(TNode*)malloc(sizeof(TNode));
    pT->firstChild=NULL;
    pT->nextSibling=NULL;
    pT->data=e;
    return pT;

}
//删除树
void deletTree(Tree*T){
    if (*T==NULL) {
        return;
    }
    deletTree(&(*T)->nextSibling);
    deletTree(&(*T)->firstChild);
    free(*T);
    *T=NULL;
}
//遍历树并打印
void prinTree(Tree T){
    if (NULL==T) {
        return;
    }else {
        printf("%c ",T->data);
        /*
         //如果不习惯递归,可以这样写
         Tree p = T->firstChild;
         while(p != NULL)
         {
         printTree(p);
         p = p->nextSibling;
         }
         */
        //注释掉的部分等价于如下两行:
        prinTree(T->firstChild);
        prinTree(T->nextSibling);

    }
}

//求树的高度
int treeDepth (Tree T){

    int hmax = 0;

    int subTreeDepth = 0;

    if(NULL == T)
        return 0;

    Tree p = T->firstChild;
    while(p != NULL)
    {
        subTreeDepth = treeDepth(p);
        p = p->nextSibling;
        if(subTreeDepth > hmax)
            hmax=subTreeDepth;
    }
    return hmax+1;

}
//全局变量记录找到的元素的地址
Tree result;
void locateElem(Tree T,ElemType e)
{
    if(NULL == T)
        return;
    if(T->data == e)
        result = T;
    /*
     Tree p = T->firstChild;

     while(p != NULL)
     {
     locateElem(p,e);
     p = p->nextSibling;
     }
     */
    locateElem(T->firstChild,e);
    locateElem(T->nextSibling,e);
}

//返回子节点的指针
Tree findChild(Tree T,ElemType e)
{
    result = NULL;
    locateElem(T,e);
    //如果没有找到或者该节点是叶子节点,返回空
    if(NULL == result && NULL == result->firstChild)
        return NULL;
    else
        return result->firstChild;
}

//返回兄弟节点的指针
Tree findSibling(Tree T,ElemType e)
{
    result = NULL;
    locateElem(T,e);
    //如果没有找到或者该节点没有右兄弟,返回空
    if(NULL == result && NULL == result->nextSibling)
        return NULL;
    else
        return result->nextSibling;
}

//插入子树
bool insertTree(Tree T,Tree Tadd,ElemType e)
{

    result = NULL;
    locateElem(T,e);
    if(result != NULL)
    {
        Tadd->nextSibling = result->firstChild;
        result->firstChild = Tadd;
        return true;
    }
    else
        return false;

}

//删除子树
bool deleteSubTree(Tree T,ElemType e)
{
    result = NULL;
    locateElem(T,e);
    //先判断result有什么节点

    if(result->firstChild != NULL)
    {
        deletTree(&(result->firstChild));
        return true;
    }
    else
        return false;
}
int main(int argc, const char * argv[]) {

    Tree  t=initTree(‘a‘);
    prinTree(t);
    insertTree(t, t, ‘b‘);
    prinTree(t);

    return 0;
}

二叉树


#include <stdio.h>
#include <stdlib.h>
/*
 二叉树操作(包含二叉树创建、销毁、计算深度、结点数目、先序遍历、中序遍历、后序遍历、二叉树线索化遍历)
 时间:2015-1-2 22:21
 说明:二叉树操作(包含二叉树创建、销毁、计算深度、结点数目、先序遍历、中序遍历、后序遍历、二叉树线索化遍历)
 */

/*声明一个枚举类型来表示指针域状态,Link=0表示指向左右孩子,Thread=1表示指向前驱或者后继*/
typedef enum{Link=0,Thread=1}PointerTag;

typedef struct BiTNode
{
    char data;
    struct BiTNode *lchild,*rchild;
    PointerTag LTag,RTag;//定义标志位
}BiTNode,*BiTree;

/*
 函数名称:CreateBinaryTree(BiTree *T)
 说明:创建二叉树,以输入‘#‘作为空指针标志,按先序遍历的顺序输入二叉树中各个结点
 */
void CreateBinaryTree(BiTree *T)
{
    char ch;
    scanf("%c",&ch);
    if(ch==‘#‘)
        *T=NULL;
    else
    {
        *T=(BiTree)malloc(sizeof(BiTNode));

        if(!(*T))//容错判断,申请内存空间失败时返回
            return;

        (*T)->data=ch;

        CreateBinaryTree(&((*T)->lchild));
        if((*T)->lchild)//当存在左孩子时将标志位定义为Link
            //if(((*T)->lchild)!=NULL)
            (*T)->LTag=Link;

        CreateBinaryTree(&((*T)->rchild));//当存在右孩子时将标志位定义为Link
        if((*T)->rchild)
            (*T)->RTag=Link;
    }
}

/*先序遍历二叉树*/
void PreOrderTraverse(BiTree T)
{
    if(!T)
        return;
    else
    {
        printf("%c",T->data);
        PreOrderTraverse(T->lchild);
        PreOrderTraverse(T->rchild);
    }
}

/*中序遍历二叉树*/
void InOrderTraverse(BiTree T)
{
    if(T==NULL)
        return;
    else
    {
        InOrderTraverse(T->lchild);
        printf("%c",T->data);
        InOrderTraverse(T->rchild);
    }
}

/*后序遍历二叉树*/
void PostOrderTraverse(BiTree T)
{
    if(!T)
        return;
    else
    {
        PostOrderTraverse(T->lchild);
        PostOrderTraverse(T->rchild);
        printf("%c",T->data);
    }
}
/*计算二叉树深度*/
int BinaryDepth(BiTree T)
{
    int ldepth,rdepth;

    if(!T)
        return 0;
    else
    {
        ldepth=BinaryDepth(T->lchild);
        rdepth=BinaryDepth(T->rchild);
        return (ldepth>rdepth?ldepth+1:rdepth+1);
    }
}

/*计算结点数量*/
int NodeCount(BiTree T)
{
    if(!T)
        return 0;
    else
        return (NodeCount(T->lchild)+NodeCount(T->rchild)+1);
}
/*销毁一个二叉树*/
void DestoryBinaryTree(BiTree *T)
{
    if(*T)
    {
        if((*T)->lchild)
            DestoryBinaryTree(&((*T)->lchild));
        if((*T)->rchild)
            DestoryBinaryTree(&((*T)->rchild));
        free(*T);
        *T=NULL;
    }
}

/*********线索化二叉树操作*********/

BiTree pre; /* 全局变量,始终指向刚刚访问过的结点 */
/*中序线索化*/
void InThreading(BiTree T)
{
    if(T)
    {
        InThreading(T->lchild);
        /*当没有左孩子时前驱线索*/
        if(!T->lchild)
        {
            T->LTag=Thread;
            T->lchild=pre;
        }
        /*当没有右孩子时后继线索*/
        if(!pre->rchild)
        {
            pre->RTag=Thread;
            pre->rchild=T;
        }

        pre=T;/* 保持pre指向前驱 */
        InThreading(T->rchild);
    }
}
/*中序遍历二叉树T,并将其线索化,head为头指针
 在线索二叉树中头结点lchild左孩子指向二叉树的根结点,
 rchild右孩子指向中序遍历时访问的最后一个结点;
 二叉树的中序遍历序列中第一个结点的lchild指针和最后一个孩子的rchild指向头结点
 */
void InOrderThreading(BiTree *head,BiTree T)
{
    *head=(BiTree)malloc(sizeof(BiTNode));
    if(!*head)/*如果创建失败*/
        return;
    /*初始化头结点*/
    (*head)->LTag=Link;
    (*head)->RTag=Thread;
    (*head)->rchild=*head;/*头指针回指*/

    if(!T)/*对传入第2个参数进行合法性检测,并采取相应处理,当T为空时*/
    {
        (*head)->lchild=(*head);
    }
    else
    {
        (*head)->lchild=T;//当T为非空时头结点指向二叉树的根结点T
        pre=(*head);

        InThreading(T);

        /*对最后一个结点线索化*/
        pre->RTag=Thread;
        pre->rchild=*head;
        (*head)->rchild=pre;/*头结点指向中序遍历最后一个结点*/
    }
}

/*中序遍历一棵二叉线索树T(T为头结点)*/
void InOrderTraverseThreadTree(BiTree T)
{
    BiTree p;
    p=T->lchild;

    while(p!=T)/*当还未遍历迭代完整棵二叉树,空树或遍历结束时p=T*/
    {
        while(p->LTag==Link)
        {
            p=p->lchild;
        }

        printf("%c ",p->data);

        while((p->rchild!=T)&&(p->RTag==Thread))
        {
            p=p->rchild;
            printf("%c ",p->data);
        }
        p=p->rchild;
    }

}

int main(int argc, char *argv[])
{

    BiTree T,H;
    printf("请创建一棵二叉树(如:‘ABDH##I##EJ###CF##G##‘)\n");
    CreateBinaryTree(&T);

    printf("\n二叉树的深度为:%d,结点数目为:%d\n",BinaryDepth(T),NodeCount(T));

    printf("\n先序遍历的结果是:\n");
    PreOrderTraverse(T);

    printf("\n中序遍历的结果是:\n");
    InOrderTraverse(T);

    printf("\n后序遍历的结果是:\n");
    PostOrderTraverse(T);

    printf("\n对二叉树进行中序线索化\n");
    InOrderThreading(&H,T);
    printf("\n中序遍历线索二叉树的结果是:\n");
    InOrderTraverseThreadTree(H);

    printf("\n摧毁一棵二叉树\n");
    DestoryBinaryTree(&T);
    printf("\n二叉树的深度为:%d,结点数目为:%d\n",BinaryDepth(T),NodeCount(T));
    return 0;
}

时间: 2024-11-08 02:17:22

数据结构-非线性-树-二叉树的相关文章

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

来源:http://www.cnblogs.com/coder2012/archive/2013/06/05/3102868.html 树 树形结构是一类非常重要的非线性结构,它可以很好地描述客观世界中广泛存在的具有分支关系或层次特性的对象,因此在计算机领域里有着广泛应用,如操作系统中的文件管理.编译程序中的语法结构和数据库系统信息组织形式等. 树的相关定义 节点的度:一个节点含有的子树的个数称为该节点的度: 树的度:一棵树中,最大的节点的度称为树的度: 叶节点或终端节点:度为零的节点: 非终端

javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作

树型结构是一类非常重要的非线性结构.直观地,树型结构是以分支关系定义的层次结构. 树在计算机领域中也有着广泛的应用,例如在编译程序中,用树来表示源程序的语法结构:在数据库系统中,可用树来组织信息:在分析算法的行为时,可用树来描述其执行过程等等. 下面讲解的内容完整代码在这:https://github.com/LukeLin/data-structure-with-js/blob/master/Binary%20tree/BinaryTree.js 首先看看树的一些概念: 1.树(Tree)是n

SDUT 3340 数据结构实验之二叉树一:树的同构

数据结构实验之二叉树一:树的同构 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 给定两棵树T1和T2.如果T1可以通过若干次左右孩子互换就变成T2,则我们称两棵树是"同构"的.例如图1给出的两棵树就是同构的,因为我们把其中一棵树的结点A.B.G的左右孩子互换后,就得到另外一棵树.而图2就不是同构的. 图1 图2 现给定两棵树,请你判断它们是否是同构的. Input 输入数据包含

javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径. ② 路径长度:结点路径上的分支数目称为路径长度. ③ 树的路径长度:从树根到每一个结点的路径长度之和. 以下图为例: A到F :结点路径 AEF : 路径长度(即边的数目) 2 : 树的路径长度:3*1+5*2+2*3=19: ④ 结点的带权路径长度:从该结点的到树的根结

javascript实现数据结构:线索二叉树

遍历二叉树是按一定的规则将树中的结点排列成一个线性序列,即是对非线性结构的线性化操作.如何找到遍历过程中动态得到的每个结点的直接前驱和直接后继(第一个和最后一个除外)?如何保存这些信息? 设一棵二叉树有n个结点,则有n-1条边(指针连线) , 而n个结点共有2n个指针域(Lchild和Rchild) ,显然有n+1个空闲指针域未用.则可以利用这些空闲的指针域来存放结点的直接前驱和直接后继信息. 对结点的指针域做如下规定: 1.若结点有左子树,则其leftChild域指示其左孩子,否则令leftC

【数据结构】之二叉树的java实现

二叉树的定义: 二叉树是树形结构的一个重要类型.许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要. 二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的.分别称作这个根的左子树和右子树的二叉树组成. 这个定义是递归的.由于左.右子树也是二叉树, 因此子树也可为空树.下图中展现了五种不同基本形态的二叉树. 其中 (a) 为空树, (b

数据结构快速回顾——二叉树 解幂子集问题

回溯法是设计递归的一种常用方法,它的求解过程实质上就是一个先序遍历一棵"状态树"的过程,只是这棵树不是遍历前预先建立的而是隐含在遍历过程中的. 下面举一个例子:求含n个元素的集的幂集:集合A={ {1,2,3}, {1,2}, {1,3}, {1}, {2,3},{2},{3},{}}; //{}表示空集合从集合A的每一个元素的角度看,它只有两种状态:或者是属于幂集的元素集,或不属于幂集元素集,则求幂集的过程就可以看成是依次对集合A中的元素进行"取","舍

数据结构快速回顾——二叉树

二叉树(Binary Tree)是个有限元素的集合,该集合或者为空.或者由一个称为根(root)的元素及两个不相交的.被分别称为左子树和右子树的二叉树组成.当集合为空时,称该二叉树为空二叉树.在二叉树中,一个元素也称作一个结点. 基本概念: (1)结点的度.结点所拥有的子树的个数称为该结点的度. (2)叶结点.度为0的结点称为叶结点,或者称为终端结点. (3)分枝结点.度不为0的结点称为分支结点,或者称为非终端结点.一棵树的结点除叶结点外,其余的都是分支结点. (4)左孩子.右孩子.双亲.树中一

javascript实现数据结构: 树和森林

树的3种常用链表结构 1 双亲表示法(顺序存储结构) 优点:parent(tree, x)操作可以在常量时间内实现 缺点:求结点的孩子时需要遍历整个结构 用一组连续的存储空间来存储树的结点,同时在每个结点中附加一个指示器(整数域) ,用以指示双亲结点的位置(下标值) . 图所示是一棵树及其双亲表示的存储结构.这种存储结构利用了任一结点的父结点唯一的性质.可以方便地直接找到任一结点的父结点,但求结点的子结点时需要扫描整个数组. 代码实现: 1 // 1.双亲表示法 2 // 优点:parent(t