树的存储结构(C++)

双亲表示法

在树中,除了根节点外的任意一个结点都有一个唯一的双亲结点,因此可以考虑使用一组连续的储存空间储存树中的每一个结点,数组中的一个元素可以表示为树中的一个结点。在数组元素中除包含结点元素本身,还包含它的双亲在数组中的序号(从0开始)。根节点的双亲域赋值为-1

这种表示方法称为双亲表示法。图例如下:
在这里插入图片描述

  1. 这种方法求某个节点的双亲比较方便,但是如果想要求某个结点的孩子结点,则需要查询整个数组。
  2. 这种方法也不能直接反应各兄弟结点之间的关系,所以求兄弟也比较困难。

实际上,可以在数组元素中增加两个域以解决上述问题:一个数据域存放该结点的第一个孩子结点在数组中的序号;另一个存放该结点的兄弟结点在数组中的序号。

孩子表示法

多重链表法 用一个多重链表表示树,链表中的每一个结点包含一个数据域(存放该结点的元素)和多个指针域(存放该结点的孩子)。

因为各个结点的孩子数不同,所以结点的指针域个数有两种不同的设置方法:

  1. 每个结点的指针域个数等于该结点的度数。这种方法比较直接,但是由于各结点构造不同,实际操作中较少使用。
  2. 每个结点指针域个数等于树的度数。这种方法各个结点是同构的。但是由于树中各结点的度数一般小于树的度数,容易造成存储空间的浪费。

数组表示法 用一维数组顺序存储树中的各个结点的信息,并将各结点的孩子信息组成一个单链表。孩子链表的每一个结点表示一个孩子结点,它由两个域组成,其中一个数据域存放孩子结点在数组中的序号,另一个指针域存放它的兄弟结点。
该树是上一个图中的树

双亲-孩子表示法

就是将上述两个方法结合起来
在这里插入图片描述
代码实现:

template <class ElemType>
struct ChildParentTreeNode 
{
// 数据成员:
	ElemType data;					// 数据域
	Node<int> *childLkList;		    // 孩子链表	
	int parent;						// 双亲位置域

// 构造函数:
	ChildParentTreeNode();			// 无参数的构造函数
	ChildParentTreeNode(ElemType item, int pt = -1, Node<int> *childlk = NULL);// 已知数据域值和双亲位置建立结构
};

孩子双亲表示树类:

template <class ElemType>
class ChildParentTree
{
protected:
//  树的数据成员:
	ChildParentTreeNode<ElemType> *nodes;			// 存储树结点
	int maxSize;									// 树结点最大个数 
	int root, num;									// 根的位置及结点数

//	辅助函数:
	void PreRootOrderHelp(int r, void (*Visit)(const ElemType &)) const;	// 先根序遍历
	void PostRootOrderHelp(int r, void (*Visit)(const ElemType &)) const;	// 后根序遍历
	int HeightHelp(int r) const;					// 返回以r为根的高
	int DegreeHelp(int r) const;					// 返回以r为根的树的度

public:
//  树方法声明及重载编译系统默认方法声明:
	ChildParentTree();								// 无参构造函数
	virtual ~ChildParentTree();						// 析构函数
	int GetRoot() const;							// 返回树的根
	bool Empty() const;								// 判断树是否为空
	Status GetElem(int cur, ElemType &e) const;	// 用e返回结点元素值
	Status SetElem(int cur, const ElemType &e);	// 将结cur的值置为e
	void PreRootOrder(void (*Visit)(const ElemType &)) const;	// 树的先序遍历
	void PostRootOrder(void (*Visit)(const ElemType &)) const;	// 树的后序遍历
	void LevelOrder(void (*Visit)(const ElemType &)) const;		// 树的层次遍历
	int NodeCount() const;							// 返回树的结点个数
	int NodeDegree(int cur) const;					// 返回结点cur的度
	int Degree() const;								// 返回树的度
	int FirstChild(int cur) const;					// 返回结点cur的第一个孩子
	int RightSibling(int cur) const;				// 返回结点cur的右兄弟
	int Parent(int cur) const;						// 返回结点cur的双亲
	Status InsertChild(int cur, int i, const ElemType &e);	
		// 将数据元素插入为cur的第i个孩子
	int	Height() const;								// 返回树的高
	ChildParentTree(const ElemType &e, int size = DEFAULT_SIZE);
		// 建立以数据元素e为根的树
	ChildParentTree(ElemType items[], int parents[], int r, int n, int size = DEFAULT_SIZE);
		// 建立数据元素为items[],对应结点双亲为parents[],根结点位置为r,结点个数为n的树
};

孩子-兄弟表示法

这是一种链式存储结构,链表中一个结点表示树中的一个结点。它除一个信息域外,还有两个指针域。一个指针域指向该结点的第一个孩子结点;另一个指向该结点的兄弟结点。这种方法又称为二重链表表示法
在这里插入图片描述
我们一般采用此方法进行树的存储。
代码实现:

// 孩子兄弟表示树结点类
template <class ElemType>
struct ChildSiblingTreeNode 
{
// 数据成员:
	ElemType data;								// 数据域
	ChildSiblingTreeNode<ElemType> *firstChild;	// 指向首孩子指针域
	ChildSiblingTreeNode<ElemType> *nextSibling;// 指向右兄弟指针域

// 构造函数:
	ChildSiblingTreeNode();						// 无参数的构造函数
	ChildSiblingTreeNode(ElemType val,			// 有参数的构造函数
		ChildSiblingTreeNode<ElemType> *fChild = NULL,
		ChildSiblingTreeNode<ElemType> *nSibling = NULL);	
		
};

由此可以对使用该种方法表示的树进行各种典型操作:

template <class ElemType>
class ChildSiblingTree
{
protected:
//  树的数据成员:
	ChildSiblingTreeNode<ElemType> *root;			// 根指针 

//	辅助函数:
	void Destroy(ChildSiblingTreeNode<ElemType> * &r);		// 销毁以r为根的树
	void PreRootOrder(ChildSiblingTreeNode<ElemType> *r, void (*Visit)(const ElemType &)) const;	
		// 先根遍历以r为根的数 
	void PostRootOrder(ChildSiblingTreeNode<ElemType> *r, void (*Visit)(const ElemType &)) const;
		// 后根遍历以r为根的数
	int NodeCount(ChildSiblingTreeNode<ElemType> *r) const;	// 求以r为根的树的结点个数
	int Height(ChildSiblingTreeNode<ElemType> *r) const;	// 求以r为根的树的高
	int Degree(ChildSiblingTreeNode<ElemType> *r) const;	// 求以r为根的树的度
	ChildSiblingTreeNode<ElemType> *Parent(ChildSiblingTreeNode<ElemType> *r, 
		const ChildSiblingTreeNode<ElemType> *cur) const;	//  求cur的双亲
	ChildSiblingTreeNode<ElemType> *CopyTree(ChildSiblingTreeNode<ElemType> *copy);	
		// 复制树
	ChildSiblingTreeNode<ElemType> *CreateTreeGhelp(ElemType items[], int parents[], int r, int n);
		// 建立数据元素为items[],对应结点双亲为parents[],根结点位置为r,结点个数为n的树,并 求树的根

public:
//  孩子兄弟表示树类方法的声明:
	ChildSiblingTree();									// 无参数的构造函数
	virtual ~ChildSiblingTree();						// 析构函数
	ChildSiblingTreeNode<ElemType> * GetRoot() const;	//  求树的根
	bool IsEmpty() const;								// 判断树是否为空
	Status GetElem(ChildSiblingTreeNode<ElemType> *cur, ElemType &e) const;
		// 用e 求结点元素值
	Status SetElem(ChildSiblingTreeNode<ElemType> *cur, const ElemType &e);
		// 将结cur的值置为e
	void PreRootOrder(void (*Visit)(const ElemType &)) const;		// 树的先根序遍历
	void PostRootOrder(void (*Visit)(const ElemType &)) const;		// 树的后根序遍历
	void LevelOrder(void (*Visit)(const ElemType &)) const;			// 树的层次遍历
	int NodeCount() const;								            // 求树的结点个数
	int NodeDegree(ChildSiblingTreeNode<ElemType> *cur) const;	    // 求结点cur的度
	int Degree() const;									            // 求树的度
	ChildSiblingTreeNode<ElemType> *FirstChild(ChildSiblingTreeNode<ElemType> *cur) const;	
		// 求树结点cur的第一个孩子
	ChildSiblingTreeNode<ElemType> *NextSibling(ChildSiblingTreeNode<ElemType> *cur) const;				
		// 求树结点cur的下一个兄弟
	ChildSiblingTreeNode<ElemType> *Parent(ChildSiblingTreeNode<ElemType> *cur) const;
		// 求结点cur的双亲
	Status InsertChild(ChildSiblingTreeNode<ElemType> *cur, int i, const ElemType &e);	
		// 将数据元素e插入为cur的第i个孩子
	Status DeleteChild(ChildSiblingTreeNode<ElemType> *cur, int i);// 删除cur的第i个棵子树
	int	Height() const;									//  求树的高
	ChildSiblingTree(const ElemType &e);				// 建立以数据元素e为根的树
	ChildSiblingTree(const ChildSiblingTree<ElemType> &copy);	// 复制构造函数
	ChildSiblingTree(ElemType items[], int parents[], int n);
		// 建立数据元素为items[],对应结点双亲为parents[],根结点位置为r,结点个数为n的树
	ChildSiblingTree(ChildSiblingTreeNode<ElemType> *r);// 建立以r为根的树
	ChildSiblingTree<ElemType> &operator=(const ChildSiblingTree<ElemType>& copy);	
		// 重载赋值运算
};

评论 1 您还未登录,请先 登录 后发表或查看评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值