二叉树

基本概念

  • 二叉树(binary tree):结点的有限集合,一般为根结点+左右子树
  • 根结点(root)
  • 子树subtree
  • 父结点parent
  • 子结点child
  • 兄弟结点sibling
  • 结点的度degree):结点的子树的个数
  • 叶结点(leaf ):度为0的结点
  • 内部结点internal node),除叶结点以外的非终端结点
  • (edge):有向线段 <k,k> <script type="math/tex" id="MathJax-Element-1"> </script>
  • 路径path
  • 路径长度length
  • 祖先ancestor
  • 子孙descendant
  • 结点的层数level):根结点为0层
  • 满二叉树full binary tree):除了叶子都满,或结点的度为0或2
  • 完全二叉树complete binary tree):宽搜时叶子都在最后
  • 扩充二叉树extended binary tree):在所有度数为0和1的结点后增加空树叶,一定是满二叉树。新增的空叶子结点(外部结点)个数是原来结点数+1
  • 外部长度路径 E :从扩充的二叉树的根到每个外部结点(新增叶子个数)的路径长度之和
  • 内部路径长度 I:从扩充的二叉树的根到每个内部结点(原来结点个数)的路径长度之和
    E+B=2n

主要性质

  • i 层上最多有2i个结点
  • 深度(depth)为 k 的二叉树至多有 2k+11个结点
  • 其终端结点数为 n0 ,度为 2 的结点数为n2,则 n0=n2+1
  • 非空满二叉树树叶数目等于其分支结点数加1。
  • 一个非空二叉树的空子树数目等于其结点数加1。
  • n 个结点(n>0)的完全二叉树的高度为 [log2(n+1)] (深度为 [log2(n+1)]1 )。 其中二叉树的高度(height )定义为二叉树中层数最大的叶结点的层数加1。

抽象数据类型

结点

template<class T>
class BinaryTreeNode{
friend class BinaryTree<T>;  //把BinaryTree设为友元类

private:
    T Value;

public:
    BinaryTreeNode();
    BinaryTreeNode(const T& val);
    BinaryTreeNode(const T& val, BinaryTreeNode<T>* left, BinaryTreeNode<T>* right);

    T getValue();
    BinaryTreeNode<T>* getLeftChild();
    BinaryTreeNode<T>* getRightChild();
    void setValue(const T& val);
    void setLeftChild(BianryTreeNode<T>* left);
    void setRightChild(BinaryTreeNode<T>* right);

    bool isLeaf() const;  //why const?
    BinaryTreeNode<T>& operator=(BinaryTreeNode<T>& node);
};

二叉树

template<class T>
class BinaryTree{
private:
    BinaryTreeNode<T>* root;

public:
    BinaryTree(){root = NULL};
    ~BinaryTree(){DeleteBinaryTree();};

    bool isEmpty() const;
    BinaryTreeNode<T>* getRoot(return root;);
    void setRoot(BinaryTreeNode<T>*);

    BinaryTreeNode<T>* Parent(BinaryTreeNode<T>* current);
    BinaryTreeNode<T>* LeftSibling(BinaryTreeNode<T>* current);
    BinaryTreeNode<T>* RightSibling(BinaryTreeNode<T>* current);

    void PreOrder(BinaryTreeNode<T>* root);
    void InOrder(BinaryTreeNode<T>* root);
    void PostOrder(BinaryTreeNode<T>* root);
    void LevelOrder(BinaryTreeNode<T>* root);
    void DeleteBinaryTree(BinaryTreeNode<T>* root);
};

遍历

  • 遍历,也叫周游(traversal),按一定顺序访问所有结点一遍,实际上就是把二叉树线性化的过程。
  • 分为深度优先广度优先,深度优先分为前序、中序、后序三种。

深度优先遍历

  • 递归方法
  • 分为前序、中序、后序三种,前中后说的是访问根节点的时刻。都采用
  • 递归定义。
    • 前序(tLR次序,preorder traversal)先根结点、再左、再右;
    • 中序(LtR次序,inorder traversal)先左结点、再根、再右;
    • 后序(LRt次序,posorder traversal)先左结点、再右、再根;

二叉树的遍历

代码

//前序
template<T>
void BinaryTree<T>::PreOrder(BinaryTreeNode<T>* root)
{
    if(root->LeftChild() != NULL)
    {
        Visit(root);
        PreOrder(root->getLeftChild());
        PreOrder(root->getRightChild());
    }
}
//中序
template<T>
void BinaryTree<T>::InOrder(BinaryTreeNode<T>* root)
{
    if(root->LeftChild() != NULL)
    {
        InOrder(root->getLeftChild());
        Visit(root);
        InOrder(root->getRightChild());
    }
}
//后序
template<T>
void BinaryTree<T>::PostOrder(BinaryTreeNode<T>* root)
{
    if(root->LeftChild() != NULL)
    {
        PostOrder(root->getLeftChild());
        PostOrder(root->getRightChild());
        Visit(root);
    }
}

非递归方法

  • 有时程序不允许递归(when?),所以要用非递归方法
  • 用栈
  • 举例:非递归前序周游
  • 每遇到一个结点,先访问该结点,将非空右子结点推入栈,周游左子树;
  • 周游不下去时就出栈;
  • 一开始推入一个空指针(监视哨),当这个空指针出栈时程序结束。
//非递归前序周游
template<T>
void BinaryTree<T>::PreOrderWithoutRecursion(BinaryTreeNode<T>* root)
{
    using std::stack;
    stack<BinaryTreeNode<T>*> S;
    S.push(NULL);
    if(root == NULL)
        return;
    S.push(root);
    while(S.front() != NULL)
    {
        BinaryTreeNode<T>* node = S.top();
        S.pop();
        Visit(node);
        if(node->getRightChild() != NULL)
            S.push(node->getRightChild());
        if(node->getLeftChild() != NULL)
            S.push(node->getLeftChild());
    }
}
//非递归中序周游
template<T>
void BinaryTree<T>::InOrderWithoutRecursion(BinaryTreeNode<T>* root)
{
    using std::stack;
    stack<BinaryTreeNode<T>*> S;
    BinaryTreeNode<T>* pointer = root;
    while(!S.empty() || pointer)
    {
        if(pointer)
        {
            S.push(pointer);
            pointer = pointer->getLeftChild();
        }
        else
        {
            BinaryTreeNode<T>* node = S.top();
            S.pop();
            Visit(node);
            pointer = node->getRightChild();
        }           
    }
}
//非递归后序周游
template<T>
void BinaryTree<T>::PostOrderWithoutRecursion(BinaryTreeNode<T>* root)
{
    using std::stack;
    stack<BinaryTreeNode<T>*> S;
    BinaryTreeNode<T>* pointer = root;
    while(!S.empty() || pointer)
    {
        if(pointer)
        {
            S.push(pointer);
            if(pointer->getRightChild())
            {
                S.push(pointer->getRightChild());
            }
            if(pointer->getLeftChild())
                pointer = pointer->getLeftChild();
            else 
                pointer = pointer->getRightChlid();
        }
        else
        {
            BinaryTreeNode<T>* node = S.top();
            S.pop();
            Visit(node);
            pointer = node->S.top();
        }           
    }
}

广度优先遍历

存储方式

链式存储结构

  • 分为二叉链表三叉链表,前者有左右子结点两个指针域,后者多一个父结点指针域。区别在找父结点时,二叉链表要从根结点开始,三叉链表直接找。
  • 完全二叉树的线性存储结构:第 i 个结点的两个子结点为2i+1, 2i+2

二叉搜索树

  • 每个结点的数据是一个关键值码;
  • 左子结点的关键值码小于根结点,右子结点的关键值码大于根结点
  • 按中序周游得到从小到大的序列
  • 搜索时只需要查两棵子树之一,时间复杂度为 O(nlogn)
  • 插入算法,在树形较平衡的时候效率高;
  • 删除算法:把子结点较小者提到根结点,再删去该子结点(递归);

  • 最小值堆
  • 一个关键码序列, Ki<=K2i+1,Ki<=K2i+2 ,对应一个完全二叉树,根结点的关键码不大于两个子结点。
  • 局部有序
  • 最小值在根结点

堆的实现

  • 筛选法:从倒数第一个度为不为0的结点开始,进行它和子结点之间的微调
  • 建堆的效率是 O(n) ,查找等操作的效率是 O(logn) , 排序的效率是 O(nlogn)

优先队列

就是堆?

Huffman树

  • 用于通信的编码和译码,对使用频率不同(权值不同)的字符分配码字,要求达到无歧义和效率高两个目的。
  • 实现方法是扩充二叉树的外部路径的加权外部路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值