zoukankan      html  css  js  c++  java
  • 树形结构是一类重要的非线性结构。树形结构是节点之间有分支,并具有层次关系的结构。

    特殊且重要:

    树中的节点,只有一个直接的前趋,有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;
    	}
    }
    
  • 相关阅读:
    人机猜拳
    M1分数分配
    postmortem report of period M1
    视频文档分享网站场景测试说明书
    功能规格说明书
    11.9Daily Scrum
    11.6Daily Scrum
    需求博客
    11.5Daily Scrum
    11.3Daily Scrum
  • 原文地址:https://www.cnblogs.com/Kissfly123/p/14636057.html
Copyright © 2011-2022 走看看