基于二叉树的家谱系统

先看实验要求:
项目名称:基于二叉树的家谱系统
项目内容:采用一棵二叉树来表示一个家谱关系,一个家谱可表示为一颗树,首先将其转换成一颗二叉树表示,如下图为红楼梦家谱的一部分:

图1.1 家谱的树形表示
在这里插入图片描述

图1.2 家谱的二叉树表示
要求完成的功能如下:
(1)输入一颗二叉树的括号表示法,完成树的构建
(2)使用后序遍历递归算法遍历二叉树并输出
(3)使用先序遍历非递归算法遍历二叉树并输出
(4)指定家谱中的某一成员,输出其所有长辈

在这里插入图片描述
由题目可见,我们需要将这个族谱先转化为字符串表示的二叉树,然后再用代码的方式来建立对应的二叉树。
我们先把对应的字符串表示的树表示出来:

在这里插入图片描述
然后将已知的图和字符串放在一起:
在这里插入图片描述
我们观察其中的规律:
在这里插入图片描述
发现:右子树的都是同一个辈分的,左子树的每次有一个分叉,它的辈分就低一级,如:BDE是同一个辈分的,假设我们规定根节点的辈分为1,辈分数依次递增,表示它的实际辈分等级越低的话,那么,BDE处在辈分为2的层次,其中C的辈分为3,最低;
为了方便观察,我们再添加几个节点来理解:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可知:AFH在同一个辈分为1的级别上;
BDEGLM都在辈分为2的级别上;
CK都在辈分为3的级别上;
这样,我们就对这个二叉树的表示家谱的方法有了基本的规律认识,下面就是开始建立我们的族谱二叉树了:
在建立之前,笔者在之前写过一将字符串表示的二叉树转化为代码实现的二叉树的博客,为了更好地理解二叉树的建立,请查看原博文:
字符串转化为二叉树的diam实现和图解过程

与该博文中讲解的字符串转化为二叉树的过程相似,只不多这个家谱的二叉树要比普通的字符串建立的二叉树的节点中要多一个成员变量:表示该节点的辈分级别 int generationLevel;
此次笔者将使用通用类的模板来创建二叉树和节点类:
下面是节点类:

#pragma once
template<class T>
class BNode
{
public:
	BNode();
	BNode(T t);
	~BNode();

public:
	/*左孩子*/
	BNode<T> *leftChild;
	/*右孩子*/
	BNode<T> *rightChild;
	/*节点的值*/
	T t;
	/*该节点所在的辈分级数*/
	int generationLevel;
};

template<class T>
inline BNode<T>::BNode()
{
}

template<class T>
inline BNode<T>::BNode(T t)
{
	this->t = t;
}

template<class T>
inline BNode<T>::~BNode()
{
}

二叉树类的模板:

#include"BNode.hpp"
#include"TempNode.hpp"
#include<string>
using namespace std;

template<class T>
class BinaryTree
{
public:
	BinaryTree(const char* str);
	/*后序遍历非递归*/
	void PosOrder();
	/*后序遍历递归*/
	void PosOrderRe(BNode<T>* root);
	/*中序遍历非递归实现*/
	void InOrder();
	/*先序遍历非递归第一种实现*/
	void PreOrderNotRe();
	/*先序遍历非递归第二种实现*/
	void PreOrderNotRe2();
	/*输出节点obj的所有长辈*/
	void FindAllAncestor(T obj);
	/*输出节点ancestor的值及同辈的值,这里的T是默认为输入一个字符char,如果T是一个节点的话那么在比较相等的时候就要注意对应的转换,适当地改改这个函数就可以了*/
	void ShowAncestor(T t);
	/*二叉树的析构函数,用于销毁二叉树的节点*/
	void destroyTree(BNode<T>* root);
	~BinaryTree();
public:
	BNode<T>* root = NULL;                                      //树的根节点指针
};

下面是主要函数以及代码的实现:
(1)建立二叉树

template<class T>
inline BinaryTree<T>::BinaryTree(const char* str)
{
	const char * p = str;                                      //临时字符串指针
	BNode<char>* node = NULL;                                  // 临时节点指针
	int k = 1;													//判定是左孩子还是右孩子的标识:1为左孩子,2为右孩子
	Stack<BNode<char>*> *stack = new Stack<BNode<char>*>();     //用于装取每个二叉树节点的栈

	while (*p != '\0')
	{
		switch (*p)
		{
		case '(':
			stack->push(node);									 //遇到‘('说明node节点为根节点
			k = 1;                                               //遇到‘(’说明下一个节点为左节点
			break;
		case ',':
			k = 2;											     //遇到‘,’说明下一个节点为右节点
			break;
		case ')':
			stack->pop();										  //遇到‘)’弹出栈顶元素的根节点
			break;
		default:
			node = new BNode<char>(*p);								//创建节点
			if (root == NULL)										 //第一个节点即为根节点
			{
				node->generationLevel = 1;                           //根节点的父辈级数为1
				root = node;
			}
			else if (k == 1)									   //左节点
			{
				BNode<char>* topNode = stack->getTop();				 //获取栈顶的根节点
				node->generationLevel = topNode->generationLevel + 1;//该节点为左子树的节点,则辈分级别加1
				topNode->leftChild = node;							 //根节点的左孩子
			}
			else if (k == 2) {										//右节点
				BNode<char>* topNode = stack->getTop();				 //获取栈顶的根节点
				node->generationLevel = topNode->generationLevel;    //该节点为右孩子,则辈分级别和父节点一样
				topNode->rightChild = node;							  //根节点的右孩子
			}
			break;
		}
		p++;														  //字符指针移动到下一个字符的位置
	}
}

在建立二叉树的时候,要注意给每个节点的辈分级别赋值,具体是:
根节点的辈分为1;
之后,左孩子节点辈分比父结点低一级,那么她的辈分级别就等于父节点的辈分级别加一;右孩子节点与父结点是兄弟辈分,所以它的辈分级别和父节点的辈分级别相同。
下面我用图解的方式讲解一下前几个节点的创建过程:
第一步:
在这里插入图片描述
第二步:
在这里插入图片描述
第三步;
在这里插入图片描述
第四步:
在这里插入图片描述
第五步;
在这里插入图片描述
第六步;
在这里插入图片描述
第七步:在这里插入图片描述
之后的步骤依次类推,注意每次新节点的创建的时候它的辈分级别要分清楚是左孩子节点还是右孩子节点,如果是左孩子节点,那么它的辈分级别就等于父节点的辈分级别加一,如果是右孩子节点,那就它的辈分级别就等于父节点的辈分级别;
由此,我们就创建好了对应的二叉树。
最终的二叉树为:
在这里插入图片描述
建好了二叉树后,我们便开始实现它的遍历所有节点的功能;
首先是
/后序遍历非递归/
void PosOrder();的实现,在这个非递归算法中,采用两个栈的方式来依次实现:

template<class T>
inline void BinaryTree<T>::PosOrder()
{
	//使用两个栈来实现遍历
	Stack<BNode<T>*> *stack = new Stack<BNode<T>*>();
	//用于输出的栈
	Stack<BNode<T>*> *outStack = new Stack<BNode<T>*>();
	BNode<T>* node = root;
	while (node != nullptr || stack->getSize() != 0)
	{
		if (node != NULL)
		{
			//父节点入栈
			stack->push(node);
			//节点入栈
			outStack->push(node);
			//临时节点指向父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}
	//输出所有的节点,逆序输出
	while (outStack->getSize() != 0)
	{
		BNode<T>* node = outStack->pop();
		cout << node->t;
	}

}

/后序遍历递归/
void PosOrderRe(BNode* root)的具体实现代码比较简单,也容易理解:

template<class T>
inline void BinaryTree<T>::PosOrderRe(BNode<T>* root)
{
	//结束递归
	if (root == NULL)
	{
		return;
	}
	//递归左子树
	this->PosOrderRe(root->leftChild);
	//递归右子树
	this->PosOrderRe(root->rightChild);
	//打印节点信息
	cout << root->t;

}

先序遍历的非递归具体的实现;
/先序遍历非递归第一种实现/
void PreOrderNotRe();

template<class T>
inline void BinaryTree<T>::PreOrderNotRe()
{
	//使用栈装取存有BNode节点和特定标识的临时节点
	Stack<TempNode<T>*> *stack = new Stack<TempNode<T>*>();
	//先将头结点入栈,并且设定标识为false
	stack->push(new TempNode<T>(root, false));
	while (stack->getSize() > 0)
	{
		//先将栈顶元素弹出
		TempNode<T>* tempNode = stack->pop();
		//如果临时节点中的BNode节点为空则跳过此次循环
		if (tempNode->bnode == NULL)
		{
			continue;
		}
		//如果临时节点的标识为true则打印输出该节点的值
		if (tempNode->fla == true)
		{
			cout << tempNode->bnode->t;
		}
		else {
			//先将右节点的包装后的临时节点入栈
			stack->push(new TempNode<T>(tempNode->bnode->rightChild, false));
			//再将左节点的包装后的临时节点入栈
			stack->push(new TempNode<T>(tempNode->bnode->leftChild, false));
			//修改此刻临时节点的标识
			tempNode->fla = true;
			//将该临时节点重新入栈
			stack->push(tempNode);
		}
	}
}

当然,先序遍历的非递归也还有另外一种实现:
/先序遍历非递归第二种实现/
void PreOrderNotRe2();`

template<class T>
inline void BinaryTree<T>::PreOrderNotRe2()
{
	if (root == NULL)
	{
		return;
	}
	BNode<T>* node = root;
	Stack<BNode*> *stack = new Stack<BNode*>();
	while (stack->getSize()>0||node!=NULL)
	{
		//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
		while (node!=NULL)
		{
			//先序遍历是第一次遇到该结点的时候输出,即结点入栈
			cout << node->tl;
			stack->push(node);
			node = node->leftChild;
		}
		if (stack->getSize()>0)
		{
			//进入最左下叶子结点的右孩子
			node = stack->pop();
			node = node->rightChild;
		}
		//进入新一轮循环
	}
}

/中序遍历非递归实现/
void InOrder();
中序遍历的非递归实现:


template<class T>
inline void BinaryTree<T>::InOrder()
{
	BNode<T>* node = root;
	Stack<BNode<T>*> *stack = new Stack<BNode<T>*>();
	while (node!=NULL||stack->getSize()>0)
	{
		//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
		while (node!=NULL)
		{
			stack->push(node);
			node = node->leftChild;
		}
		if (stack->getSize()>0)
		{
			//中序遍历是第二次遇到该结点的时候输出,即结点出栈
			node = stack->pop();
			//进入最左下叶子结点的右孩子
			cout << node->t;
			//进入新一轮循环
			node = node->rightChild;
		}


	}

}

下面是查找某个成员的所有兄弟辈分并输出:其实这个问题就是转化为了查找辈分级别相同的二叉树的节点并输出节点的值。那么我们就先要从根节点开始遍历,首先找到该成员对应的节点,然后获得该节点的辈分级别,并输出辈分级别和该成员辈分级别相同的节点值。具体的代码如下:


template<class T>
inline void BinaryTree<T>::ShowAncestor(T target)
{
	//target元素对应的目标节点
	BNode<T>* targetNode = NULL;
	//临时节点,用于遍历所有的树节点
	BNode<T>* node = root;
	//临时装载父节点的栈
	Stack<BNode<T>*>* stack = new 	Stack<BNode<T>*>();
	//用于装取后序遍历顺序的节点的栈
	Stack<BNode<T>*>* outstack = new 	Stack<BNode<T>*>();
	BNode<T>* temp =NULL;
	while (node!=NULL||stack->getSize()> 0)
	{
		if (node!= NULL)
		{
			//找到T元素对应的树节点
			if (node->t== target)
			{
				targetNode = node;
			}
			//父节点入栈
			stack->push(node);
			//节点入栈
			outstack->push(node);
			//临时节点指向弹出的父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}
		
		while (outstack->getSize() > 0) {
			//逆序打印
			BNode<T>* bNode = outstack->pop();
			//打印输出与T元素节点相同辈分级别的节点的元素值
			if (bNode->generationLevel==targetNode->generationLevel)
			{
			cout << bNode->t;
			}
		}
}




接下来我们就开始实现查找某成员的所有长辈的功能,这个功能和寻找兄弟辈分的节点一样,我们先找到目标节点,然后输出比目标节点辈分级别低的节点即可。下面是具体的代码实现:


template<class T>
inline void BinaryTree<T>::FindAllAncestor(T target)
{
	//target元素对应的目标节点
	BNode<T>* targetNode = NULL;
	//临时节点,用于遍历所有的树节点
	BNode<T>* node = root;
	//临时装载父节点的栈
	Stack<BNode<T>*>* stack = new 	Stack<BNode<T>*>();
	//用于装取后序遍历顺序的节点的栈
	Stack<BNode<T>*>* outstack = new 	Stack<BNode<T>*>();
	BNode<T>* temp = NULL;
	while (node != NULL || stack->getSize() > 0)
	{
		if (node != NULL)
		{
			//找到T元素对应的树节点
			if (node->t == target)
			{
				targetNode = node;
			}
			//父节点入栈
			stack->push(node);
			//节点入栈
			outstack->push(node);
			//临时节点指向弹出的父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}

	while (outstack->getSize() > 0) {
		//逆序打印
		BNode<T>* bNode = outstack->pop();
		//打印输出辈分级别小于target对应节点辈分级别的树节点的元素值
		//比如target节点的辈分为3,那么就要打印输出辈分为1或2的节点的值
		if (bNode->generationLevel < targetNode->generationLevel)
		{
			cout << bNode->t;
		}
	}

}

实现了所有要求的功能,最后不要忘记了销毁二叉树,释放空间,防止内存泄露。这里采用了递归遍历实现释放功能:


template<class T>
inline void BinaryTree<T>::destroyTree(BNode<T>* root)
{
	if (root!=nullptr)
	{ 
		//递归销毁左子树
		destroyTree(root->leftChild);
		//递归销毁右子树
		destroyTree(root->rightChild);
		//释放根节点
		delete root;
	}
}

到此,我们的主体代码基本都完成了,现在开始测试:

测试代码为;

#include<iostream>
#include"Stack.hpp"
#include<string>
#include"BNode.hpp"
#include"BinaryTree.hpp"
using namespace std;


int main()
{
	const char* p = "A(B(C,D(,E)),)";
	BinaryTree<char> *tree = new BinaryTree<char>(p);
	cout << "后序遍历非递归:";
	tree->PosOrder();
	cout << endl;
	cout << "后序遍历递归:";
	tree->PosOrderRe(tree->root);
	cout << endl;
	cout << "先序遍历非递归:";
	tree->PreOrderNotRe();
	cout << endl;
	cout << "中序遍历非递归:";
	tree->InOrder();
	cout << endl;
	cout << "B的兄弟:";
	tree->ShowAncestor('B');
	cout << endl;
	cout << "E的兄弟:";
	tree->ShowAncestor('E');
	cout << endl;
	cout << "C的长辈:";
	tree->FindAllAncestor('C');
	cout << endl;
	cout << "E的长辈:";
	tree->FindAllAncestor('E');
	system("pause");
	return 0;
}

运行结果:

在这里插入图片描述

下面附上完整的代码(使用自定义的栈,通用模板类,以.hpp结尾);

#pragma once
#include<iostream>
#include<string>
#include"LNode.hpp"
using namespace std;
template<class T>
class Stack
{
public:
	Stack();
	Stack(Stack<T> &list);
	void push(T element);
	T pop();
	LNode<T>* getButton();
	int getSize();
	void clear();
	Stack<T>& operator=(Stack<T> &list);
	T getTop();
	bool isEmpty();
	~Stack();

private:
	LNode<T> *buttom;
	LNode<T>  *top;
	int size;
	T element;
};
template<class T>
Stack<T>::Stack()
{
	this->size = 0;
}

template<class T>
inline Stack<T>::Stack(Stack<T>& list)
{
	
		int listsize = list.getSize();
		LNode<T> *temp = list.getButton();
		while (temp != NULL)
		{
			LNode<T> *node = new LNode<T>(temp->element);
			if (this->size == 0)
			{
				top = node;;
			   buttom = node;
			   
			}
			else {
				top->next = node;
				top = node;
			}
			this->size++;
			temp = temp->next;
		}
	
}


template<class T>
Stack<T>::~Stack()
{
	this->clear();
	if (this->buttom != NULL) {

	delete this->buttom;
	}
	if (this->top != NULL) {

		delete this->top;
	}
	
}

template<class T>
inline void Stack<T>::push(T element)
{
	LNode<T> *node = new LNode<T>(element);
	if (this->size == 0)
	{
		top = node;
		buttom = node;
	}
	else {
		top->next= node;
		top = node;

	}
	this->size++;
}

template<class T>
inline T Stack<T>::pop()
{
	if (this->size == 0) {
		return T();
	}
	else
	{
		T t = top->element;
	    this->size--;
	LNode<T> *temp = buttom;
	for (int i  = 1; i < this->size; i++)
	{
		temp = temp->next;
	}
	top = temp;
	return t;
	}
}

template<class T>
inline LNode<T>* Stack<T>::getButton()
{
	return buttom;
}

template<class T>
inline int Stack<T>::getSize()
{
	return this->size;
}

template<class T>
inline void Stack<T>::clear()
{
	LNode<T> *temp1 = buttom;
	LNode<T> *temp2 = temp1;
	while (temp1 != NULL)
	{
		temp1 = temp1->next;
		delete temp2;
		temp2 = temp1;
	}
	buttom = new LNode<T>();
	top = new LNode<T>();
	this->size = 0;
}

template<class T>
inline Stack<T>& Stack<T>::operator=(Stack<T> &list)
{
	if (list == NULL)
	{
		return;
	}
	if (this->size != 0)
	{
		this->clear();
	}
	int listsize = list.getSize();
	LNode<T> *temp = list.getButton();
	while (temp != NULL)
	{
		LNode<T> *node = new LNode<T>(temp->element);
		if (this->size == 0)
		{
			top = node;;
			buttom = node;

		}
		else {
			top->next = node;
			top = node;
		}
		this->size++;
		temp = temp->next;
	}
	return this;
}

template<class T>
inline T Stack<T>::getTop()
{
	return top->element;
}

template<class T>
inline bool Stack<T>::isEmpty()
{
	return this->size == 0;
}

#pragma once
#include<iostream>
using namespace std;
template<class T>
class LNode
{
public:
	LNode();
	LNode(T element);
	~LNode();
public :
	T element;
	LNode<T>* next;
};
template<class T>
LNode<T>::LNode()
{
	
}
template<class T>
LNode<T>::LNode(T element)
{
	this->element = element;
}

template<class T>
inline LNode<T>::~LNode()
{
}

#pragma once
template<class T>
class BNode
{
public:
	BNode();
	BNode(T t);
	~BNode();

public:
	/*左孩子*/
	BNode<T> *leftChild;
	/*右孩子*/
	BNode<T> *rightChild;
	/*j节点的值*/
	T t;
	/*该节点所在的辈分级数*/
	int generationLevel;
};

template<class T>
inline BNode<T>::BNode()
{
}

template<class T>
inline BNode<T>::BNode(T t)
{
	this->t = t;
}

template<class T>
inline BNode<T>::~BNode()
{
}

#pragma once
#include<iostream>
#include"BNode.hpp"
using namespace std;
template<class T>
class TempNode
{
public:
	TempNode(BNode<T>* bnode,bool fla);
	~TempNode();
	BNode<T>* bnode;
	bool fla;
};

template<class T>
inline TempNode<T>::TempNode(BNode<T>* bnode, bool fla)
{
	this->bnode = bnode;
	this->fla = fla;
}

template<class T>
inline TempNode<T>::~TempNode()
{
}

#pragma once
#include"Stack.hpp"
#include"BNode.hpp"
#include"TempNode.hpp"
#include<string>
using namespace std;

template<class T>
class BinaryTree
{
public:
	BinaryTree(const char* str);
	/*后序遍历非递归*/
	void PosOrder();
	/*后序遍历递归*/
	void PosOrderRe(BNode<T>* root);
	/*中序遍历非递归实现*/
	void InOrder();
	/*先序遍历非递归第一种实现*/
	void PreOrderNotRe();
	/*先序遍历非递归第二种实现*/
	void PreOrderNotRe2();
	/*输出节点obj的所有长辈*/
	void FindAllAncestor(T obj);
	/*输出节点ancestor的值及同辈的值,这里的T是默认为输入一个字符char,如果T是一个节点的话那么在比较相等的时候就要注意对应的转换,适当地改改这个函数就可以了*/
	void ShowAncestor(T t);
	/*二叉树的析构函数,用于销毁二叉树的节点*/
	void destroyTree(BNode<T>* root);
	~BinaryTree();
public:
	BNode<T>* root = NULL;                                      //树的根节点指针
};


template<class T>
inline BinaryTree<T>::BinaryTree(const char* str)
{
	const char * p = str;                                      //临时字符串指针
	BNode<char>* node = NULL;                                  // 临时节点指针
	int k = 1;													//判定是左孩子还是右孩子的标识:1为左孩子,2为右孩子
	Stack<BNode<char>*> *stack = new Stack<BNode<char>*>();     //用于装取每个二叉树节点的栈

	while (*p != '\0')
	{
		switch (*p)
		{
		case '(':
			stack->push(node);									 //遇到‘('说明node节点为根节点
			k = 1;                                               //遇到‘(’说明下一个节点为左节点
			break;
		case ',':
			k = 2;											     //遇到‘,’说明下一个节点为右节点
			break;
		case ')':
			stack->pop();										  //遇到‘)’弹出栈顶元素的根节点
			break;
		default:
			node = new BNode<char>(*p);								//创建节点
			if (root == NULL)										 //第一个节点即为根节点
			{
				node->generationLevel = 1;                           //根节点的父辈级数为1
				root = node;
			}
			else if (k == 1)									   //左节点
			{
				BNode<char>* topNode = stack->getTop();				 //获取栈顶的根节点
				node->generationLevel = topNode->generationLevel + 1;//该节点为左子树的节点,则辈分级别加1
				topNode->leftChild = node;							 //根节点的左孩子
			}
			else if (k == 2) {										//右节点
				BNode<char>* topNode = stack->getTop();				 //获取栈顶的根节点
				node->generationLevel = topNode->generationLevel;    //该节点为右孩子,则辈分级别和父节点一样
				topNode->rightChild = node;							  //根节点的右孩子
			}
			break;
		}
		p++;														  //字符指针移动到下一个字符的位置
	}
}

template<class T>
inline void BinaryTree<T>::PosOrder()
{
	//使用两个栈来实现遍历
	Stack<BNode<T>*> *stack = new Stack<BNode<T>*>();
	//用于输出的栈
	Stack<BNode<T>*> *outStack = new Stack<BNode<T>*>();
	BNode<T>* node = root;
	while (node != nullptr || stack->getSize() != 0)
	{
		if (node != NULL)
		{
			//父节点入栈
			stack->push(node);
			//节点入栈
			outStack->push(node);
			//临时节点指向父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}
	//输出所有的节点,逆序输出
	while (outStack->getSize() != 0)
	{
		BNode<T>* node = outStack->pop();
		cout << node->t;
	}

}

template<class T>
inline void BinaryTree<T>::PosOrderRe(BNode<T>* root)
{
	//结束递归
	if (root == NULL)
	{
		return;
	}
	//递归左子树
	this->PosOrderRe(root->leftChild);
	//递归右子树
	this->PosOrderRe(root->rightChild);
	//打印节点信息
	cout << root->t;

}

template<class T>
inline void BinaryTree<T>::InOrder()
{
	BNode<T>* node = root;
	Stack<BNode<T>*> *stack = new Stack<BNode<T>*>();
	while (node!=NULL||stack->getSize()>0)
	{
		//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
		while (node!=NULL)
		{
			stack->push(node);
			node = node->leftChild;
		}
		if (stack->getSize()>0)
		{
			//中序遍历是第二次遇到该结点的时候输出,即结点出栈
			node = stack->pop();
			//进入最左下叶子结点的右孩子
			cout << node->t;
			//进入新一轮循环
			node = node->rightChild;
		}


	}

}


template<class T>
inline void BinaryTree<T>::PreOrderNotRe()
{
	//使用栈装取存有BNode节点和特定标识的临时节点
	Stack<TempNode<T>*> *stack = new Stack<TempNode<T>*>();
	//先将头结点入栈,并且设定标识为false
	stack->push(new TempNode<T>(root, false));
	while (stack->getSize() > 0)
	{
		//先将栈顶元素弹出
		TempNode<T>* tempNode = stack->pop();
		//如果临时节点中的BNode节点为空则跳过此次循环
		if (tempNode->bnode == NULL)
		{
			continue;
		}
		//如果临时节点的标识为true则打印输出该节点的值
		if (tempNode->fla == true)
		{
			cout << tempNode->bnode->t;
		}
		else {
			//先将右节点的包装后的临时节点入栈
			stack->push(new TempNode<T>(tempNode->bnode->rightChild, false));
			//再将左节点的包装后的临时节点入栈
			stack->push(new TempNode<T>(tempNode->bnode->leftChild, false));
			//修改此刻临时节点的标识
			tempNode->fla = true;
			//将该临时节点重新入栈
			stack->push(tempNode);
		}
	}
}

template<class T>
inline void BinaryTree<T>::PreOrderNotRe2()
{
	if (root == NULL)
	{
		return;
	}
	BNode<T>* node = root;
	Stack<BNode*> *stack = new Stack<BNode*>();
	while (stack->getSize()>0||node!=NULL)
	{
		//对每一棵树而言,都一直向左下走,直到走到最左下的叶子结点
		while (node!=NULL)
		{
			//先序遍历是第一次遇到该结点的时候输出,即结点入栈
			cout << node->tl;
			stack->push(node);
			node = node->leftChild;
		}
		if (stack->getSize()>0)
		{
			//进入最左下叶子结点的右孩子
			node = stack->pop();
			node = node->rightChild;
		}
		//进入新一轮循环
	}
}

template<class T>
inline void BinaryTree<T>::ShowAncestor(T target)
{
	//target元素对应的目标节点
	BNode<T>* targetNode = NULL;
	//临时节点,用于遍历所有的树节点
	BNode<T>* node = root;
	//临时装载父节点的栈
	Stack<BNode<T>*>* stack = new 	Stack<BNode<T>*>();
	//用于装取后序遍历顺序的节点的栈
	Stack<BNode<T>*>* outstack = new 	Stack<BNode<T>*>();
	BNode<T>* temp =NULL;
	while (node!=NULL||stack->getSize()> 0)
	{
		if (node!= NULL)
		{
			//找到T元素对应的树节点
			if (node->t== target)
			{
				targetNode = node;
			}
			//父节点入栈
			stack->push(node);
			//节点入栈
			outstack->push(node);
			//临时节点指向弹出的父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}
		
		while (outstack->getSize() > 0) {
			//逆序打印
			BNode<T>* bNode = outstack->pop();
			//打印输出与T元素节点相同辈分级别的节点的元素值
			if (bNode->generationLevel==targetNode->generationLevel)
			{
			cout << bNode->t;
			}
		}
}

template<class T>
inline void BinaryTree<T>::FindAllAncestor(T target)
{
	//target元素对应的目标节点
	BNode<T>* targetNode = NULL;
	//临时节点,用于遍历所有的树节点
	BNode<T>* node = root;
	//临时装载父节点的栈
	Stack<BNode<T>*>* stack = new 	Stack<BNode<T>*>();
	//用于装取后序遍历顺序的节点的栈
	Stack<BNode<T>*>* outstack = new 	Stack<BNode<T>*>();
	BNode<T>* temp = NULL;
	while (node != NULL || stack->getSize() > 0)
	{
		if (node != NULL)
		{
			//找到T元素对应的树节点
			if (node->t == target)
			{
				targetNode = node;
			}
			//父节点入栈
			stack->push(node);
			//节点入栈
			outstack->push(node);
			//临时节点指向弹出的父节点的右孩子
			node = node->rightChild;
		}
		else
		{
			//弹出父节点
			node = stack->pop();
			//临时节点指向弹出的父节点的左孩子
			node = node->leftChild;
		}
	}

	while (outstack->getSize() > 0) {
		//逆序打印
		BNode<T>* bNode = outstack->pop();
		//打印输出辈分级别小于target对应节点辈分级别的树节点的元素值
		//比如target节点的辈分为3,那么就要打印输出辈分为1或2的节点的值
		if (bNode->generationLevel < targetNode->generationLevel)
		{
			cout << bNode->t;
		}
	}

}

template<class T>
inline void BinaryTree<T>::destroyTree(BNode<T>* root)
{
	if (root!=nullptr)
	{ 
		//递归销毁左子树
		destroyTree(root->leftChild);
		//递归销毁右子树
		destroyTree(root->rightChild);
		//释放根节点
		delete root;
	}
}
template<class T>
inline BinaryTree<T>::~BinaryTree()
{
	this->destroyTree(root);
}

温馨提示:通用的模板类和普通的类不一样,不能.h和cpp文件分离实现,要在同一个文件中书写声明和实现,否则就会在编译的时候发生错误。常用的通用模板的命名规范是以.hpp结尾文件,这样别人一看就知道这是一个模板类了。
欢迎各位指出其中的不足!

家谱管理系统,主要用来管理家族成员的基本信息 1、确定整个程序的功能模块。实现程序的主界面,要对主界面的功能选择输入进行容错处理。 2、实现单个结点信息的录入。 3、对录入日期信息进行合法性检验。 4、采用改变字体颜色的方式突出显示主界面的功能项。 5、计算从出生日期到死亡日期的实际天数 6、若家谱树为空,则新建家谱树。实现成员节点的添加。基本功能中可以 强制要求所有成员不同名,即不考虑同名情况(符合小家族的实际情况)。 7、添加成员节点时,可以选择将新添加的节点作为整个家谱的上一代祖先, 或者将新添加的节点作为某个现有成员的孩子。 8、作为某个现有成员的孩子,根据给出的父节点的姓名将该结点添加到相 应位置,注意,针对某一父节点,添加第一个孩子和其它孩子的区别。 9、要求在孩子兄弟二叉树中按各个孩子的年龄进行排序。 10、将家谱树保存到二进制文件。注意,不能保存空白节点。 11、从文件读入家谱信息,重建孩子兄弟二叉树形式的家谱。 12.从文件中读出所有节点信息到一个数组中,然后按一年中生日的先后进 行快速排序。 13、按姓名查询家谱成员并显示该成员的各项信息。 14、给出某一成员的姓名,删除该成员和该成员的所有子孙。 15、成员信息的修改。信息修改时要给出选择界面让用户选择需要修改的信 息项。基本功能中可以限定不容许修改父亲姓名和本人姓名。对日期信 息进行修改时要进行检验。 16、实现层次递进的方式显示整个家谱,显示结果应该体现家谱树的结构。 17、按各种关键字进行查询,要求给出关键字选择界面,并显示符合查询条 件的节点信息。 18、信息统计基本要求包括:平均身高,平均寿命,男女成员各多少,平均 家庭人口数目(假定每个成员构成一个家庭,该家庭的家庭成员是指成 员本人和他的孩子,即家庭人口数=孩子数+1)。要给出统计项的选择界 面. 19、查询某一成员的所有直系亲属。 20、给出某一成员的所有嫡系祖先。 21、确定两人关系。若两人辈分不等,则应指出甲是乙的多少代长辈(晚辈), 甲是否是乙的直系长辈(晚辈),若辈分相同,则应指出是亲兄弟还是多 少代的堂兄弟。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陌意随影

您的鼓励是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值