树
树
树形结构是一类重要的非线性结构。树形结构是节点之间有分支,并具有层次关系的结构。
特殊且重要:
树中的节点,只有一个直接的前趋,有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;
}
}