第95篇 C++数据结构(五)树

本文详细介绍了树的数据结构,特别是二叉树的概念,包括树的特点、相关名词解释以及二叉树的类型。接着,实现了一个C++模板类`Tree`,用于表示排序二叉树,包括节点的定义、添加、删除、替换、查找、遍历等操作,并提供了测试代码展示其实现功能。
摘要由CSDN通过智能技术生成

有许多介绍树的博文,这里不做过多的介绍,本文注重于二叉树的相关操作,且实现的相当于是一颗有序二叉树。

1.树的简介

树形结构是一种重要的非线性数据结构。其中树和二叉树最为常用,直观看来树是以分支关系定义的层次结构。树形结构是我们平时比较熟悉的,比如文件夹目录、公司组织关系等。在计算机领域也得到广泛的应用,编译程序就是以树来表示源程序的语法结构。

1.1.树的特点

树(tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。

   (1)每个节点有 0 或 多个 子节点;
   (2)没有父节点的节点称为根节点;
   (3)每一个非根节点有且仅有一个父节点;
   (4)除了根节点外,每个子节点可以分为多个不相交的子树;

1.2.树的相关名词

   (1)节点的度:一个节点含有的子树的个数(数节点的子节点数即可);
   (2)树的度:一棵树中,最大的节点的度称为树的度;
   (3)叶节点或终端节点:度为 0 的节点;
   (4)父节点:若一个节点含有子节点,则该节点称为其子节点的父节点;
   (5)子节点:一个节点含有的子树的根节点称为该节点的子节点;
   (6)兄弟节点:具有相同父节点的节点
   (7)节点的层次:从根节点开始定义,根为第 1 层,根的子节点为第 2 层,以此类推;
   (8)树的高度或深度:树种节点的最大层次
   (9)堂兄弟节点:父节点在同一层的节点互为堂兄弟
   (10)节点的祖先:从根到该节点所经分支上的所有点
   (11)子孙:以某节点为根的子树中任一节点都称为该节点的子孙
   (12)森林:由 m(m>0)棵互不相交的树的集合称为森林

1.3.二叉树

二叉树:每个节点最多含有两个子树的树称为二叉树
完全二叉树:对于一课二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数均已达最大值,且第d层所有节点从左向右连续地紧密排列;
平衡二叉树(AVL树):当且仅当任何两棵子树的高度差不大于1的二叉树;
排序二叉树(二叉查找树 Binary Search Tree, 也称二叉搜索树、有序二叉树);

2.节点

实现的是一颗排序二叉树,二叉树其实也可以用数组来表示,不过数组表示,对于任何形式的数据结构来说,插入删除之类的都麻烦,因此采用链表形式较好,在此定义一个节点:

template <typename _dataType>
struct TreeNode
{
	using _TreeNodePtr = TreeNode*;

	_dataType m_value;
	_TreeNodePtr m_leftChild;
	_TreeNodePtr m_rightChild;

	TreeNode() : m_value(_dataType()), m_leftChild(nullptr), m_rightChild(nullptr) {}
	TreeNode(_dataType value) : m_value(value), m_leftChild(nullptr), m_rightChild(nullptr) {}
	TreeNode(_dataType value, _TreeNodePtr leftChild, _TreeNodePtr rightChild) 
		: m_value(value), m_leftChild(leftChild), m_rightChild(rightChild) {}
};

节点包含内容为,节点数据,节点左孩子,节点右孩子,为了便于可能遇到的构造需求,这里定义三个构造函数。

3.实现

3.1.变量

因为实现的排序树支持插入重复数据,因此替换数据的时候有可能替换很多个,而替换是通过删除于添加完成,因此使用removeCount来统计删除次数,这样删除目标数据几次,就添加替换数据几次就好了。

变量名类型属性说明
m_root_TreeNodePtr私有根节点
m_lensize_t私有树的节点数
removeCount = 0int私有统计删除次数

3.2.方法

因为树的根节点是私有的,不支持外面直接访问,因此许多函数都是提供访问名,实际操作由类内定义的其他函数完成。
比如添加数据的函数add(_dataType value),递归实现的话需要传根节点,但是根节点是不给外面调用的,因此就写一个私有的add(_TreeNodePtr root, _dataType value),可传根节点。

方法名返回类型参数属性说明
Tree()--公有缺省构造
Tree()-const Tree& t公有拷贝构造
operator = ()Tree&const Tree& t公有=运算符重载
~Tree()--公有析构
isEmpty() constbool-公有判断树是否为空
size() constsize_t-公有返回节点数
add()bool_dataType value公有添加数据
remove()bool_dataType value公有删除数据
replace()bool_dataType target, _dataType value公有替换数据
search()bool_dataType value公有查找数据
clear()bool-公有清空数据
depth()size_t-公有返回树的深度
numberOfLeaves()size_t-公有计算叶子数
preorderTraversal()void-公有先序遍历递归实现
midorderTraversal()void-公有中序遍历递归实现
postorderTraversal()void-公有后序遍历递归实现
preorderTraversalIterate()void-公有先序遍历迭代实现
midorderTraversalIterate()void-公有中序遍历迭代实现
postorderTraversalIterate()void-公有后序遍历迭代实现
levelTraversal()void-公有层次遍历
add()bool_TreeNodePtr& root, _dataType value私有添加数据
remove()bool_TreeNodePtr& root, _dataType value私有删除数据
changeNodeParent()_TreeNodePtr&_TreeNodePtr& rNode私有返回需要删除的节点的父节点
replace()bool_TreeNodePtr root, _dataType target, _dataType value私有替换数据
search()bool_TreeNodePtr root, _dataType value私有查找数据
copy()void_TreeNodePtr& leftRoot, _TreeNodePtr rightRoot私有复制数据
clear()void_TreeNodePtr& root私有清除数据
depth()size_t_TreeNodePtr root私有计算深度
numberOfLeaves()size_t_TreeNodePtr root私有计算叶子节点数
preorderTraversal()void_TreeNodePtr root私有先序遍历
midorderTraversal()void_TreeNodePtr root私有中序遍历
postorderTraversal()void_TreeNodePtr root私有后序遍历

4.测试

4.1.测试代码

#include <iostream>

#include "Tree.h"

void treeTest();
//11 9 10 4 5 3 14 13 15 11 9 10 4 5 3 14 13 15
int main()
{
	treeTest();

	return 0;
}

void treeTest()
{
	std::cout << "\n构造测试:" << std::endl;
	Tree<int> t;

	int num(0);
	int c = 18;
	while (c-- > 0) {
		std::cin >> num;
		t.add(num);
	}
	std::cout << "先序遍历迭代:";
	t.preorderTraversalIterate();
	std::cout << "先序遍历递归:";
	t.preorderTraversal();
	std::cout << std::endl;

	std::cout << "中序遍历迭代:";
	t.midorderTraversalIterate();
	std::cout << "中序遍历递归:";
	t.midorderTraversal();
	std::cout << std::endl;

	std::cout << "后序遍历迭代:";
	t.postorderTraversalIterate();
	std::cout << "后序遍历递归:";
	t.postorderTraversal();
	std::cout << std::endl;

	std::cout << "层次遍历:";
	t.levelTraversal();

	std::cout << "树的深度:" << t.depth() << std::endl;

	std::cout << "树的叶子数:" << t.numberOfLeaves() << std::endl;

	std::cout << "树的节点数:" << t.size() << std::endl;

	std::cout << "\n拷贝构造函数测试:" << std::endl;
	Tree<int> t2 = t;
	std::cout << "中序遍历迭代:";
	t2.midorderTraversalIterate();

	std::cout << "\n复制运算符测试:" << std::endl;
	Tree<int> t3;
	t3 = t2;
	std::cout << "中序遍历迭代:";
	t3.midorderTraversalIterate();

	std::cout << "\n添加数据12:" << std::endl;
	t.add(12);
	std::cout << "中序遍历迭代:";
	t.midorderTraversalIterate();

	std::cout << "\n删除数据11:" << std::endl;
	t.remove(11);
	std::cout << "中序遍历迭代:";
	t.midorderTraversalIterate();

	std::cout << "\n将数据13替换为11:" << std::endl;
	t.replace(13, 11);
	std::cout << "中序遍历迭代:";
	t.midorderTraversalIterate();

	std::cout << "\n查找数据13:" << std::endl;
	if (t.search(13)) {
		std::cout << "13存在!" << std::endl;
	}
	else {
		std::cout << "13不存在!" << std::endl;
	}

	std::cout << "\n查找数据11:" << std::endl;
	if (t.search(11)) {
		std::cout << "11存在!" << std::endl;
	}
	else {
		std::cout << "11不存在!" << std::endl;
	}

	std::cout << "\n判断树是否为空:" << std::endl;
	if (t.isEmpty()) {
		std::cout << "树没有数据!" << std::endl;
	}
	else {
		std::cout << "树有数据!" << std::endl;
	}

	std::cout << "\n清空:" << std::endl;
	t.clear();
	if (t.isEmpty()) {
		std::cout << "树没有数据!" << std::endl;
	}
	else {
		std::cout << "树有数据!" << std::endl;
	}
}

4.2.输出

构造测试:
11 9 10 4 5 3 14 13 15 11 9 10 4 5 3 14 13 15
先序遍历迭代:11, 9, 4, 3, 3, 5, 4, 5, 10, 9, 10, 14, 13, 11, 13, 15, 14, 15,
先序遍历递归:11, 9, 4, 3, 3, 5, 4, 5, 10, 9, 10, 14, 13, 11, 13, 15, 14, 15,
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 13, 13, 14, 14, 15, 15,
中序遍历递归:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 13, 13, 14, 14, 15, 15,
后序遍历迭代:3, 3, 4, 5, 5, 4, 9, 10, 10, 9, 11, 13, 13, 14, 15, 15, 14, 11,
后序遍历递归:3, 3, 4, 5, 5, 4, 9, 10, 10, 9, 11, 13, 13, 14, 15, 15, 14, 11,
层次遍历:11, 9, 14, 4, 10, 13, 15, 3, 5, 9, 10, 11, 13, 14, 15, 3, 4, 5,
树的深度:5
树的叶子数:9
树的节点数:18

拷贝构造函数测试:
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 13, 13, 14, 14, 15, 15,

复制运算符测试:
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 13, 13, 14, 14, 15, 15,

添加数据12:
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15,

删除数据11:
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 12, 13, 13, 14, 14, 15, 15,

将数据13替换为11:
中序遍历迭代:3, 3, 4, 4, 5, 5, 9, 9, 10, 10, 11, 11, 12, 14, 14, 15, 15,

查找数据13:
13不存在!

查找数据11:
11存在!

判断树是否为空:
树有数据!

清空:
树没有数据!

5.实现代码

#pragma once
#ifndef _TREE_H_
#define _TREE_H_

#include <cassert>
#include <iostream>
#include <stack>
#include <queue>

template <typename _dataType>
struct TreeNode
{
	using _TreeNodePtr = TreeNode*;

	_dataType m_value;
	_TreeNodePtr m_leftChild;
	_TreeNodePtr m_rightChild;

	TreeNode() : m_value(_dataType()), m_leftChild(nullptr), m_rightChild(nullptr) {}
	TreeNode(_dataType value) : m_value(value), m_leftChild(nullptr), m_rightChild(nullptr) {}
	TreeNode(_dataType value, _TreeNodePtr leftChild, _TreeNodePtr rightChild) 
		: m_value(value), m_leftChild(leftChild), m_rightChild(rightChild) {}
};

template <typename _dataType>
class Tree
{
	using _TreeNodePtr = TreeNode<_dataType>*;
public:
	Tree() : m_root(nullptr), m_len(0) {}

	Tree(const Tree& t)
	{
		copy(this->m_root, t.m_root);
		m_len = t.m_len;
	}

	Tree& operator = (const Tree& t)
	{
		if (!this->isEmpty()) {
			this->clear();
		}

		copy(this->m_root, t.m_root);
		m_len = t.m_len;

		return *this;
	}

	~Tree()
	{
		clear();
	}

	bool isEmpty() const
	{
		return m_root == nullptr;
	}

	size_t size() const
	{
		return m_len;
	}

	bool add(_dataType value) //添加数据
	{
		return add(m_root, value);
	}

	bool remove(_dataType value) //删除数据
	{
		return remove(m_root, value);
	}

	bool replace(_dataType target, _dataType value) //替换数据
	{
		return replace(m_root, target, value);
	}

	bool search(_dataType value)
	{
		return search(m_root, value);
	}

	bool clear()
	{
		clear(m_root);

		return true;
	}

	size_t depth()
	{
		return depth(m_root);
	}

	size_t numberOfLeaves()
	{
		return numberOfLeaves(m_root);
	}

	void preorderTraversal()
	{
		preorderTraversal(m_root);
	}

	void midorderTraversal()
	{
		midorderTraversal(m_root);
	}

	void postorderTraversal()
	{
		postorderTraversal(m_root);
	}

	void preorderTraversalIterate()
	{
		std::stack<_TreeNodePtr> treeNodeStack;
		_TreeNodePtr root = m_root;
		while (root || !treeNodeStack.empty()) {
			if (root) {
				std::cout << root->m_value << ", ";
				treeNodeStack.push(root);
				root = root->m_leftChild;
			}
			else {
				root = treeNodeStack.top();
				treeNodeStack.pop();
				root = root->m_rightChild;
			}
		}
		std::cout << std::endl;
	}

	void midorderTraversalIterate()
	{
		std::stack<_TreeNodePtr> treeNodeStack;
		_TreeNodePtr root = m_root;
		while (root || !treeNodeStack.empty()) {
			if (root) {
				treeNodeStack.push(root);
				root = root->m_leftChild;
			}
			else {
				root = treeNodeStack.top();
				treeNodeStack.pop();
				std::cout << root->m_value << ", ";
				root = root->m_rightChild;
			}
		}
		std::cout << std::endl;
	}

	void postorderTraversalIterate()
	{
		std::stack<_TreeNodePtr> treeNodeStack;
		_TreeNodePtr root = m_root;
		_TreeNodePtr record = nullptr;
		while (root || !treeNodeStack.empty()) {
			if (root) {
				treeNodeStack.push(root);
				root = root->m_leftChild;
			}
			else {
				root = treeNodeStack.top();
				if (root->m_rightChild && root->m_rightChild != record) {
					root = root->m_rightChild;
				}
				else {
					treeNodeStack.pop();
					std::cout << root->m_value << ", ";
					record = root;
					root = nullptr;
				}
			}
		}
		std::cout << std::endl;
	}

	void levelTraversal()
	{
		std::queue<_TreeNodePtr> treeNodeStack;
		_TreeNodePtr root = m_root;
		treeNodeStack.push(root);
		while (!treeNodeStack.empty()) {
			root = treeNodeStack.front();
			treeNodeStack.pop();
			std::cout << root->m_value << ", ";
			if (root->m_leftChild) {
				treeNodeStack.push(root->m_leftChild);
			}
			if (root->m_rightChild) {
				treeNodeStack.push(root->m_rightChild);
			}
		}
		std::cout << std::endl;
	}

private:
	_TreeNodePtr m_root;//根节点
	size_t m_len;
	int removeCount = 0;//统计删除次数

	bool add(_TreeNodePtr& root, _dataType value)
	{
		if (root) {
			if (value >= root->m_value) {
				return add(root->m_rightChild, value);
			}
			else {
				return add(root->m_leftChild, value);
			}
		}
		else {
			root = new TreeNode<_dataType>(value);
			m_len++;
		}

		return true;
	}

	bool remove(_TreeNodePtr& root, _dataType value)
	{
		if (root) {
			if (value == root->m_value) {
				removeCount++;
				if (root->m_rightChild) {
					if (root->m_rightChild->m_leftChild) {
						_TreeNodePtr cNodeParent = changeNodeParent(root->m_rightChild);

						_TreeNodePtr cNode = cNodeParent->m_leftChild;
						std::swap(root->m_value, cNode->m_value);
						cNodeParent->m_leftChild = cNode->m_rightChild;

						delete cNode;
						cNode = nullptr;

						return (remove(root, value) || true);
					}
					else {
						_TreeNodePtr r = root;
						root->m_rightChild->m_leftChild = root->m_leftChild;
						root = root->m_rightChild;

						delete r;
						r = nullptr;

						return (remove(root, value) || true);
					}
				}
				else if (root->m_leftChild) {
					_TreeNodePtr r = root;
					root = root->m_leftChild;

					delete r;
					r = nullptr;

					return true;
				}
				else {
					delete root;
					root = nullptr;

					return true;
				}
			}
			else if (value > root->m_value) {
				return remove(root->m_rightChild, value);
			}
			else {
				return remove(root->m_leftChild, value);
			}
		}
		else {
			return false;
		}
	}

	_TreeNodePtr& changeNodeParent(_TreeNodePtr& rNode)
	{
		if (rNode->m_leftChild->m_leftChild) {
			return changeNodeParent(rNode->m_leftChild);
		}
		else {
			return rNode;
		}
	}

	bool replace(_TreeNodePtr root, _dataType target, _dataType value)
	{
		if (target == value) {
			return false;
		}

		removeCount = 0;
		remove(target);
		if (removeCount == 0) {
			return false;
		}

		while (removeCount-- > 0) {
			add(value);
		}

		return true;
	}

	bool search(_TreeNodePtr root, _dataType value)
	{
		if (root) {
			if (value == root->m_value) {
				return true;
			}
			else if (value > root->m_value) {
				return search(root->m_rightChild, value);
			}
			else {
				return search(root->m_leftChild, value);
			}
		}
		else {
			return false;
		}
	}

	void copy(_TreeNodePtr& leftRoot, _TreeNodePtr rightRoot)
	{
		if (rightRoot) {
			leftRoot = new TreeNode<_dataType>(rightRoot->m_value);
			copy(leftRoot->m_leftChild, rightRoot->m_leftChild);
			copy(leftRoot->m_rightChild, rightRoot->m_rightChild);
		}
	}

	void clear(_TreeNodePtr& root)
	{
		if (root) {
			clear(root->m_leftChild);
			clear(root->m_rightChild);
			delete root;
			root = nullptr;
		}
	}

	size_t depth(_TreeNodePtr root)
	{
		if (root) {
			size_t leftDepth = depth(root->m_leftChild);
			size_t rightDepth = depth(root->m_rightChild);

			return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;

		}
		else {
			return 0;
		}
	}

	size_t numberOfLeaves(_TreeNodePtr root)
	{
		if (root) {
			if (root->m_leftChild && root->m_rightChild) {
				return numberOfLeaves(root->m_leftChild) + numberOfLeaves(root->m_rightChild);
			}
			else if (root->m_leftChild) {
				return numberOfLeaves(root->m_leftChild);
			}
			else if (root->m_rightChild) {
				return numberOfLeaves(root->m_rightChild);
			}
			else {
				return 1;
			}
		}
		else {
			return 0;
		}
	}

	void preorderTraversal(_TreeNodePtr root)
	{
		if (root) {
			std::cout << root->m_value << ", ";
			preorderTraversal(root->m_leftChild);
			preorderTraversal(root->m_rightChild);
		}
	}

	void midorderTraversal(_TreeNodePtr root)
	{
		if (root) {
			midorderTraversal(root->m_leftChild);
			std::cout << root->m_value << ", ";
			midorderTraversal(root->m_rightChild);
		}
	}
	void postorderTraversal(_TreeNodePtr root)
	{
		if (root) {
			postorderTraversal(root->m_leftChild);
			postorderTraversal(root->m_rightChild);
			std::cout << root->m_value << ", ";
		}
	}
};

#endif //_TREE_H_

6.总结

只是实现,节点的删除如果不明白的话,可以自己多按着代码写几次,当然想,个人看别人的代码没有很好的耐心,就上课的时候自己画画写写,在纸上模拟过程,怎么删才好,还是觉得自己想过的东西才会有更深的理解。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大唐不良猿

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值