数据结构开发(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
  4. 在工厂方法中 new 新结点并将 m_flag 设置为 true

树结点的工厂模式示例:

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

GTreeNode.h

#ifndef GTREENODE_H
#define GTREENODE_H

#include "Tree.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(‘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);

    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 "Tree.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

原文地址:https://www.cnblogs.com/PyLearn/p/10158757.html

时间: 2024-10-17 04:23:50

数据结构开发(20):树中结点的查找、插入、清除与删除操作的相关文章

4. mongodb插入,修改,删除操作

mongodb插入操作 语法: db.collection.save(collections); OR  db.collection.insert(collections); 插入有上面两种方式. 1. 借用对象来插入 >item={id:1,name:"hello"} >db.lottu.insert(item) 2. 循环插入数据:一下子插入5条记录这种很不错吧 >for(i=1;i<=5;i++)db.lottu.insert({id:i,name:&qu

jQuery:节点(插入,复制,替换,删除)操作

<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>jQuery插入,复制.替换和删除节点</title> <script type="text/javascript" src="jquery-1.3.2.js"></scri

二叉平衡树的插入和删除操作

1.      二叉平衡树 二叉排序树的时间复杂度和树的深度n有关.当先后插入的结点按关键字有序时,二叉排序树退化为单枝树,平均查找长度为(n+1)/2,查找效率比较低.提高查找效率,关键在于最大限度地降低树的深度n.因此需要在构成二叉排序树的过程中进行“平衡化”处理,使之成为二叉平衡树. 二叉平衡树,又称AVL树.它或者是一棵空树,或者是具有下列性质的树: 1)      具备二叉排序树的所有性质: 2)      左子树和右子树深度差的绝对值不超过1: 3)      左子树和右子树都是二叉

数据结构开发(23):二叉树中结点的查找、插入、删除与清除操作

0.目录 1.二叉树中结点的查找操作 2.二叉树中结点的插入操作 3.二叉树中结点的删除操作 4.二叉树中结点的清除操作 5.小结 1.二叉树中结点的查找操作 查找的方式: 基于数据元素值的查找 BTreeNode<T>* find(const T& value) const 基于结点的查找 BTreeNode<T>* find(TreeNode<T>* node) const 树中数据元素和结点的查找: 基于数据元素值的查找: 定义功能:find(node,

数据结构开发(21):树中属性操作与层次遍历

0.目录 1.树中属性操作的实现 2.树形结构的层次遍历 3.小结 1.树中属性操作的实现 树中结点的数目: 定义功能:count(node) 在 node 为根结点的树中统计结点数目 树结点数目的计算示例: count(A) = count(B) + count(C) + count(D) + 1 在GTree.h中实现统计结点数目: protected: int count(GTreeNode<T>* node) const { int ret = 0; if( node != NULL

数据结构Java实现03----单向链表的插入和删除

数据结构Java实现03----单向链表的插入和删除 文本主要内容: 链表结构 单链表代码实现 单链表的效率分析 一.链表结构:            概念: 链式存储结构是基于指针实现的.我们把一个数据元素和一个指针称为结点.   数据域:存数数据元素信息的域. 指针域:存储直接后继位置的域. 链式存储结构是用指针把相互直接关联的结点(即直接前驱结点或直接后继结点)链接起来.链式存储结构的线性表称为链表. 链表类型: 根据链表的构造方式的不同可以分为: 单向链表 单向循环链表 双向循环链表 二

算法11---红黑树不带父结点指针的插入实现

算法11---红黑树不带父结点指针的插入实现 1 #include <iostream> 2 using namespace std; 3 #define BLACK 0 4 #define RED 1 5 #define Nil -1 6 #define LEN sizeof(struct Tree) 7 struct Tree 8 { 9 struct Tree*left; 10 struct Tree*right; 11 int key; 12 int color; 13 }; 14 s

让你提前认识软件开发(20):如何在C语言里面执行SQL语句?

[文章摘要] 在通信类软件中,程序经常需要与数据库打交道.为了实现诸如从数据库中获取数据.更新数据库表某字段.插入或删除某条数据等功能,就需要在C语言程序中构造一些SQL语句,并用函数来执行这些SQL语句. 本文介绍如何在C语言程序中构造并执行SQL语句,为相关软件开发工作的开展提供了参考. [关键词] SQL语句  C语言  程序  流程  开发 一.为什么要在C语言程序中执行SQL语句? 在C语言程序中执行SQL语句的原因有以下几个: (1) 程序需要获取数据库中某数据表的字段值,并对这些字

【剑指offer】q50:树中结点的最近祖先

#@ root: the root of searched tree #@ nodeToFind: the tree-node to be found #@ path: the path from root to node #@@ #@@ search tree referenced by root, and return the path #@@ from root to node, if node not exist, path = [] #@@ def getPath(root, node