二叉树

来源:互联网 发布:最新3d网络游开服时间 编辑:程序博客网 时间:2024/05/29 05:56

基本概念

  • 二叉树(binary tree):结点的有限集合,一般为根结点+左右子树
  • 根结点(root)
  • 子树subtree
  • 父结点parent
  • 子结点child
  • 兄弟结点sibling
  • 结点的度degree):结点的子树的个数
  • 叶结点(leaf ):度为0的结点
  • 内部结点internal node),除叶结点以外的非终端结点
  • (edge):有向线段<k,k>
  • 路径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树

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