通用树结构——GTree、GTreeNode

  通用树结构工程量比较大,我没时间把每一个点扩展开,详细的附上说明与过程。我浓缩了下步骤,过程全部体现在代码与注释里了。所以希望阅读的小伙伴可以按一个目录为一章的心态,根据目录的顺序与讲解,对照代码一点点的去理解。
  其中因为继承,也涉及了Tree与TreeNode中的成员变量,所以对照抽象父类来阅读会更加方便。(https://blog.csdn.net/Es_Study_Yu/article/details/105268522)

通用树的存储结构与实现

  通用树整体的继承结构:


  设计要点:
    GTree的每个结点可以存在多个后继结点
    GTreeNode能够包含任意多指向后继结点的指针
    实现树形结构中的接口(增,删,查,等)

树是递归定义的,所以树中的函数也大都用递归实现!

  GTreeNode的设计与实现:
    继承自TreeNode(继承关系),引用单链表实现(组合关系)

  GTree的设计与实现:
    继承自Tree(继承关系),引用通用树节点实现(组合关系)

  通用树的实现架构:


设计时,为什么每个树节点需要包含指向前驱结点的指针?
因为:根节点→叶节点是非线性的数据结构
   叶结点→根节点是线性的数据结构(链表)

通用树结构示例

通用树中的查找操作

 1、通过树中的元素的值查找
   定义功能:find(node,value)
   在node为根节点的树中查找value所在的结点

 2、通过树中的结点的值查找
   定义功能:find(node,obj)
   在node为根节点的树中查找是否存在obj结点

通用树中的插入操作

  如何指定树中的结点插入在什么位置?
    树是非线性的,无法使用下标来定位数据元素
    每一个结点都有唯一的前驱结点(父节点)
    因此,必须找到(指明)前驱结点,才可以插入新结点

新结点插入示例

插入流程图

插入子函数

通用树中的清除操作

  定义清除操作功能:free(node)
    清除node为根节点的树
    释放树中的每一个结点(内存中的释放)


  树中的结点可能来源与不同的存储空间,如何判断堆空间中的结点并释放?(PS:单凭内存地址很难判断是否为堆空间创建的)
    使用工厂模式
    在TreeNode中增加包含成员变量m_flag
    将TreeNode中的operator new重载为包含成员函数(无法外部new出新对象)
    提供工厂方法GTreeNode* NewNode()
    在工厂方法中,new出新结点时,将m_flag设置为true
    无参构造的时候,m_flag设置为false
    只有这两种方式可以产生结点,所以可以通过m_flag判断是否为堆对象。

通用树中的删除操作

  删除操作成员函数的设计要点
    将删除结点所代表的子树一并删除
    如果删除中的结点所代表的子树中,有扔需要的数据怎么办?
      删除后,返回为一棵独立的堆空间中的树
    如果没有需要的数据呢?
      返回为指向该子树的智能指针对象
      不需要的话,因为生命周期原因,会直接析构
      需要的话,使用智能指针对象指向即可继续使用

删除流程图

通用树中的属性操作

 1、树中结点的数目
   定义功能:count(node)
   在node为根节点的树中统计结点数目

树中结点数目的计算示例

 2、树的高度
   定义功能:height(node)
   获取node为根结点的树的高度

树的高度计算示例

 3、树的度数
   定义功能:degree(node)
   获取node为根结点的树的度数

树的度计算示例

通用树中的层次遍历

  树是非线性的数据结构,没有固定的编号方式,只能通过查找访问结点,需要遍历时效率太低,怎样快速遍历呢?
  设计思路:
    使用游标的方式,利用LinkQueue实现
    提供一组遍历相关函数:begin() current() next() end()
    函数实现方式:
      begin() → 将根节点压入队列
      current() → 返回队头结点指向的数据元素
      next() → 将队头结点弹出,将队头结点的孩子压入队列中
      end() → 判断队列是否为空

层次遍历算法示例

GTree

GTree.h

#ifndef __GTREE_H_
#define __GTREE_H_

#include "include/Tree.h"
#include "include/GTreeNode.h"
#include "include/Exception.h"
#include "include/SharedPointer.h"
#include "include/LinkQueue.h"


namespace JYlib
{


/*                  通用树结构
 *    通用树里插入的对象的生命周期必须要比树的生命周期长
 * 不然在调用或者析构时都会有问题(尽量不用局部变量)
 *    begin next current end 调用时,需要先begin,
 * 让队列里有元素,才可以使用current
*/
template < typename T >
class GTree : public Tree<T>
{
protected:
    //用链式队包含GTreeNode的指针,提供队列的遍历方式
	LinkQueue<GTreeNode<T>*> m_queue;

	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())//堆对象才会被析构
			{
                //std::cout << "delete  " << node->value << std::endl;
				delete node;
			}
		}
	}

	void remove(GTreeNode<T>* node,GTree<T>*& ret)
	{
		ret = new GTree();

		if(ret != NULL)
		{
			if(node != root())//是否为根节点
			{
                //找到这个孩子(Noed)的父结点(GTreeNode),在父结点的孩子们(child链表)中移除(remove)该孩子
				LinkList<GTreeNode<T>*>& child = dynamic_cast<GTreeNode<T>*>(node->parent)->child;

				child.remove(child.find(node));

				node->parent = NULL;
			}
			else
			{
				this->m_root = NULL;
			}
			ret->m_root = node;//将移除的结点作为一颗树返回
		}
		else
		{
			THROW_EXCEPTION(NoEnoughMemoryException,"No enough memory to creat Tree ...");
		}
	}

	int count(GTreeNode<T>* node)const
	{
		int ret = 0;
        
		if(node != NULL)
		{
			ret = 1;

			for(node->child.move(0);!node->child.end();node->child.next())
			{
				ret += count(node->child.current());
			}
		}

		return ret;
	}

	/*自己写的递归height高度
	int height(GTreeNode<T>* node,int n_height=0)const
	{
		int ret = 0;

		if(node != NULL)
		{
			n_height++;
			for(node->child.move(0);!node->child.end();node->child.next())
			{
				ret = height(node->child.current());

				ret = n_height+1 > ret ? n_height+1 : ret;
			}
		}

		return ret;
	}
	*/

	int height(GTreeNode<T>* node)const
	{
		int ret = 0;

		if(node != NULL)
		{
			for(node->child.move(0);!node->child.end();node->child.next())
			{
				int h = height(node->child.current());

				ret = h > ret ? h : ret;
			}
			ret++;
		}

		return ret;
	}

	int degree(GTreeNode<T>* node)const
	{
		int ret = 0;

		if(node != NULL)
		{
			ret = node->child.length();
			for(node->child.move(0);!node->child.end();node->child.next())
			{
				int d = degree(node->child.current());

				ret = d > ret ? d : ret;
			}	
		}

		return ret;
	}

public:

	bool insert(TreeNode<T>* node)//以结点插入,结点的父对象需要指明,即指明插入哪里
	{
		bool ret = true;
		if(node != NULL)
		{
			if(this->m_root == NULL)//是否为空树
			{
				this->m_root = node;
				node->parent = NULL;//根节点没有直接前驱
			}
			else
			{
				GTreeNode<T>* node_parent = find(node->parent);

				if(node_parent != NULL)
				{
					GTreeNode<T>* n = dynamic_cast<GTreeNode<T>*>(node);

					if(node_parent->child.find(n) < 0)//查找是否存在该子节点(防止重复插入)
					{
						node_parent->child.insert(n);
					}
				}
				else
				{
					THROW_EXCEPTION(InvalidParameterException,"Invalid parrent tree node ...");
				}
			}
		}
		else
		{
			THROW_EXCEPTION(InvalidParameterException,"Parameter node cannot be NULL ...");
		}
        
		return ret;
	}

	bool insert(const T& value,TreeNode<T>* parent)//以值插入,结点的父对象需要指明,即指明插入哪里
	{
		bool ret = false;

		GTreeNode<T>* new_node = GTreeNode<T>::NewNode();

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

			ret = insert(new_node);//代码复用
		}
		else
		{
			THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create new_node ...");
		}
        
		return ret;
	}

	//返回智能指针类型,函数返回值生命周期只有一条语句,如果没有智能指针接手,就会析构
	SharedPointer< Tree<T> > remove(const T& value)
	{
		GTree<T>* ret = NULL;
		GTreeNode<T>* del_node = find(value);

		if(del_node != NULL)
		{
			remove(del_node,ret);

			m_queue.clear();
		}
		else
		{
			THROW_EXCEPTION(InvalidParameterException,"Can not find the node via parameter value ...");
		}
		
		return ret;
	}

	SharedPointer< Tree<T> > remove(TreeNode<T>* node)
	{
		GTree<T>* ret = NULL;
		GTreeNode<T>* del_node = find(node);

		if(del_node != NULL)//树中是否存在该结点
		{
			remove(del_node,ret);

			m_queue.clear();
		}
		else
		{
			THROW_EXCEPTION(InvalidParameterException,"Parameter node is invalid ...");
		}

		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 degree(root());
	}

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

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

	bool begin()
	{
		bool ret = (root() != NULL);

		if(ret)
		{
			m_queue.clear();//清空队列,防止有上一次树剩余的子项
			m_queue.add(root());//根节点加入队列
		}

		return ret;
	}

	bool next()//弹出队首,并插入child,next后
	{
		bool ret = (m_queue.length() > 0);

		if(ret)
		{
			GTreeNode<T>* node = m_queue.front();//弹出首结点

			m_queue.remove();

			for(node->child.move(0);!node->child.end();node->child.next())//将该结点的孩子们加入队列
			{
				m_queue.add(node->child.current());
			}
		}
		
		return ret;
	}

	T current()
	{
		if(!end())
		{
			return m_queue.front()->value;
		}
		else
		{
			THROW_EXCEPTION(InvalidOperationException,"No value at current queue ...");
		}
	}

	bool end()
	{
		return (m_queue.length() == 0);
	}

	void clear() 
	{
        free(root());

		this->m_root = NULL;

        m_queue.clear();
	}

	~GTree()
	{
		clear();
	}

};


}


#endif

GTreeNode

GTreeNode.h

#ifndef __GTREENODE_H_
#define __GTREENODE_H_

#include "include/Object.h"
#include "include/LinkList.h"


namespace JYlib
{

/*
					通用树节点
封装new,使得只能调用提供的静态成员函数NewNode,生成堆对象
在NewNode和构造函数中将堆对象做标记,区分是否为堆对象
*/
template < typename T >
class GTreeNode : public TreeNode<T>
{
public:
	//链表的指向仍为指针,所以删除链表后,链表指向的内存仍然存在
	LinkList<GTreeNode<T>*> child;//用链表完成指向任意多个后继的子节点

	//创建堆对象并做标记,仅能通过该方式创建堆对象
	static GTreeNode<T>* NewNode()
	{
		GTreeNode<T>* ret = new GTreeNode();

		if(ret)
		{
			ret->m_flag = true;
		}

		return ret;
	}

};



}




#endif

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Keil MDK中使用树可以通过定义树节点结构体和使用指针来实现。下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> // 定义树节点结构体 typedef struct node { int data; struct node *left; struct node *right; } TreeNode; // 创建节点 TreeNode* create_node(int data) { TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); node->data = data; node->left = NULL; node->right = NULL; return node; } // 插入节点 TreeNode* insert_node(TreeNode* root, int data) { if (root == NULL) { return create_node(data); } if (data < root->data) { root->left = insert_node(root->left, data); } else { root->right = insert_node(root->right, data); } return root; } // 计算树的深度 int tree_depth(TreeNode* root) { if (root == NULL) { return 0; } int left_depth = tree_depth(root->left); int right_depth = tree_depth(root->right); return (left_depth > right_depth ? left_depth : right_depth) + 1; } int main() { // 创建根节点 TreeNode* root = create_node(5); // 插入节点 root = insert_node(root, 3); root = insert_node(root, 7); root = insert_node(root, 1); root = insert_node(root, 4); root = insert_node(root, 6); root = insert_node(root, 8); // 计算树的深度并输出 int depth = tree_depth(root); printf("Tree Depth: %d\n", depth); return 0; } ``` 在这个示例代码中,我们首先定义了一个树节点结构体,包含了节点数据和左右子节点指针。然后我们定义了创建节点和插入节点的函数,使用递归的方式实现。最后我们定义了计算树深度的函数,并在main函数中使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值