树:二叉树

之前写了一些链表和排序的blog,其中有说到多链表,堆,其中提到了一种特殊的数据结构:树。

人们发明树结构,用于储存和搜索海量的数据。

树的种类

无序树:树中任意节点的子结点之间没有顺序关系,这种树称为无序树,也称为自由树;

有序树:树中任意节点的子结点之间有顺序关系,这种树称为有序树;

二叉树:每个节点最多含有两个子树的树称为二叉树;

完全二叉树:二叉树的所有子树要么没有孩子,要么一定有左孩子。堆是一种完全二叉树。

红黑树:红黑树是把树中的结点定义为红、黑两种颜色,并通过规则确保从根结点到叶结点的最长路径的长度不超过最短路径的两倍。在c++的STL中,set、multiset、map、multimap等数据结构都是基于红黑树实现的。

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点(最后一层上的无子结点的结点为叶子结点)。

霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树;

如果二叉树不是平衡的,那么它就退化为一个链表,而搜索时间也退化为一个线性函数。

树的深度
定义一棵树的根结点层次为1,其他节点的层次是其父结点层次加1。一棵树中所有结点的层次的最大值称为这棵树的深度。

树的遍历
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。

遍历命名
根据访问结点操作发生位置命名:
① NLR:前序遍历(PreorderTraversal亦称(先序遍历))
——访问根结点的操作发生在遍历其左右子树之前。如10、6、4、8、14、12、16。
② LNR:中序遍历(InorderTraversal)
——访问根结点的操作发生在遍历其左右子树之中(间)。如4、6、8、10、12、14、16。
③ LRN:后序遍历(PostorderTraversal)
——访问根结点的操作发生在遍历其左右子树之后。如4、8、6、12、16、14、10。

?

中序遍历的投影法
?

一般二叉树的定义:

struct BiTNode{
	TElemType data;
	BiTNode *lchild,*rchild;
};

二叉树的遍历操作:

1.先序遍历(DLR)
先序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1) 访问根结点;
(2) 先序遍历根结点的左子树;
(3) 先序遍历根结点的右子树。

先序遍历二叉树的递归算法如下:

void PreOrder(BiTree bt) /*先序遍历二叉树bt*/
{
      if (bt==NULL) return; /*递归调用的结束条件*/
      Visite(bt->data);     /*访问结点的数据域*/
      PreOrder(bt->lchild); /*先序递归遍历bt 的左子树*/
      PreOrder(bt->rchild); /*先序递归遍历bt 的右子树*/
}

2.中序遍历(LDR)

中序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1)中序遍历根结点的左子树;
(2)访问根结点;
(3)中序遍历根结点的右子树。

中序遍历二叉树的递归算法如下:

void InOrder(BiTree bt) /*中序遍历二叉树bt*/
{
     if (bt==NULL) return; /*递归调用的结束条件*/
     InOrder(bt->lchild); /*中序递归遍历bt 的左子树*/
     Visite(bt->data);     /*访问结点的数据域*/
     InOrder(bt->rchild); /*中序递归遍历bt 的右子树*/
}

3.后序遍历(LRD)
后序遍历的递归过程为:若二叉树为空,遍历结束。否则,
(1)后序遍历根结点的左子树;
(2)后序遍历根结点的右子树。
(3)访问根结点;

后序遍历二叉树的递归算法如下:

void PostOrder(BiTree bt) /*后序遍历二叉树bt*/
{
     if (bt==NULL) return; /*递归调用的结束条件*/
     PostOrder(bt->lchild); /*后序递归遍历bt 的左子树*/
     PostOrder(bt->rchild); /*后序递归遍历bt 的右子树*/
     Visite(bt->data); /*访问结点的数据域*/
}

从表面上看,从代码中,遍历语句的位置可以看出是什么遍历。

4.层次遍历(有些面试题会叫宽度优先遍历)

由层次遍历的定义可以推知,在进行层次遍历时,对一层结点访问完后,再按照它们的访问次序对各个结点的左孩子和右孩子顺序访问,这样一层一层进行,先遇到的结点先访问,这与队列的操作原则比较吻合。因此,在进行层次遍历时,可设置一个队列结构,遍历从二叉树的根结点开始,首先将根结点指针入队列,然后从对头取出一个元素,每取一个元素,执行下面两个操作:
(1) 访问该元素所指结点;
(2) 若该元素所指结点的左、右孩子结点非空,则将该元素所指结点的左孩子指针和右孩子指针顺序入队。
此过程不断进行,当队列为空时,二叉树的层次遍历结束。

在下面的层次遍历算法中,二叉树以二叉链表存放,一维数组Queue[MAXNODE]用以实现队列,变量front 和rear 分别表示当前对首元素和队尾元素在数组中的位置。

void LevelOrder(BiTree bt) /*层次遍历二叉树bt*/
{
    BiTree Queue[MAXNODE];
    int front,rear;

    if (bt==NULL)
        return;

    front=-1;
    rear=0;
    queue[rear]=bt;

    while(front!=rear)
    {
        front++;
        Visite(queue[front]->data); /*访问队首结点的数据域*/

        if (queue[front]->lchild!=NULL) /*将队首结点的左孩子结点入队列*/
        {
            rear++;
            queue[rear]=queue[front]->lchild;
        }

        if (queue[front]->rchild!=NULL) /*将队首结点的右孩子结点入队列*/
        {
            rear++;
            queue[rear]=queue[front]->rchild;
        }
    }
}

二叉树遍历的非递归实现

从二叉树各种遍历来说,各种遍历都是从根结点开始的,且在遍历过程中经过结点的路线是一样的,只是访问的时机不同而已。这一路线都是从根结点开始沿左子树深入下去,当深入到最左端,无法再深入
下去时,则返回,再逐一进入刚才深入时遇到结点的右子树,再进行如此的深入和返回,直到最后从根结点的右子树返回到根结点为止。先序遍历是在深入时遇到结点就访问,中序遍历是在从左子树返回时遇到结点访问,后序遍历是在从右子树返回时遇到结点访问。

这种路线可以用栈来实现。其实递归在本质上就是一个栈结构。

在这一过程中,返回结点的顺序与深入结点的顺序相反,即后深入先返回,正好符合栈结构后进先出的点。因此,可以用栈来帮助实现这一遍历路线。其过程如下。在沿左子树深入时,深入一个结点入栈一个结点,若为先序遍历,则在入栈之前访问之;当沿左分支深入不下去时,则返回,即从堆栈中弹出前面压入的结点,若为中序遍历,则此时访问该结点,然后从该结点的右子树继续深入;若为后序遍历,则将此结点再次入栈,然后从该结点的右子树继续深入,与前面类同,仍为深入一个结点入栈一个结点,深入不下去再返回,直到第二次从栈里弹出该结点,才访问之。

1. 先序遍历的非递归实现

在下面算法中,二叉树以二叉链表存放,一维数组 stack[MAXNODE] 用以实现栈,变量top 用来表示当前栈顶的位置。

void NRPreOrder(BiTree bt) /*非递归先序遍历二叉树*/
{
    BiTree stack[MAXNODE],p;
    int top;

    if (bt==NULL)
        return;

    top=0;
    p=bt;

    while(!(p==NULL&&top==0))
    {
        while(p!=NULL)
        {
            Visite(p->data);   /*访问结点的数据域*/

            if (top<MAXNODE-1) /*将当前指针p 压栈*/
            {
                stack[top]=p;
                top++;
            }
            else
            {
                printf("栈溢出");
                return;
            }

            p=p->lchild; /*指针指向p 的左孩子*/

        }

        if (top<=0)
            return; /*栈空时结束*/
        else
        {
            top--;
            p=stack[top]; /*从栈中弹出栈顶元素*/
            p=p->rchild; /*指针指向p 的右孩子结点*/
        }
    }
}

2. 中序遍历的非递归实现
中序遍历的非递归算法的实现,只需将先序遍历的非递归算法中的 Visite(p->data) 移到 p=stack[top] 和 p=p->rchild 之间即可。

3. 后序遍历的非递归实现
由前面的讨论可知,后序遍历与先序遍历和中序遍历不同,在后序遍历过程中,结点在第一次出栈后,还需再次入栈,也就是说,结点要入两次栈,出两次栈,而访问结点是在第二次出栈时访问。因此,为了区别同一个结点指针的两次出栈,设置一标志flag,令:
flag = 1 -> 第一次出栈,结点不能访问
flag = 2 -> 第二次出栈,结点可以访问
当结点指针进、出栈时,其标志flag 也同时进、出栈。因此,可将栈中元素的数据类型定义为指针和标志flag 合并的结构体类型。定义如下:

typedef struct
{
    BiTree link;
    int flag;
} stacktype;

后序遍历二叉树的非递归算法如下。在算法中,一维数组 stack[MAXNODE] 用于实现栈的结构,指针变量p 指向当前要处理的结点,整型变量top 用来表示当前栈顶的位置,整型变量 sign 为结点 p 的标志量。

void NRPostOrder(BiTree bt) /*非递归后序遍历二叉树bt*/
{
    stacktype stack[MAXNODE];
    BiTree p;
    int top,sign;

    if (bt==NULL)
        return;

    top=-1   /*栈顶位置初始化*/
    p=bt;

    while (!(p==NULL && top==-1))
    {
        if (p!=NULL)    /*结点第一次进栈*/
        {
            top++;
            stack[top].link=p;
            stack[top].flag=1;
            p=p->lchild;   /*找该结点的左孩子*/
        }
        else
        {
            p=stack[top].link;
            sign=stack[top].flag;
            top--;

            if (sign==1)   /*结点第二次进栈*/
            {
                top++;
                stack[top].link=p;
                stack[top].flag=2;   /*标记第二次出栈*/
                p=p->rchild;
            }
            else
            {
                Visite(p->data);   /*访问该结点数据域值*/
                p=NULL;
            }
        }
    }
}

原文地址:https://www.cnblogs.com/losophy/p/9520868.html

时间: 2024-10-23 12:34:36

树:二叉树的相关文章

纸上谈兵: 树, 二叉树, 二叉搜索树

树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1,8,7是3的子节点, 3是1,8,7的父节点.树有一个没有父节点的节点,称为根节点(

树——二叉树的线索化

1,什么是线索化二叉树? 1,将二叉树转换为双向链表的过程(非线性 ==> 线性): 1,实际工程开发中,很大一部分情况下,二叉树一旦建立之后,就不会轻易改动,一般的用于遍历,并且这种操作一般执行很多: 2,先中后序遍历都是递归完成的,实际工程开发中,对一棵已经建立的二叉树反复执行先序遍历,效率低,所以不推荐反复的递归的遍历: 3,直接将遍历后的结果保存下来,下一次遍历直接用这个结果就可以: 4,工程开发中还有一种常见情况,就是要反复的知道某个结点在中序遍历下,前驱结点是谁.后继结点是谁,需要这

树——二叉树的先序、中序和后序遍历

1,二叉树是否只有一种遍历方式(层次遍历)? 2,典型的二叉树的遍历方式: 1,先序遍历(Pre-Order Traversal): 2,中序遍历(In-Order Traversal): 3,后序遍历(Post-Order Traversal): 3,先序遍历(“先序”指最先访问根结点中的数据元素): 1,二叉树为空: 1,无操作,直接返回: 2,二叉树不为空: 1,访问根结点中的数据元素: 2,先序遍历左子树: 3,先序遍历右子树: 4,先序遍历功能定义及其代码实现: 1,代码示例: 1 p

树 - 二叉树

读了Robert Sedgewick的<算法:C语言实现>(第三版)的第五章,了解了许多关于树,特别是二叉树的知识.这里总结一下.直接看代码(C++)吧. 1 #include <cstdio> 2 #include <cstdlib> 3 #include <queue> 4 #include <stack> 5 6 #define null 0 7 #define NEXTLINE printf("\n") 8 #defi

树&amp;二叉树

(哈弗曼树.哈弗曼编码.排序二叉树.平衡二叉树.红黑树.3种遍历(先序,后序,中序).深度-广度优先遍历) 关键词.语句: 树的三种存储结构:父节点表示法.子节点表示法.链表存储 树代表一种非线性的数据结构 如果一组数组节点之间存在复杂的一对多关联时,程序就可以考虑使用树来保存这组数据了 一对多?例如,一个父节点可以包含多个子节点 TreeMap即利用红黑树实现 树:一个集合(里面存在N个有父子关系的节点,N有限,即有限集合) 满足条件(请背诵下来): a.  当N=0的时候,节点集合为空,称为

树 二叉树 森林 之间转换

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

数据结构与算法-----&gt;数据结构-----&gt;树-------&gt;二叉树的遍历

二叉树的遍历 第一部分 基本概念以及编程实现 概述: 遍历树,就是指按照一定的顺序访问树中的所有节点. 遍历树有三种常用方法,分别是中序遍历(inorder).前序遍历(preorder).后序遍历(postorder) 三种遍历方法的三个步骤都是相同的,只不过这三个步骤的执行顺序不同.三种遍历方式的名称的由来是根据""访问节点内容""这个步骤的执行时间来定的,这个步骤在第一步执行的是前序遍历,在第二步执行的是中序遍历,在第三步执行的是后序遍历. 1.1中序遍历(i

数据结构学习笔记04树(二叉树、二叉搜索树、平衡二叉树)

一.树 树的基本术语 ①结点的度(Degree):结点的子树个数 ②树的度:树的所有结点中最大的度数 ③叶结点(Leaf):度为0的结点 ④父结点(Parent):有子树的结点是其子树的根结点的父结点 ⑤子结点(Child):若A结点是B结点的父结点,则称B结点是A结点的子结点:子结点也称孩子结点. ⑥兄弟结点(Sibling):具有同一父结点的各结点彼此是兄弟结点. ⑦路径和路径长度:从结点n1到nk的路径为一个结点序列n1 , n2 ,… , nk , ni是 ni+1的父结点.路径所包含边

树 二叉树 多叉树

本文先介绍了树的概念,然后给出了二叉树和多叉树的实现源码实例. 一.树的概念 树(本质上就使用了递归来定义的,递归就是堆栈应用,因此树离不开递归和堆栈):树是n个点的有限结合.n=0时是空树,n=1时有且仅有一个结点叫做根,n>1,其余的结点被分成m个互不相交的子集,每一个子集又是一棵树. 森林 二叉树 满二叉树 深度为k,结点个数是2的k次方-1的二叉树. 完全二叉树 深度为k,结点为n,当且仅当每一个结点的编号与对应的满二叉树完全一致. 顺序存储:访问方便 链式存储:删除方便 二叉排序树:对

树, 二叉树, 二叉搜索树

转载:Vamei   出处:http://www.cnblogs.com/vamei 树的特征和定义 树(Tree)是元素的集合.我们先以比较直观的方式介绍树.下面的数据结构是一个树: 树有多个节点(node),用以储存元素.某些节点之间存在一定的关系,用连线表示,连线称为边(edge).边的上端节点称为父节点,下端称为子节点.树像是一个不断分叉的树根. 每个节点可以有多个子节点(children),而该节点是相应子节点的父节点(parent).比如说,3,5是6的子节点,6是3,5的父节点:1