028-86922220

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

1数据结构(13)_二叉树的概念及常用操作实现

1. 树到二叉树的转换

思考:通用树结构的实现太过复杂(树中每个结点都可以有任意多的孩子,具有多种形态),工程中很少会用到如此复杂的树是否可以简化呢?
思路:减少树结点中孩子的数量。但这样树是否还能通用呢?

公司主营业务:网站设计、做网站、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出克东免费做网站回馈大家。

1.1.树的两种表示法

双亲孩子表示法:
1 数据结构(13)_二叉树的概念及常用操作实现
孩子兄弟表示法:
1 数据结构(13)_二叉树的概念及常用操作实现
孩子兄弟表示法的特点:
1.能够表示任意的树形结构
2.每个结点包含一个数据成员和两个指针成员
3.孩子结点指针和兄弟结点指针构成“树杈”

2.2.二叉树

二叉树是由n(n>=0)个节点组成的有限集合,该集合或者为空,或者是由一个根结点加上两颗分别称为左子树和右子树的、互不相交的二叉树组成。
1 数据结构(13)_二叉树的概念及常用操作实现
满二叉树:
如果二叉树中所有分支结点的度数都为2,且叶子结点都在同一层次上,则称这类二叉树为满二叉树。
完全二叉树:
如果一棵具有N个结点高度为K的二叉树,它的每一个结点与高度为K的满二叉树中编号1~n的结点一一对应,则称这颗二叉树为完全二叉树。(从上到下,从左到右编号)。
完全二叉树的特性:
同样结点的二叉树,完全二叉树的高度最小;完全二叉树的叶结点一定出现在最下面两层。
1.最底层的叶结点一定出现在左边;
2.倒数第二层的叶结点一定出现在右边;
3.完全二叉树中度数为1的结点只有左孩子。
1 数据结构(13)_二叉树的概念及常用操作实现
总结:
1.通用树结构采用了双亲结点表示法进行描述;
2.孩子兄弟表示法也有能力描述任意类型的树结构;
3.孩子兄弟表示法能够将通用树转化为二叉树(最多有两个孩子);

2.二叉树的深层特性

1.在二叉树的第i层最多有2^(i-1)个结点(i>=1);
2.高度为K的二叉树最多有2^k - 1个结点(K>=0);
3.对于任何一颗二叉树,如果其叶结点有n0个,度为2的非叶结点有n2个,则有n0 = n2 + 1;
推导证明:

template < typename T >
class BTree : public Tree
{

};

二叉树的实现架构:
1 数据结构(13)_二叉树的概念及常用操作实现

4. 二叉树的常用操作

4.1 .二叉树的查找操作

1 数据结构(13)_二叉树的概念及常用操作实现
1.基于数据元素值的查找:
BTreeNode* find(const T& value) const
1 数据结构(13)_二叉树的概念及常用操作实现

    virtual BTreeNode* find(BTreeNode* node, const T& value) const
    {
        BTreeNode* ret = NULL;

        if(node != NULL)    // 判断是否为空树
        {
            if(node->value == value)    //比较根结点
            {
                ret = node;
            }
            else
            {
                if(ret == NULL)
                {
                    //递归查找左子树
                    ret = find(node->m_left, value);
                }

                if(ret == NULL)
                {
                    //递归查找右子树
                    ret = find(node->m_right, value);
                }
            }
        }

        return ret;
    }

        BTreeNode* find(const T& value) const
    {
        return find(root(), value);
    }

2.基于结点的查找:
BTreeNode find(TreeNode node) const
1 数据结构(13)_二叉树的概念及常用操作实现

        virtual BTreeNode* find(BTreeNode* node, BTreeNode* obj) const
    {
        BTreeNode* ret = NULL;

        if(node != NULL)    // 判断是否为空树
        {
            if(node == obj)    //比较根结点
            {
                ret = node;
            }
            else
            {
                if(ret == NULL)
                {
                    //递归查找左子树
                    ret = find(node->m_left, obj);
                }

                if(ret == NULL)
                {
                    //递归查找右子树
                    ret = find(node->m_right, obj);
                }
            }
        }

        return ret;
    }

        BTreeNode* find(TreeNode* node) const
    {
        return find(root(), dynamic_cast*>(node));
    }

4.2.二叉树的插入操作

思考:是否能在二叉树的任意结点处插入子结点?
因为二叉树的定义中,每个结点最多只能有两个子结点,所以必然不能在任意结点处插入,因此需要制定新的数据元素(新结点)的插入位置。
二叉树结点的位置定义:

enum BTreeNodePos
{
    ANY,
    LEFT,
    RIGHT
};

1 数据结构(13)_二叉树的概念及常用操作实现
1 数据结构(13)_二叉树的概念及常用操作实现
1.定义功能函数,指定位置的结点插入:
virtual bool insert(BTreeNode<T>* newnode, BTreeNode<T>* node, BTNodePos pos)
1 数据结构(13)_二叉树的概念及常用操作实现

    virtual bool insert(BTreeNode* n, BTreeNode* np, BTreeNodePos pos)
    {
        bool ret = true;

        //指定的插入位置为ANY(没有指定插入位置)
        if(pos == ANY)
        {
            if(np->m_left == NULL)    // 左子树结点为空,插入到左子树
            {
                np->m_left = n;
            }
            else if(np->m_right == NULL)  // ...
            {
                np->m_right = n;
            }
            else
            {
                ret = false;
            }
        }

        // 指定插入到左孩子结点
        if(pos == LEFT)
        {
            if(np->m_left == NULL)
            {
                np->m_left = n;
            }
            else
            {
                ret = false;
            }
        }

        // 指定插入到右孩子结点
        if(pos == RIGHT)
        {
            if(np->m_right == NULL)
            {
                np->m_right = n;
            }
            else
            {
                ret = false;
            }
        }

        return ret;
    }

2.插入新结点

bool insert(TreeNode* node, BTreeNodePos pos)
bool insert(TreeNode* node)

1 数据结构(13)_二叉树的概念及常用操作实现

    //插入结点,并指定位置
    bool insert(TreeNode* node, BTreeNodePos pos)
    {
        bool ret = true;

        if(node != NULL)
        {
            if(root() == NULL)   //判断根结点处是否可以插入
            {
                node->m_parent = NULL;
                this->m_root = node;
            }
            else
            {
                BTreeNode* np  = find(node->m_parent);   //查找父节点是否存在

                if(np != NULL)
                {
                    // 调用二叉树插入操作功能函数
                    ret = insert(dynamic_cast*>(node), np, pos);
                }
                else
                {
                    THROW_EXCEPTION(InvaildParameterException, "invalid parent tree node...");
                }
            }
        }
        else
        {
            THROW_EXCEPTION(InvaildParameterException, "param con't be NULL...");
        }

        return ret;
    }

    //插入结点,无位置要求
    bool insert(TreeNode* node)
    {
        return insert(node, ANY);
    }

3.插入数据元素

bool insert(const T& value,TreeNode* parent, BTreeNodePos pos)
bool insert(const T& value,TreeNode* parent)

1 数据结构(13)_二叉树的概念及常用操作实现

    //插入数据元素,指定位置
    bool insert(const T& value,TreeNode* parent, BTreeNodePos pos)
    {
        bool ret = true;

        BTreeNode* node = BTreeNode::NewNode();

        if(node != NULL)
        {
            node->value = value;
            node->m_parent = parent;

            insert(node, pos);
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "no memory to create new tree node...");
        }

        return ret;
    }

    bool insert(const T& value,TreeNode* parent)
    {
        return insert(value, parent, ANY);
    }

测试技巧:从叶结点到根结点为线性数据结构,可以使用链表的遍历方式。
总结:
1.二叉树的插入操作需要指明插入的位置;
2.插入操作必须正确处理指向父节点的指针
3.插入数据元素时需要从堆空间中创建结点,让数据元素插入失败时,需要释放结点空间。

4.3. 二叉树中结点的删除与清除

4.3.1.结点删除操作

1 数据结构(13)_二叉树的概念及常用操作实现
1.删除操作功能定义
void remove(BTreeNode node, BTree& ret)
将node为根结点的子树从原来的二叉树中删除,ret作为子树返回(ret指向堆空间中的二叉树对象)
1 数据结构(13)_二叉树的概念及常用操作实现

    virtual void remove(BTreeNode* node, BTree*& ret)
    {
        ret = new BTree();

        if(ret != NULL)
        {
            if(root() == node)
            {
                this->m_root = NULL;
            }
            else
            {
                BTreeNode* np = dynamic_cast*>(node->m_parent);

                if(np->m_left == node)
                {
                    np->m_left = NULL;
                }
                else if(np->m_right == node)
                {
                    np->m_right = NULL;
                }

                node->m_parent = NULL;
            }

            ret->m_root = node;
        }
        else
        {
            THROW_EXCEPTION(NoEnoughMemoryException, "no memory to create new tree...");
        }
    }

2.基于数据元素的删除
SharedPointer< Tree > remove(const T& value)

    SharedPointer< Tree > remove(const T& value)
    {
        BTree* ret = NULL;
        BTreeNode* node = find(value);

        if(node != NULL)
        {
            remove(node, ret);
            m_queue.clear();
        }

        return ret;
    }

3.基于结点的删除
SharedPointer< Tree > remove(TreeNode* node)

    SharedPointer< Tree > remove(TreeNode* node)
    {
        BTree* ret = NULL;
        node = find(node);

        if(node != NULL)
        {
            remove(dynamic_cast*>(node), ret);
            m_queue.clear();
        }

        return ret;
    }

测试技巧:直接打印已经删除的子树。
总结:
删除操作将目标界定啊所在的子树移除,必须完善处理父子结点的关系

4.3.2.结点清除操作

void clear() // 将二叉树中的所有节点清除(释放堆中的结点)
1 数据结构(13)_二叉树的概念及常用操作实现
1.清除操作功能定义
free(node) // 清除node为根结点的二叉树,释放二叉树中的每个结点
1 数据结构(13)_二叉树的概念及常用操作实现

    // 清空树的功能函数定义
    void free(BTreeNode* node)
    {
        if(node != NULL)
        {
            free(node->m_left);
            free(node->m_right);

            //cout << node->value << endl;
            if(node->flag())
            {
                delete node;
            }
        }
    }

    void clear()
    {
        free(root());
        this->m_root = NULL;
    }

测试技巧:可以在free函数中打印删除的每一个结点
总结:
清除操作用于销毁树中的每个结点,销毁时要判断是否释放对应的内存空间(工厂模式)。

4.4.二叉树的属性操作实现

1 数据结构(13)_二叉树的概念及常用操作实现

4.4.1.二叉树的结点数目

定义功能函数:cout(node) // 在node为根结点的二叉树中递归统计结点数目
1 数据结构(13)_二叉树的概念及常用操作实现

    // 获取树的结点个数,递归实现
    int count(BTreeNode* node) const
    {
        int ret = 0;

        if(node != NULL)
        {
            // 左子树的结点个数 + 右子树的结点个数 + 1(根结点)
            ret = count(node->m_left) + count(node->m_right) + 1;
        }

        return ret;
    }

    int count() const
    {
        return  count(root());
    }

4.4.2.二叉树的高度

定义功能函数:height(node) // 递归获取node为根结点的二叉树的高度
1 数据结构(13)_二叉树的概念及常用操作实现

    // 获取树的结点个数,递归实现
    int height(BTreeNode* node) const
    {
        int ret = 0;

        if(node != NULL)
        {
            int hl = height(node->m_left);
            int hr = height(node->m_right);

            // 左右子树高度的最大值 + 1(根结点)
            ret = ((hl > hr) ? hl : hr) + 1;
        }

        return ret;
    }

    int height() const
    {
        return  height(root());
    }

4.4.3.二叉树的度数

定义功能函数:degree(node) // 获取node为根结点的二叉树的度数
1 数据结构(13)_二叉树的概念及常用操作实现

    // 获取二叉树的度,递归实现
    int degree(BTreeNode* node) const
    {
        int ret = 0;

        if(node != NULL)
        {
        /*
         // 普通思路
            int dl = degree(node->m_left);  // 左子树的度
            int dr = degree(node->m_right); // 右子树的度
            ret = !!node->m_left + !!node->m_right;     //根结点的度

            if(dl > ret)
            {
                ret = dl;
            }
            else if(dr > ret)
            {
                ret = dr;
            }
        */
        /*
         * 优化效率,二叉树的最大度数为2,如果ret已经为2,则不需要继续遍历
            ret = !!node->m_left + !!node->m_right;     //根结点的度
            if(ret < 2)
            {
                int dl = degree(node->m_left);  // 左子树的度
                if(dl > ret)
                {
                    ret = dl;
                }

            }

            if(ret < 2)
            {
                int dr = degree(node->m_right);  // 左子树的度
                if(dr > ret)
                {
                    ret = dr;
                }

            }
        */

            // 优化冗余代码
            ret = !!node->m_left + !!node->m_right;     //根结点的度
            BTreeNode* child[] = {node->m_left, node->m_right};

            for(int i=0; i<2 && ret<2; i++)
            {
                int d = degree(child[i]);
                if(d > ret)
                {
                    ret = d;
                }
            }
        }

        return ret;
    }

    int degree() const
    {
        return degree(root());
    }

4.5.二叉树的层次遍历

二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中的所有节点,使得每个结点被访问一次。
思考:通用树结构的层次遍历算法是否可以用在二叉树结构上?如果可以需要做什么改动?
不同之处在于二叉树最多只有两个孩子。
设计思路:
在树中定义一个新游标(BTreeNode*),遍历开始将游标指向根结点(root()),获取游标指向的数据元素,通过结点中的child成员移动游标;
提供一组遍历相关的函数,按层次访问树中的数据元素。
1 数据结构(13)_二叉树的概念及常用操作实现
层次遍历算法:
原料:class LinkQueue; 游标:LinkQueue::front();
思想:

template 
// 其中node为根结点,也是中序遍历访问的结点,pre为中序遍历时的前驱结点指针
void inOrderThread(BTreeNode* node,BTreeNode*& pre)   
{
    if(node != NULL)
    {
        inOrderThread(node->m_left,pre);
        node->m_left = pre;
        if(pre != NULL)
        {
            pre->m_right = node;
        }
        pre = node;
        inOrderThread(node->m_right,pre);
    }
}

template 
BTreeNode* inOrderThread1(BTreeNode* node)
{
    BTreeNode* pre = NULL;
    inOrderThread(node,pre);

    while((node != NULL) && (node->m_left != NULL))
    {
        node = node->m_left;
    }

    return node;
}

8.2.2.解法2

中序遍历的结点正好是结点的水平次序
思路:

1.使用辅助指针,指向转换后双向链表的头节点和尾节点;
2.根结点与左右子树转为的双向链表连接,称为完整双向链表。
1 数据结构(13)_二叉树的概念及常用操作实现
定义功能函数:inOrderThread(node, head, tail):
Node为根结点,也是中序遍历的访问结点,head转后成功后指向双向链表的首节点,tail转换成功后指向双向链表的尾节点。
1 数据结构(13)_二叉树的概念及常用操作实现

template 
// Node为根结点,也是中序遍历的访问结点,head转换成功后指向双向链表的首节点,tail转换成功后指向双向链表的尾节点
void inOrderThread(BTreeNode* node,BTreeNode*& head,BTreeNode*& tail)
{
    if(node != NULL)
    {
        BTreeNode* h = NULL;
        BTreeNode* t = NULL;
        inOrderThread(node->m_left,h,t);
        node->m_left = t;
        if(t != NULL)
        {
            t->m_right = node;
        }

        head = (h != NULL) ?  h : node;

        h = NULL;
        t = NULL;

        inOrderThread(node->m_right,h,t);
        node->m_right = h;
        if(h != NULL)
        {
            h->m_left = node;
        }
        tail = (t != NULL) ?  t : node;
    }
}

template 
BTreeNode* inOrderThread2(BTreeNode* node)
{
    BTreeNode* head = NULL;
    BTreeNode* tail = NULL;
    inOrderThread(node,head,tail);

    return head;
}

当前文章:1数据结构(13)_二叉树的概念及常用操作实现
转载来源:http://www.tsicrk.com/article/gsgscd.html

其他资讯

让你的专属顾问为你服务

0.9444s