树形结构是一类重要的非线性结构。树形结构是节点之间有分支,并具有层次关系的结构。

特殊且重要:

树中的节点,只有一个直接的前趋,有n个直接的后继

比如:家谱

非线性结构:在结构中任何一个节点,只有一个直接前驱,有n个直接后继

线性结构:在结构中任何一个节点,只有一个直接前驱,只有一个直接后继

一、树的定义

树(tree)是包含n(n>0)个节点的有穷集。树中每个元素用节点来表示

在一棵非空树中,有且仅有一个特定的称为根的节点,当n>1时其余节点可分为m(m>0)个互不相交的有限集T1,T2....Tm,其中,每一个集合本身又是一棵树,并且称为根的子树(subtree).

树也可以理解为:是由根节点和若干棵子树构成--描述了递归性

树的递归定义刻画了树的固有特性:一棵非空树是由若干棵子树构成的,而子树又可由若干棵更小的子树构成

树中有且只有一个根节点

二、树形结构基本术语

1.节点的度:

一个节点含有的子树的个数称为该节点的度

2.树的度:

一棵树中,最大的节点的度称为树的度

3.叶节点(终端节点):

度为0的节点

4.分支节点(非终端节点):

度不为0的节点

5.孩子和双亲:

树中的某个节点的子树之根称为该节点的孩子或儿子,相应的,该节点称为孩子的双亲或父亲。同一个双亲的孩子称为兄弟

6.祖先和子孙:

以某节点为根的子树中任一节点都称为该节点的子孙。相反这个子孙节点沿父亲节点往上直至根节点的任一节点称为祖先

7、节点的层:

从根开始定义起,根为第1层,根的子节点为第2层,以此类推。

如果没有做出特殊的说明,根节点默认为第一层,但是根节点也能被描述为第0层

8、树的高度或深度:

树中节点的最大层次。

9、森林:

由m(m>=0)棵互不相交的树的集合称为森林。

10、有序树和无序树:

树中任意节点的子结点之间有顺序关系,这种树称为有序树。

反之树中任意节点 的子结点之间没有顺序关系,这种树称为无序树,也称为自由树。

三、树形结构的逻辑特征

1、树中任意一节点都可以有零个或多个直接后继(即孩子)节点,但至多只能有一个直接前趋(即双亲)节点。

2、树中只有根节点无前趋,它是开始节点;叶节点无后继,它们是终端节点。

3、祖先与子孙的关系是对父子关系的延拓,它定义了树中节点之间的纵向次序

4、有序树中,同一组兄弟节点从左到右有长幼之分。

四、树形结构代码示例

#pragma once
template<class T>
class cmytree_list
{
	typedef struct treenode//树中节点的结构
	{
		T data;//数据域
		//指针域
		treenode* parent;//父节点
		treenode* brother;//兄弟节点
		treenode* child;//子节点,都是可以指空的
	}T_NODE,*PT_NODE;
	PT_NODE root;//根节点--指针类型
public:
	cmytree_list();
	~cmytree_list();
	void clear();//清除函数---只是一个接口
	bool find(T const&finddata);//查找函数--在私有属性下面的类中成员还是可以访问的,而这个函数只是提供了一个在类外可以访问私有函数成员的接口
	void insert(T const& insertnode, T const& finddata, bool ischild=true);//认为在插入的时候一定是先插入的子节点


private:
	void _clear(PT_NODE r);//真正的私有的清除函数
	PT_NODE _find(PT_NODE root, T const &finddata)//真正的查找函数,查找到数据后返回这个节点
	{
		//不一定是内联函数
		if (root)//如果根节点不为空
		{
			if (root->data == finddata)
				return root;//把这个指向要的节点的指针返回出去
			PT_NODE temp=_find(root->brother, finddata);//如果根节点不是要找的节点,那么就再找根节点的兄弟节点,需要一个结构体指针来接收可能的返回值
			if (temp)
				return temp;
			return _find(root->child, finddata);//再找子节点
		}
		return nullptr;//根节点为空直接返回空
	}
};

template<class T>
cmytree_list<T>::cmytree_list()
{
	 
}

template<class T>
cmytree_list<T>::~cmytree_list()
{
	clear();
}

template<class T>
void cmytree_list<T>::clear()//清除函数的接口
{
	_clear(root);
}

template<class T>
 bool cmytree_list<T>::find(T const & finddata)
{
	 return _find(root) != nullptr;
}

 template<class T>
void cmytree_list<T>::insert(T const & insertnode, T const & finddata, bool ischild)//第一个参是要插入的数据,第二个参是查找的数据,第三个参是是否在子节点处插入
 {
	/*
		插入一个子节点的步骤
		1.先看有没有根节点
		2.没有根节点,就让待插入节点成为根节点
		3.有根节点,就进行下一步操作
		
		4.寻找插入位置
		5.没有找到,就自己决定怎么操作,这里我用的是,直接将待插入节点插入到最后一个子节点的子节点上
		6.找到位置,进行下一步操作
		
		还有看插入时操作者想在子节点插入还是在兄弟节点插入

		在子节点插入
		7.看这个节点的子节点是不是置空的
		8.如果是,就直接插入这个子节点
		9.不是的话,就找这个节点的兄弟节点,然后插入

		在兄弟节点插入
		10.不是根节点才进行插入
		11.找到最后一个兄弟节点,再进行插入
		*/
	//先准备一个待插入的节点
	PT_NODE tempinsertnode = new T_NODE;//分配空间
	tempinsertnode->data = insertnode;//数据域赋值
	tempinsertnode->parent = nullptr;//父节点指针赋值
	tempinsertnode->brother = nullptr;//兄弟节点赋值
	tempinsertnode->child = nullptr;//孩子节点赋值
	if (root)
	{
		//表示非空树
		PT_NODE findnode = _find(root, finddata);//定义一个新的节点来接收查找到的节点
		if (findnode)//找到了插入位置
		{
			//表示在树中找到了位置
			if (ischild)//在子节点处插入
			{
				//在该位置的子节点处插入
				if (findnode->child)
				{
					//表示该位置已有子节点
					PT_NODE temp = findnode->child;
					while (temp->brother)//找这个子节点有没有兄弟节点
					{
						temp = temp->brother;
					}
					temp->brother = tempinsertnode;
					tempinsertnode->parent = temp->parent;
                    //因为这里temp指向兄弟节点的还是,这个兄弟节点的兄弟节点,所以要用temp->parent,而不是直接用temp
				}
				else
				{
					//表示该位置没有子节点
					findnode->child = tempinsertnode;
					tempinsertnode->parent = findnode;//这里指向子节点的findnode一定是父亲节点,所以就可以直接用findnode
				}
			}
			else//在该位置的兄弟节点处插入
			{
				if (findnode != root)
				{
					while (findnode->brother)
					{
						findnode = findnode->brother;
					}
					findnode->brother = tempinsertnode;
					tempinsertnode->parent = findnode->parent;
				}				
			}
		}
		else
		{
			//没有找到插入位置
			//自定义规则:如果没有找到插入位置,直接插入到最后一个子节点的子节点上
			PT_NODE temp = root;//根节点不能动
			while (temp->child)
			{
				temp = temp->child;
			}//目的是指向末尾子节点的指针域
			//temp指针定在最后一个子节点上
			//temp指向的child指针是指向的空地址
			temp->child = tempinsertnode;//让最后一个孩子指针指向待插入的节点
			tempinsertnode->parent = temp;//待插入节点的父节点要指向最后一个子节点
		}	  
	}
	else
	{
		//表示根节点为空
		root = tempinsertnode;//直接让插入节点为根节点
	}

 }

template<class T>
void cmytree_list<T>::_clear(PT_NODE r)
{
	if (r)
	{
		_clear(r->brother);//先删除兄弟节点
		_clear(r->child);//再删除子节点
		delete r;
		r = nullptr;
	}
}
posted @ 2021-04-09 10:22  kisfly  阅读(249)  评论(0编辑  收藏  举报