IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 第95篇 C++数据结构(五)树 -> 正文阅读

[数据结构与算法]第95篇 C++数据结构(五)树

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

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.总结

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

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-28 12:04:59  更:2022-04-28 12:07:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:35:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码