【数据结构学习笔记】二叉树

基础定义

一个树t是一个非空的有限元素的集合,其中一个元素为(root),其余的元素(如果有的话)组成t的子树(subtree)

树的另一常用术语为级(level)。树根是1级,其孩子(如果有)是2级,孩子的孩子是3级,等等。

一棵树的高度(height)或深度(depth)是树中级的个数

一个元素的度(degree of an element)是指其孩子的个数。

一棵的度是其元素的度的最大值

二叉树

定义

定义:一棵二叉树(binary tree)t是有限个元素的集合(可以为空)。当二叉树非空时,其中有一个元素称为,余下的元素(如果有的话)被划分为两颗二叉树,分别称为t的左子树和右子树

特点:

1)二叉树的每个元素都恰好有两棵子树(其中一个或两个可能为空)

2)在二叉树中,每个元素的子树都是有序的,也就是有左子树和右子树之分

3)二叉树可以为空,而树不能为空

特性

1)一棵二叉树有n个元素,n>0,它有n-1条边

2)一棵二叉树的高度为h,h>=0,它最少有h个元素,最多有2^h-1个元素

3)一棵二叉树有n个元素,n>0,它的高度最大为n,最小高度为log2(n+1)向上取整

当高度为h的二叉树恰好有2^h-1个元素时,称其为满二叉树(full binary tree)

假设从满二叉树中删除k个其编号为2^h-i(最后一层)元素,1<=i<=k<2^h,所得到的二叉树被称为完全二叉树(complete binary tree)

设完全二叉树的一元素编号为i,1<=i<=n,有以下关系:

1)如果i=1,则该元素是二叉树的根。若i>1,则其父节点的编号为i/2向下取整

2)如果2i>n,则该元素无左孩子。否则,其左孩子的编号为2i

3)如果2i+1>n,则该元素无右孩子。否则,其右孩子的编号为2i+1

二叉树的链表描述

每个元素用一个节点表示,节点有两个指针域,分别称为leftChile和rightChile。除此之外,还有一个element域。

template<class T>
struct binaryTreeNode
{
    T element;
    binaryTreeNode<T> *leftChild;//左子树
    binaryTreeNode<T> *rightChild;//右子树

    binaryTreeNode() {leftChild=rightChile=NULL;}
    binaryTreeNode(const T& theElement)
    {
        element(theElement);
        leftChild=rightChild=NULL;
    }
    binaryTreeNode(const T& theElement,binaryTreeNode *theLeftChild,binaryTreeNode *theRightChild)
    {
        element(theElement);
        leftChild=theLeftChile;
        rightChild=theRightChild;
    }
};

二叉树的遍历

前序遍历(先根遍历)

顾名思义,先访问根节点,然后前序遍历左子树,最后前序遍历右子树

递归程序:

template<class T>
void preOrder(binaryTreeNode<T> *t)
{
    //递归前序遍历
    if(t!=NULL)
    {
        visit(t);//访问根节点
        preOrder(t->leftChild);//前序遍历左子树
        preOrder(t->leftChild);//前序遍历右子树
    }
}

非递归程序:用栈来模拟递归过程

template<class T>
void preOrder(binaryTreeNode<T> *t)
{
    //非递归前序遍历
    stack<binaryTreeNode<T>*> s;
    binaryTreeNode<T> *p=t;
    while(!s.empty() || p!=NULL)
    {
        while(p)
        {
            s.push(p);
            visit(p);
            p=p->leftChild;
        }
        p=s.top();
        s.pop();
        p=p->rightChild;
    }
}

中序遍历(中根遍历)

顾名思义,先中序遍历左子树,然后访问根节点,最后中序遍历右子树

递归程序:

template<class T>
void inOrder(binaryTreeNode<T> *t)
{
    //递归中序遍历
    if(t!=NULL)
    {
        inOrder(t->leftChild);//中序遍历左子树
        visit(t);//访问根节点
        inOrder(t->rightChild);//中序遍历右子树
    }
}

非递归程序:

template<class T>
void inOrder(binaryTreeNode<T> *t)
{
    //非递归中序遍历
    stack<binaryTreeNode<T>*> s;
    binaryTreeNode<T> *p=t;
    while(!s.empty() || p!=NULL)
    {
        while(p)
        {
            s.push(p);
            p=p->leftChild;
        }
        p=s.top();
        visit(p);
        s.pop();
        p=p->rightChild;
    }
}

后序遍历(后根遍历)

顾名思义:先后序遍历左子树,然后后序遍历右子树,最后访问根节点

递归程序:

template<class T>
void postOrder(binaryTreeNode<T> *t)
{
    //递归后序遍历
    if(t!=NULL)
    {
        postOrder(t->leftChild);//后序遍历左子树
        postOrder(t->rightChild);//后序遍历右子树
        visit(t);//访问根节点
    }
}

非递归程序:后序遍历的非递归程序比较难,因为根节点要在最后访问,如果用栈来模拟递归,根节点会被先出栈。我们可以考虑:只有当第二次将根节点出栈时,才访问它。可以用一个辅助栈来同步计数

template<class T>
void postOrder(binaryTreeNode<T> *t)
{
    //非递归后序遍历
    stack<binaryTreeNode<T>*> s;
    stack<int> index;
    binaryTreeNode<T> *p=t;
    while(!s.empty() || p!=NULL)
    {
        while(p)
        {
            s.push(p);
            index.push(0);//计数一次
            p=p->leftChild;
        }
        if(index.top()==1)
        {
            //第二次出栈则访问根节点
            visit(s.top());
            s.pop();
            index.pop();
        }
        else
        {
            p=s.top();
            p=p->rightChild;
            index.top()=1;//计数两次
        }
    }
}

层次遍历

层次遍历是从顶层到底层,在同一层中,从左到右,依次访问树的元素。因为层次遍历需要队列而不是栈,因此很难编写递归程序。

非递归程序:

template<class T>
void levelOrder(binaryTreeNode<T> *t)
{
    //层次遍历
    arrayQueue<binaryTreeNode<T>*> q;
    while(t!=NULL)
    {
        visit(t);//访问t
        //将t的孩子插入队列
        if(t->leftChild!=NULL)
            q.push(t->leftChild);
        if(t->rightChild!=NULL)
            q.push(t->rightChild);
        //提取下一个要访问的节点
        try
        {
            t=q.front();
        }
        catch(queueEmpty)
        {
            return;
        }
        q.pop();
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值