数据结构基础 树和二叉树

本文介绍了树的基本概念,包括树的逻辑结构、性质、结点的关系以及树的遍历方法。同时,详细讲解了二叉树的定义,特征以及二叉树的遍历方式。内容涵盖树的存储结构,如双亲表示法、孩子表示法等,并探讨了二叉树的顺序存储结构和二叉链表实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树的逻辑结构

:n个结点的有限集合。
树的性质:

  • 有且仅有一个特定的结点称为 根结点
  • 当有多个结点时,除根结点外的其余结点被分成 互不相关 的有限集合,其中每个集合又是一棵树,并称为这个结点的子树。

结点的度:结点所拥有的子树的个数。
树的度:树中各结点度的最大值。
叶子结点:度为0的结点。
分支节点:度不为0的结点。
孩子结点、双亲结点、兄弟结点:某结点的子树的根结点称为该结点的孩子结点;反之,该结点称为其孩子结点的双亲结点;具有同一个双亲结点的孩子结点互称兄弟结点。
路径:从双亲结点到其子结点的序列称为路径。
路径长度:路径上经过的边数。
结点的层数:规定根结点的层数为1,对任何结点,若某结点在第k层,则其孩子结点在第k+1层。
树的深度(或高度):树中所有结点的最大层数。
层序编号:将树结点按照从上到下,从左到右的次序依次给它们编号。
有序树、无序树:如果一棵树中结点的各子树从左到右是有次序的,交换结点后,构成不同的树,称这棵树为有序树。反之为无序树。
森林:多颗互不相交的树的集合构成森林。

树的遍历
1.前序遍历
若树为空,则空操作返回;否则

  • 访问根结点;
  • 按照从左到右的顺序前序遍历根结点的每一颗子树。

2.后序遍历
若树为空,则空操作返回;否则

  • 按照从左到右的顺序后序遍历根结点的每一颗子树。
  • 访问根结点;

3.层序遍历
若树为空,则空操作返回;否则

  • 从树的第一层开始,自上而下逐层遍历,在同一层,按从左到右的顺序对结点逐个访问。

树的存储结构

1.双亲表示法
由树的定义,树中每个结点都有且仅有一个双亲结点。根据这一特性,可以用一维数组来存储树的各个结点(一般是层序存储),数组中的一个元素对应树中的一个结点。数组元素包括树中结点的数据信息和该结点的双亲在数组中的下标。实质上是一个 静态链表

但是该方法不能反映各兄弟结点之间的关系,所以在实际中,在数组中增设存放第一个孩子的域或存放右兄弟的域。

2.孩子表示法
一种基于 链表 的存储方法。

(1)多重链表表示法
由于树的每个结点都可能有多个孩子,因此链表中的每个结点都包括一个数据域和多个指针域,每个指针域指向该结点的一个孩子。由于树中各结点的度不同,因此指针域的设置有两种方法。

  • 指针域的个数等于该结点的度
data(数据域)degree(度域)child1child2childd(指针域)

数据域存放该结点的数据信息;度域存放该结点的度;指针域指向该结点的孩子结点。

  • 指针域的个数等于树的度*
data(数据域)child1child2childd(指针域)

数据域存放该结点的数据信息;指针域指向该结点的孩子结点。

(2)孩子链表表示法
一种用多个 单链表 表示数的方法,即把每个结点的孩子排列起来,看成一个线性表,且以单链表存储。n个结点共有n个孩子链表。

(3)双亲孩子表示法

data(数据域)parent(双亲结点下标)firstchild(孩子链表的头指针)

数据域存放该结点的数据信息;双亲结点下标;firstchild存储孩子链表的头指针,指向孩子链表。

(4)孩子兄弟表示法
又称二叉链表表示法。链表中的每个结点除数据域外,还设置了两个指针分别指向该结点的第一个孩子和右兄弟。

firstchild(孩子链表的头指针)data(数据域)rightsib(右兄弟结点)

二叉树

二叉树的逻辑结构

二叉树是n(n>=0)个结点的有限集合,该集合或者为空,或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树的二叉树组成。

二叉树特性

  • 每个结点最多有两棵子树;
  • 二叉树是有序的,其次序不能颠倒,即使树中的某个结点只有一颗子树,也要区分它是左子树还是右子树。
    二叉树和树是两种结构

斜树:所有结点都只有左子树的二叉树称为左斜树;所有结点都只有由子树的二叉树称为右斜树;左斜树和右斜树统称为 斜树

满二叉树:在一棵二叉树中,如果所有分支结点都存在左、右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

完全二叉树:对一颗二叉树按层序编号,如果所有结点与同样深度的满二叉树中的结点位置完全相同,则这棵树是完全二叉树。

二叉树的遍历
1.前序遍历
若二叉树为空,则空操作返回;否则

  • 访问根结点;
  • 前序遍历根结点的左子树;
  • 前序遍历根结点的右子树。

2.中序遍历
若二叉树为空,则空操作返回;否则

  • 中序遍历根结点的左子树;
  • 访问根结点;
  • 中序遍历根结点的右子树。

3.后序遍历
若二叉树为空,则空操作返回;否则

  • 后序遍历根结点的左子树;
  • 后序遍历根结点的右子树;
  • 访问根结点。

4.层序遍历

  • 从二叉树的第一层开始,按照从上到下,从左到右的顺序对结点逐个访问。

二叉树的存储结构及实现

1.顺序存储结构
完全二叉树中结点的层序遍历可以唯一地放映结点之间的父子关系,然后用一维数组存储。

2.二叉链表
令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左、右孩子的指针。如图:
在这里插入图片描述
(1)递归实现
BiTree.h

//
// Created by 野尽 on 2020/4/2.
//

#ifndef SEQLIST_CLASSTEMPLATEINHFILE__BITREE_H
#define SEQLIST_CLASSTEMPLATEINHFILE__BITREE_H

#include <iostream>
#include "SeqQueue.h"
#include <cmath>
using namespace std;

template <class DataType>
struct BiNode
{
    DataType data;
    BiNode<DataType> * lchild, * rchild;
};

template <class DataType>
class BiTree
{
public:

    // 构造函数,建立一颗二叉树
    BiTree();

    // 析构函数
    ~BiTree();

    // 前序遍历二叉树
    void PreOrder();

    // 中序遍历二叉树
    void InOrder();

    // 后序遍历二叉树
    void PostOrder();

    // 层序遍历二叉树
    void LeverOrder();

private:

    // 指向根结点头指针
    BiNode<DataType> * root;

    // 构造函数调用
    BiNode<DataType> * Creat(BiNode<DataType> * bt);

    // 析构函数调用
    void Release(BiNode<DataType> * bt);

    // 前序遍历函数调用
    void PreOrder(BiNode<DataType> * bt);

    //中序遍历函数调用
    void InOrder(BiNode<DataType> * bt);

    // 后序遍历函数调用
    void PostOrder(BiNode<DataType> * bt);
};

template<class DataType>
BiTree<DataType>::BiTree() {

    root = Creat(root);
}

template<class DataType>
BiTree<DataType>::~BiTree() {

    Release(root);
    cout<<"BiTree had been cleaned!"<<endl;
}

template<class DataType>
void BiTree<DataType>::PreOrder() {

    PreOrder(root);
}

template<class DataType>
void BiTree<DataType>::InOrder() {

    InOrder(root);
}

template<class DataType>
void BiTree<DataType>::PostOrder() {

    PostOrder(root);
}

template<class DataType>
void BiTree<DataType>::LeverOrder() {

    SeqQueue <BiNode<DataType> *> seqQueue;

    if (root == NULL)
        return;

    seqQueue.EnQueue(root);

    while (seqQueue.Empty() != 1)
    {
        BiNode<DataType> * q;
        q = seqQueue.DeQueue();
        cout<<q->data<<" ";

        if (q->lchild != NULL)
            seqQueue.EnQueue(q->lchild);
        if (q->rchild != NULL)
            seqQueue.EnQueue(q->rchild);
    }

    cout<<endl;
}

template<class DataType>
BiNode<DataType> *BiTree<DataType>::Creat(BiNode<DataType> * bt) {

    DataType ch;
    cin>>ch;

    // 假设数据为字符
    if (ch == '#')
        bt = NULL;
    else
    {
        // 注意 以下是前序遍历的步骤,建立二叉树
        bt = new BiNode<DataType>;
        bt->data = ch;
        bt->lchild = Creat(bt->lchild);
        bt->rchild = Creat(bt->rchild);
    }

    return bt;
}

template<class DataType>
void BiTree<DataType>::Release(BiNode<DataType> * bt) {

    if (bt != NULL)
    {
        Release(bt->lchild);
        Release(bt->rchild);
        delete bt;
    }
}

template<class DataType>
void BiTree<DataType>::PreOrder(BiNode<DataType> * bt) {

    if (bt == NULL)
        return ;
    else
    {
        cout<<bt->data<<" ";
        PreOrder(bt->lchild);
        PreOrder(bt->rchild);
    }
}

template<class DataType>
void BiTree<DataType>::InOrder(BiNode<DataType> * bt) {

    if (bt == NULL)
        return ;
    else
    {
        InOrder(bt->lchild);
        cout<<bt->data<<" ";
        InOrder(bt->rchild);
    }
}

template<class DataType>
void BiTree<DataType>::PostOrder(BiNode<DataType> * bt) {

    if (bt == NULL)
        return ;
    else
    {
        PostOrder(bt->lchild);
        PostOrder(bt->rchild);
        cout<<bt->data<<" ";
    }
}


#endif //SEQLIST_CLASSTEMPLATEINHFILE__BITREE_H

main.cpp

#include "BiTree.h"

int main() {
 BiTree.h
    // 注意 数据为字符
    BiTree <char> biTree;

    cout<<"层序遍历:";
    biTree.LeverOrder();
    cout<<endl;

    cout<<"前序遍历:";
    biTree.PreOrder();
    cout<<endl;

    cout<<"中序遍历:";
    biTree.InOrder();
    cout<<endl;

    cout<<"后序遍历:";
    biTree.PostOrder();
    cout<<endl;

测试结果:

此程序运行,需要先输入序列。

输入:? + a # # * b # # - c # # d # # / e # # f # #

输出:
层序遍历:? + / a * e f b - c d 
SeqQueue had been cleaned!

前序遍历:? + a * b - c d / e f 
中序遍历:a + b * c - d ? e / f 
后序遍历:a b c d - * + e f / ? 
BiTree had been cleaned!

注意:二叉树的前序、中序、后序遍历中的任何一个都不能唯一确定一颗二叉树,因此用二叉树对应的扩展二叉树。? + a # # * b # # - c # # d # # / e # # f # #,序列是扩展二叉树的前序遍历序列,能唯一确定这棵树。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值