【c++回顾】1.5数据类型-树

终于到了非线性、层次关系的数据结构——树了,所有的数据结构里,最不熟悉的就是树和图,今天就先来好好回顾一下树。
在这不想写太多基础概念,因为概念上的东西百度一下就有,不再赘述。

看过基本概念后,我认为有三个地方值得注意:
1.树描述了非线性、层次性的数据
2.树具有递归特性,即树的子树与其结构类似。
3.树的数学表达,层次括号法

那么问题来了,树的具体应用背景是什么?之前的表,栈,队列都有着明确的对应的实际应用场景,树的应用背景我一时想不到,大概搜了一下,发现树的应用场景还是很多的:
公司组织架构:董事长-CXO-总监-经理-主管-员工;
中国行政区域划分:中国-省-市(县)-街道(小区)-门牌号;
汽车产品库:车-品牌-车系-配置。
(来自此处
此外最经典的应用是家谱的存储,突然想起来当年c++的课上也是用家谱的存储命题。

本来打算用类实现一个简单的链式二叉树,突然觉得一个结点只能由两个子节点好lowb,所以干脆用类实现一个任意叉树吧。
分析一下和二叉树的区别,我们发现任意叉树的某个节点的子节点个数应任意,所以可以用链表来存储一个节点的子节点。这里我们用vector作为子节点地址的容器。

实现的功能:

	Tree();//多叉树初始化
	~Tree();//多叉树销毁
	void AssignRootNode(Datatype data);//根节点赋值
	void AddChildNode(string father_name, Datatype data);//增加子节点
	void DeleteNode(string name);//删除节点(及其子树)
	void PrintTreeNodes();//打印树的所有节点数据
	void SearchKeyword(string keyword);//查找关键词
	void PrintTree();//按照括号层次法打印树

话不多说上代码:

// 用类实现一个链式任意叉树,用于存储家谱信息(姓名,性别,生日,配偶)
// 成员添加方式为指定父/母姓名

#include "pch.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#define Datatype Data
using namespace std;

//定义配偶信息结构体
struct Spouse
{
	string name;
	string gender;
	string birthday;
};
//定义家谱信息结构体
struct Data
{
	string name;//姓名,最多四个汉字
	string gender;//性别,一个汉字
	string birthday;//生日,八个字符,19970701,香港永远是中国不可分割的一部分
	Spouse* spouse;//采用结构体地址,可以判断是否已婚
};
//定义多叉树的节点结构体
struct Node
{
	Datatype data;//节点数据
	vector<Node*> child_nodes;//子节点地址列表
	Node* father_point;//父节点地址
};
//打印节点数据
void PrintNodeData(Node* point)
{
	cout << point->data.name << ' ' << point->data.gender << ' ' << point->data.birthday;
	if (point->data.spouse)cout << " 配偶:" << point->data.spouse->name << ' '
		<< point->data.spouse->gender << ' ' << point->data.spouse->birthday << ' ' << endl;
	else cout << " 无配偶" << endl;
}

//swap式删除数据
void DeleteVectorElement(vector<Node*> &vector, int i)
{
	if (i == vector.size() - 1)vector.pop_back();
	else {
		swap(vector[i], vector.back());
		vector.pop_back();
		swap(vector[i], vector.back());
	}
}

//定义多叉树类
class Tree
{
public:
	Tree();//多叉树初始化
	~Tree();//多叉树销毁
	void AssignRootNode(Datatype data);//根节点赋值
	void AddChildNode(string father_name, Datatype data);//增加子节点
	void DeleteNode(string name);//删除节点(及其子树)
	void PrintTreeNodes();//打印树的所有节点数据
	void SearchKeyword(string keyword);//查找关键词
	void PrintTree();//按照括号层次法打印树

private:
	void subPrintTree(Node* root_point);//打印函数的辅助函数,以实现递归
	Node* SearchNode(string name);//查找节点
	Node* GenerateNewNode(Datatype data, Node* father_point);//根据data生成新节点
	void GetAllPoints(vector<Node*> &point_list, Node* root_point);//获取所有节点地址
	void DeleteNode(Node* point);//销毁节点
	int node_number;//节点数
	Node* root_point;//根节点地址
};
//多叉树初始化
Tree::Tree()
{
	node_number = 0;
	root_point = NULL;
	cout << "【树已初始化】" << endl;
}
//多叉树销毁
Tree::~Tree()
{
	//获取所有节点地址
	vector<Node*> point_list;
	GetAllPoints(point_list, root_point);
	//遍历删除节点
	for (auto &point : point_list)
	{
		DeleteNode(point);
	}
	cout << "【树已销毁】" << endl;
}
//根节点赋值
void Tree::AssignRootNode(Datatype data)
{
	//申请内存
	Node* new_node = new Node;
	//data赋值
	new_node->data.name = data.name;
	new_node->data.gender = data.gender;
	new_node->data.birthday = data.birthday;
	new_node->data.spouse = data.spouse;
	//指针赋值
	new_node->father_point = NULL;
	new_node->child_nodes.push_back(NULL);
	//根节点赋值
	root_point = new_node;
	//修改总节点数
	node_number++;
}
//增加子节点
void Tree::AddChildNode(string father_name, Datatype data)
{
	//查找获取其地址
	Node* point = SearchNode(father_name);
	//如果没找到
	if (!point)return;
	//如果找到了,将其链至树上
	point->child_nodes.push_back(GenerateNewNode(data, point));
	//并修改树的总节点数
	node_number++;
}
//打印树的所有节点数据
void Tree::PrintTreeNodes()
{
	cout << "【打印所有节点】" << endl;
	//获取所有节点地址
	vector<Node*> all_points;
	GetAllPoints(all_points, root_point);
	//遍历打印
	for (auto point : all_points)PrintNodeData(point);
	cout << "【打印结束】" << endl;
}
//根据data生成新节点
Node* Tree::GenerateNewNode(Datatype data, Node* father_node)
{
	//申请内存
	Node* new_node = new Node;
	//data赋值
	new_node->data.name = data.name;
	new_node->data.gender = data.gender;
	new_node->data.birthday = data.birthday;
	new_node->data.spouse = data.spouse;
	//指针赋值
	new_node->father_point = father_node;
	new_node->child_nodes.push_back(NULL);
	//返回新指针
	return new_node;
}
/*深度优先,递归获取所有节点地址*/
//给出某(子)树的根节点,获取该(子)树的所有节点地址并存储在point_list中
//使用时应先初始化point_list并保证其为空
void Tree::GetAllPoints(vector<Node*> &point_list, Node* root_point)
{
	//只要root_point不为空就继续递归
	while (root_point)
	{
		//存储当前节点地址
		point_list.push_back(root_point);
		//深度优先,递归子节点,反向遍历,保证最后一个值为NULL
		vector<Node*>::reverse_iterator rit;
		for (rit = root_point->child_nodes.rbegin(); rit != root_point->child_nodes.rend(); rit++)
		{
			GetAllPoints(point_list, *rit);
		}
		return;
	}
}
/*销毁节点*/
//给出节点地址,销毁该节点,销毁时先删除spouse,再删除data
void Tree::DeleteNode(Node* point)
{
	if (point->data.spouse)delete point->data.spouse;
	delete &(point->data);
}
//查找结点
Node* Tree::SearchNode(string name)
{
	//获取所有节点地址
	vector<Node*> all_points;
	GetAllPoints(all_points, root_point);
	//遍历查找
	for (auto &point : all_points)
	{
		//如果找到该节点
		if (point->data.name == name)
		{
			return point;
		}
	}
	//如果未找到,则报错
	cout << "查无此人" << endl;
	return NULL;
}
//查找关键词,打印包含关键词的人员信息
void Tree::SearchKeyword(string keyword)
{
	//获取所有节点地址
	vector<Node*> all_points;
	GetAllPoints(all_points, root_point);
	//插flag以判断是否有找到结果
	bool flag = false;
	//遍历查找
	for (auto &point : all_points)
	{
		//可以将人员信息的所有字符串拼接在一起,以减小if条件判断的篇幅,
		//但是这样做可能导致拼接部分的误判断,因此不能采用这种写法
		/*string all_str = point->data.name + point->data.gender + point->data.birthday
			+ point->data.spouse->name + point->data.spouse->gender + point->data.spouse->birthday;*/

			//如果无配偶,则只查找本人信息
		if (!point->data.spouse)
		{
			if (point->data.name.find(keyword) != string::npos || point->data.gender.find(keyword) != string::npos || point->data.birthday.find(keyword) != string::npos)
			{
				//打印该节点
				PrintNodeData(point);
				//修改flag
				flag = true;
			}
		}
		//否则查找两人信息
		else
		{
			if (point->data.name.find(keyword) != string::npos || point->data.gender.find(keyword) != string::npos || point->data.birthday.find(keyword) != string::npos ||
				point->data.spouse->name.find(keyword) != string::npos || point->data.spouse->gender.find(keyword) != string::npos || point->data.spouse->birthday.find(keyword) != string::npos)
			{
				//打印该节点
				PrintNodeData(point);
				//修改flag
				flag = true;
			}
		}
	}
	//如果未找到,则报错
	if (!flag)cout << "【查找无结果】" << endl;
}
//删除节点(及其子树)
void Tree::DeleteNode(string name)
{
	//获取节点地址
	Node* point = SearchNode(name);
	//如果没找到,结束函数
	if (!point)return;
	//如果找到了,获取该子树所有节点
	vector<Node*> all_points;
	GetAllPoints(all_points, point);
	//记录父节点
	Node* father_point = point->father_point;
	//从父节点的子节点vector中删除该项
	//引用该vector
	vector<Node*> &vec = father_point->child_nodes;
	vector<Node*>::reverse_iterator rit;
	for (rit = vec.rbegin(); rit != vec.rend(); rit++)
	{
		if ((*rit)->data.name == name)
		{
			DeleteVectorElement(vec, vec.rend() - 1 - rit);
			//删除元素后迭代器失效,退出循环
			break;
		}
	}
	//遍历销毁
	//遍历删除节点
	for (auto &point : all_points)
	{
		DeleteNode(point);
	}
	cout << "【删除成功】" << endl;
}
/*括号层次法打印树辅助函数*/
//打印规则:
//递归开始时打印“(root_name”
//循环中有值则打印,第一项打印“(name”,后面的项打印“,(name”,空项打印“)”
void Tree::subPrintTree(Node* root_point)
{
	//只要root_point不为空就继续递归
	while (root_point)
	{
		cout << "(" << root_point->data.name;
		//深度优先,递归子节点,反向遍历,保证最后一个值为NULL
		vector<Node*>::reverse_iterator rit;
		for (rit = root_point->child_nodes.rbegin(); rit != root_point->child_nodes.rend(); rit++)
		{
			if (rit != root_point->child_nodes.rbegin() && rit != root_point->child_nodes.rend() - 1)
				cout << ",";
			subPrintTree(*rit);
		}
		return;
	}
	cout << ")";
}
//括号层次法打印树
void Tree::PrintTree()
{
	subPrintTree(root_point);
	cout << endl;
}

//用户输入节点数据
Datatype InputData()
{
	cout << "请输入人员信息(姓名,性别,生日):" << endl;
	Datatype new_data;
	cin >> new_data.name >> new_data.gender >> new_data.birthday;
	cout << "是否已婚?(请输入 是 或 否)" << endl;
	string married;
	cin >> married;
	if (married == "是")
	{
		cout << "请输入配偶信息(姓名,性别,生日):" << endl;
		Spouse* spouse = new Spouse;
		cin >> spouse->name >> spouse->gender >> spouse->birthday;
		new_data.spouse = spouse;
	}
	else new_data.spouse = NULL;
	return new_data;
}


//主函数测试各功能
int main()
{
	//初始化
	Tree tree;

	//根节点输入测试
	cout << "祖先:" << endl;
	tree.AssignRootNode(InputData());
	//子节点输入测试
	cout << "家庭成员人数:" << endl;
	int n;
	cin >> n;
	string father_name;
	for (int i = 0; i < n; i++)
	{
		cout << "待添加人员的父亲名字:" << endl;
		cin >> father_name;
		tree.AddChildNode(father_name, InputData());
	}
	//打印所有节点
	tree.PrintTreeNodes();

	//删除节点测试
	cout << "删除人员:" << endl;
	string name;
	cin >> name;
	tree.DeleteNode(name);
	tree.PrintTreeNodes();

	//关键词查找测试
	cout << "查找关键词:" << endl;
	string keyword;
	cin >> keyword;
	tree.SearchKeyword(keyword);

	//括号层次法打印测试
	tree.PrintTree();
	return 0;
}
/*
输入:
老爸 男 19500101
是
老妈 女 19510101
4
老爸
儿子 男 19800101
是
儿媳 女 19800101
老爸
大儿子 男 19790101
否
老爸
女儿 女 19850101
是
女婿 男 19840101
儿子
孙子 男 20000101
否
大儿子
女婿
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值