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


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

  • 1.树的简介
    • 1.1.树的特点
    • 1.2.树的相关名词
    • 1.3.二叉树
    • 2.节点
  • 3.实现
    • 3.1.变量
    • 3.2.方法
  • 4.测试
    • 4.1.测试代码
    • 4.2.输出
  • 5.实现代码
  • 6.总结

有许多介绍树的博文,这里不做过多的介绍,本文注重于二叉树的相关操作,且实现的相当于是一颗有序二叉树。
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 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_len size_t 私有 树的节点数
removeCount = 0 int 私有 统计删除次数
3.2.方法 因为树的根节点是私有的,不支持外面直接访问,因此许多函数都是提供访问名,实际操作由类内定义的其他函数完成。
比如添加数据的函数add(_dataType value),递归实现的话需要传根节点,但是根节点是不给外面调用的,因此就写一个私有的add(_TreeNodePtr root, _dataType value),可传根节点。
方法名 返回类型 参数 属性 说明
Tree() - - 公有 缺省构造
Tree() - const Tree& t 公有 拷贝构造
operator = () Tree& const Tree& t 公有 =运算符重载
~Tree() - - 公有 析构
isEmpty() const bool - 公有 判断树是否为空
size() const size_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 #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 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 t2 = t; std::cout << "中序遍历迭代:"; t2.midorderTraversalIterate(); std::cout << "\n复制运算符测试:" << std::endl; Tree 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 #include #include #include template 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 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 =https://www.it610.com/article/= 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 =https://www.it610.com/article/= 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.总结 【数据结构|第95篇 C++数据结构(五)树】只是实现,节点的删除如果不明白的话,可以自己多按着代码写几次,当然想,个人看别人的代码没有很好的耐心,就上课的时候自己画画写写,在纸上模拟过程,怎么删才好,还是觉得自己想过的东西才会有更深的理解。

    推荐阅读