zoukankan      html  css  js  c++  java
  • 数据结构开发(20):树中结点的查找、插入、清除与删除操作

    0.目录

    1.树中结点的查找操作

    2.树中结点的插入操作

    3.树中结点的清除操作

    4.树中结点的删除操作

    5.小结

    1.树中结点的查找操作

    查找的方式:

    • 基于数据元素值的查找
      1. GTreeNode<T>* find(const T& value) const
    • 基于结点的查找
      1. GTreeNode<T>* find(TreeNode<T>* node) const

    树中数据元素和结点的查找:

    基于数据元素值的查找:

    • 定义功能:find(node, value)
      1. 在 node 为根结点的树中查找 value 所在的结点

    在GTree.h中实现基于数据元素值的查找:

    protected:
        GTreeNode<T>* find(GTreeNode<T>* node, const T& value) const
        {
            GTreeNode<T>* ret = NULL;
    
            if( node != NULL )
            {
                if( node->value == value )
                {
                    return node;
                }
                else
                {
                    for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next())
                    {
                        ret = find(node->child.current(), value);
                    }
                }
            }
    
            return ret;
        }
    public:
        GTreeNode<T>* find(const T& value) const
        {
            return find(root(), value);
        }
    

    基于结点的查找:

    • 定义功能:find(node, obj)
      1. 在 node 为根结点的树中查找是否存在 obj 结点

    在GTree.h中实现基于结点的查找:

    protected:
        GTreeNode<T>* find(GTreeNode<T>* node, GTreeNode<T>* obj) const
        {
            GTreeNode<T>* ret = NULL;
    
            if( node == obj )
            {
                return node;
            }
            else
            {
                if( node != NULL )
                {
                    for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next())
                    {
                        ret = find(node->child.current(), obj);
                    }
                }
            }
    
            return ret;
        }
    public:
        GTreeNode<T>* find(TreeNode<T>* node) const
        {
            return find(root(), dynamic_cast<GTreeNode<T>*>(node));
        }
    

    2.树中结点的插入操作

    插入的方式:

    • 插入新结点
      1. bool insert(TreeNode<T>* node)
    • 插入数据元素
      1. bool insert(const T& value, TreeNode<T>* parent)

    问题:

    • 如何指定新结点在树中的位置?

    问题分析:

    • 树是非线性的,无法采用下标的形式定位数据元素
    • 每一个树结点都有唯一的前驱结点 ( 父结点 )
    • 因此,必须先找到前驱结点,才能完成新结点的插入

    新结点的插入:

    插入新结点:

    插入数据元素:

    在GTree.h中实现插入操作:

    public:
        bool insert(TreeNode<T>* node)
        {
            bool ret = true;
    
            if( node != NULL )
            {
                if( this->m_root == NULL )
                {
                    node->parent = NULL;
                    this->m_root = node;
                }
                else
                {
                    GTreeNode<T>* np = find(node->parent);
    
                    if( np != NULL )
                    {
                        GTreeNode<T>* n = dynamic_cast<GTreeNode<T>*>(node);
    
                        if( np->child.find(n) < 0 )
                        {
                            np->child.insert(n);
                        }
                    }
                    else
                    {
                        THROW_EXCEPTION(InvalidOperationException, "Invalid parent tree node ...");
                    }
                }
            }
            else
            {
                THROW_EXCEPTION(InvalidParameterException, "Parameter node cannot be NULL ...");
            }
    
            return ret;
        }
    
        bool insert(const T& value, TreeNode<T>* parent)
        {
            bool ret = true;
            GTreeNode<T>* node = new GTreeNode<T>();
    
            if( node != NULL )
            {
                node->value = value;
                node->parent = parent;
    
                insert(node);
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
            }
    
            return ret;
        }
    

    mian.cpp测试这棵树:

    #include <iostream>
    #include "GTree.h"
    
    using namespace std;
    using namespace StLib;
    
    int main()
    {
        GTree<char> t;
        GTreeNode<char>* node = NULL;
    
        t.insert('A', NULL);
    
        node = t.find('A');
        t.insert('B', node);
        t.insert('C', node);
        t.insert('D', node);
    
        node = t.find('B');
        t.insert('E', node);
        t.insert('F', node);
    
        node = t.find('E');
        t.insert('K', node);
        t.insert('L', node);
    
        node = t.find('C');
        t.insert('G', node);
    
        node = t.find('G');
        t.insert('N', node);
    
        node = t.find('D');
        t.insert('H', node);
        t.insert('I', node);
        t.insert('J', node);
    
        node = t.find('H');
        t.insert('M', node);
    
        const char* s = "KLFGMIJ";
    
        for(int i=0; i<7; i++)
        {
            TreeNode<char>* node = t.find(s[i]);
    
            while( node != NULL )
            {
                cout << node->value << " ";
    
                node = node->parent;
            }
    
            cout << endl;
        }
    
        return 0;
    }
    

    运行结果为:

    K E B A 
    L E B A 
    F B A 
    G C A 
    M H D A 
    I D A 
    J D A 
    

    3.树中结点的清除操作

    清除操作的定义:

    • void clear()
      1. 将树中的所有结点清除 ( 释放堆中的结点 )

    树中数据元素的清除:

    清除操作功能的定义:

    • free(node)
      1. 清除 node 为根结点的树
      2. 释放树中的每一个结点

    在GTree.h中实现清除操作:

    protected:
        void free(GTreeNode<T>* node)
        {
            if( node != NULL )
            {
                for(node->child.move(0); !node->child.end(); node->child.next())
                {
                    free(node->child.current());
                }
    
                delete node;
            }
        }
    public:
        void clear()
        {
            free(root());
    
            this->m_root = NULL;
        }
    

    问题:

    • 树中的结点可能来源于不同的存储空间如何判断堆空间中的结点并释放?

    问题分析

    • 单凭内存地址很难准确判断具体的存储区域
    • 只有堆空间的内存需要主动释放 ( delete )
    • 清除操作时只需要对堆中的结点进行释放

    解决方案:工厂模式

    1. GTreeNode 中增加保护成员变量 m_flag
    2. GTreeNode 中的 operator new 重载为保护成员函数
    3. 提供工厂方法 GTreeNode* NewNode()
    4. 在工厂方法中 new 新结点并将 m_flag 设置为 true

    树结点的工厂模式示例:

    实现树结点的工厂模式(修改GTreeNode.h和GTree.h中的对应代码):
    GTreeNode.h

    #ifndef GTREENODE_H
    #define GTREENODE_H
    
    #include "TreeNode.h"
    #include "LinkList.h"
    
    namespace StLib
    {
    
    template <typename T>
    class GTreeNode : public TreeNode<T>
    {
    protected:
        bool m_flag;
    
        void* operator new(size_t size) throw()
        {
            return Object::operator new(size);
        }
    public:
        LinkList<GTreeNode<T>*> child;
    
        GTreeNode()
        {
            m_flag = false;
        }
    
        bool flag()
        {
            return m_flag;
        }
    
        static GTreeNode<T>* NewNode()
        {
            GTreeNode<T>* ret = new GTreeNode<T>();
    
            if( ret != NULL )
            {
                ret->m_flag = true;
            }
    
            return ret;
        }
    };
    
    }
    
    #endif // GTREENODE_H
    

    修改GTree.h中的对应代码:

    protected:
        void free(GTreeNode<T>* node)
        {
            if( node != NULL )
            {
                for(node->child.move(0); !node->child.end(); node->child.next())
                {
                    free(node->child.current());
                }
    
                if( node->flag() )
                {
                    delete node;
                }
            }
        }
    public:
        bool insert(const T& value, TreeNode<T>* parent)
        {
            bool ret = true;
            GTreeNode<T>* node = GTreeNode<T>::NewNode();
    
            if( node != NULL )
            {
                node->value = value;
                node->parent = parent;
    
                insert(node);
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
            }
    
            return ret;
        }
    

    4.树中结点的删除操作

    删除的方式:

    • 基于数据元素值的删除
      1. SharedPointer< Tree<T> > remove(const T& value)
    • 基于结点的删除
      1. SharedPointer< Tree<T> > remove(TreeNode<T>* node)

    删除操作成员函数的设计要点:

    • 将被删结点所代表的子树进行删除
    • 删除函数返回一棵堆空间中的树
    • 具体返回值为指向树的智能指针对象

    树中结点的删除:

    实用的设计原则:

    • 当需要从函数中返回堆中的对象时,使用智能指针 ( SharedPointer ) 作为函数的返回值。

    删除操作功能的定义:

    • void remove(GTreeNode<T>* node, GTree<T>*& ret)
      1. 将 node 为根结点的子树从原来的树中删除
      2. ret 作为子树返回 ( ret 指向堆空间中的树对象 )

    删除功能函数的实现:

    在GTree.h中实现删除操作:

    protected:
        void remove(GTreeNode<T>* node, GTree<T>*& ret)
        {
            ret = new GTree<T>();
    
            if( ret == NULL )
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree ...");
            }
            else
            {
                if( root() == node )
                {
                    this->m_root = NULL;
                }
                else
                {
                    LinkList<GTreeNode<T>*>& child = dynamic_cast<GTreeNode<T>*>(node->parent)->child;
    
                    child.remove(child.find(node));
    
                    node->parent = NULL;
                }
    
                ret->m_root = node;
            }
        }
    public:
        SharedPointer< Tree<T> > remove(const T& value)
        {
            GTree<T>* ret = NULL;
            GTreeNode<T>* node = find(value);
    
            if( node == NULL )
            {
                THROW_EXCEPTION(InvalidParameterException, "Can not find the node via parameter value ...");
            }
            else
            {
                remove(node, ret);
            }
    
            return ret;
        }
    
        SharedPointer< Tree<T> > remove(TreeNode<T>* node)
        {
            GTree<T>* ret = NULL;
    
            node = find(node);
    
            if( node == NULL )
            {
                THROW_EXCEPTION(InvalidParameterException, "Parameter node is invalid ...");
            }
            else
            {
                remove(dynamic_cast<GTreeNode<T>*>(node), ret);
            }
    
            return ret;
        }
    

    main.cpp测试

    #include <iostream>
    #include "GTree.h"
    
    using namespace std;
    using namespace StLib;
    
    int main()
    {
        GTree<char> t;
        GTreeNode<char>* node = NULL;
        GTreeNode<char> root;
    
        root.value = 'A';
        root.parent = NULL;
    
        t.insert(&root);
    
        node = t.find('A');
        t.insert('B', node);
        t.insert('C', node);
        t.insert('D', node);
    
        node = t.find('B');
        t.insert('E', node);
        t.insert('F', node);
    
        node = t.find('E');
        t.insert('K', node);
        t.insert('L', node);
    
        node = t.find('C');
        t.insert('G', node);
    
        node = t.find('D');
        t.insert('H', node);
        t.insert('I', node);
        t.insert('J', node);
    
        node = t.find('H');
        t.insert('M', node);
    
        SharedPointer< Tree<char> > p = t.remove(t.find('D'));
    
        const char* s = "KLFGMIJ";
    
        for(int i=0; i<7; i++)
        {
            TreeNode<char>* node = p->find(s[i]);
    
            while( node != NULL )
            {
                cout << node->value << " ";
    
                node = node->parent;
            }
    
            cout << endl;
        }
    
        return 0;
    }
    

    运行结果为:

    
    
    
    
    M H D 
    I D 
    J D 
    

    5.小结

    • 查找操作是树的关键操作之一
    • 基于数据元素的查找可判断值是否存在于树中
    • 基于结点的查找可判断树中是否存在指定结点
    • 插入操作和删除操作都依赖于查找操作

    • 插入操作是构建树的唯一操作
    • 执行插入操作时必须指明结点间的父子关系
    • 插入操作必须正确处理指向父结点的指针
    • 插入数据元素时需要从堆空间中创建结点

    • 清除操作用于销毁树中的每个结点
    • 销毁结点时需要决定是否释放对应的内存空间
    • 工厂模式可用于“定制”堆空间中的结点
    • 只有销毁定制结点的时候需要进行释放

    • 删除操作将目标结点所代表的子树移除
    • 删除操作必须完善处理父结点和子结点的关系
    • 删除操作的返回值为指向树的智能指针对象
    • 函数中返回堆中的对象时,使用智能指针作为返回值

    最终的GTreeNode.h和GTree.h代码:
    GTreeNode.h

    #ifndef GTREENODE_H
    #define GTREENODE_H
    
    #include "TreeNode.h"
    #include "LinkList.h"
    
    namespace StLib
    {
    
    template <typename T>
    class GTreeNode : public TreeNode<T>
    {
    protected:
        bool m_flag;
    
        void* operator new(size_t size) throw()
        {
            return Object::operator new(size);
        }
    public:
        LinkList<GTreeNode<T>*> child;
    
        GTreeNode()
        {
            m_flag = false;
        }
    
        bool flag()
        {
            return m_flag;
        }
    
        static GTreeNode<T>* NewNode()
        {
            GTreeNode<T>* ret = new GTreeNode<T>();
    
            if( ret != NULL )
            {
                ret->m_flag = true;
            }
    
            return ret;
        }
    };
    
    }
    
    #endif // GTREENODE_H
    

    GTree.h

    #ifndef GTREE_H
    #define GTREE_H
    
    #include "Tree.h"
    #include "GTreeNode.h"
    #include "Exception.h"
    
    namespace StLib
    {
    
    template <typename T>
    class GTree : public Tree<T>
    {
    protected:
        GTreeNode<T>* find(GTreeNode<T>* node, const T& value) const
        {
            GTreeNode<T>* ret = NULL;
    
            if( node != NULL )
            {
                if( node->value == value )
                {
                    return node;
                }
                else
                {
                    for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next())
                    {
                        ret = find(node->child.current(), value);
                    }
                }
            }
    
            return ret;
        }
    
        GTreeNode<T>* find(GTreeNode<T>* node, GTreeNode<T>* obj) const
        {
            GTreeNode<T>* ret = NULL;
    
            if( node == obj )
            {
                return node;
            }
            else
            {
                if( node != NULL )
                {
                    for(node->child.move(0); !node->child.end() && (ret == NULL); node->child.next())
                    {
                        ret = find(node->child.current(), obj);
                    }
                }
            }
    
            return ret;
        }
    
        void free(GTreeNode<T>* node)
        {
            if( node != NULL )
            {
                for(node->child.move(0); !node->child.end(); node->child.next())
                {
                    free(node->child.current());
                }
    
                if( node->flag() )
                {
                    delete node;
                }
            }
        }
    
        void remove(GTreeNode<T>* node, GTree<T>*& ret)
        {
            ret = new GTree<T>();
    
            if( ret == NULL )
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree ...");
            }
            else
            {
                if( root() == node )
                {
                    this->m_root = NULL;
                }
                else
                {
                    LinkList<GTreeNode<T>*>& child = dynamic_cast<GTreeNode<T>*>(node->parent)->child;
    
                    child.remove(child.find(node));
    
                    node->parent = NULL;
                }
    
                ret->m_root = node;
            }
        }
    public:
        bool insert(TreeNode<T>* node)
        {
            bool ret = true;
    
            if( node != NULL )
            {
                if( this->m_root == NULL )
                {
                    node->parent = NULL;
                    this->m_root = node;
                }
                else
                {
                    GTreeNode<T>* np = find(node->parent);
    
                    if( np != NULL )
                    {
                        GTreeNode<T>* n = dynamic_cast<GTreeNode<T>*>(node);
    
                        if( np->child.find(n) < 0 )
                        {
                            np->child.insert(n);
                        }
                    }
                    else
                    {
                        THROW_EXCEPTION(InvalidOperationException, "Invalid parent tree node ...");
                    }
                }
            }
            else
            {
                THROW_EXCEPTION(InvalidParameterException, "Parameter node cannot be NULL ...");
            }
    
            return ret;
        }
    
        bool insert(const T& value, TreeNode<T>* parent)
        {
            bool ret = true;
            GTreeNode<T>* node = GTreeNode<T>::NewNode();
    
            if( node != NULL )
            {
                node->value = value;
                node->parent = parent;
    
                insert(node);
            }
            else
            {
                THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new tree node ...");
            }
    
            return ret;
        }
    
        SharedPointer< Tree<T> > remove(const T& value)
        {
            GTree<T>* ret = NULL;
            GTreeNode<T>* node = find(value);
    
            if( node == NULL )
            {
                THROW_EXCEPTION(InvalidParameterException, "Can not find the node via parameter value ...");
            }
            else
            {
                remove(node, ret);
            }
    
            return ret;
        }
    
        SharedPointer< Tree<T> > remove(TreeNode<T>* node)
        {
            GTree<T>* ret = NULL;
    
            node = find(node);
    
            if( node == NULL )
            {
                THROW_EXCEPTION(InvalidParameterException, "Parameter node is invalid ...");
            }
            else
            {
                remove(dynamic_cast<GTreeNode<T>*>(node), ret);
            }
    
            return ret;
        }
    
        GTreeNode<T>* find(const T& value) const
        {
            return find(root(), value);
        }
    
        GTreeNode<T>* find(TreeNode<T>* node) const
        {
            return find(root(), dynamic_cast<GTreeNode<T>*>(node));
        }
    
        GTreeNode<T>* root() const
        {
            return dynamic_cast<GTreeNode<T>*>(this->m_root);
        }
    
        int degree() const
        {
            return 0;
        }
    
        int count() const
        {
            return 0;
        }
    
        int height() const
        {
            return 0;
        }
    
        void clear()
        {
            free(root());
    
            this->m_root = NULL;
        }
    
        ~GTree()
        {
            clear();
        }
    };
    
    }
    
    #endif // GTREE_H
    
  • 相关阅读:
    【Azure 应用服务】在Azure App Service多实例的情况下,如何在应用中通过代码获取到实例名(Instance ID)呢?
    【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
    【Azure Developer】使用Azure Key Vault 的Key签名后,离线验证的一些参考资料
    【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题
    【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(Xms512m Xmx1204m)?
    【Azure API 管理】APIM添加Logtoeventhub的策略后,一些相关APIM与Event Hub的问题
    【Azure API 管理】为调用APIM的请求启用Trace 调试APIM Policy的利器
    【Azure 事件中心】China Azure上是否有Kafka服务简答
    【Azure 应用服务】探索在Azure上设置禁止任何人访问App Service的默认域名(Default URL)
    【Azure 微服务】记一次错误的更新Service Fabric 证书而引发的集群崩溃而只能重建
  • 原文地址:https://www.cnblogs.com/PyLearn/p/10158757.html
Copyright © 2011-2022 走看看