当前位置:   article > 正文

数据结构与算法学习笔记七--二叉树(C++)

数据结构与算法学习笔记七--二叉树(C++)

目录

前言

1.树的定义和基本术语

2.二叉树

1.二叉树的定义

2.二叉树的性质

3.二叉树的存储结构

1.二叉树链表存储表示

2.二叉树链表常用操作

1.定义

2.先序遍历二叉树

3.中序遍历二叉树

4.后序遍历二叉树

5.判断二叉树是否为空

6.创建二叉树

7.求二叉树深度

8.返回二叉树根节点

9.求二叉树的节点个数

10.完整代码

3.遍历二叉树和线索二叉树

1.遍历二叉树

1.先序遍历二叉树

2.中序遍历二叉树

3.后序遍历二叉树

2.线索二叉树

1.定义

2.中序线索化函数

3.中序遍历线索二叉树

4.创建线索二叉树

5.完整代码


前言

   这篇文章介绍数据结构中的树和二叉树

1.树的定义和基本术语

        在计算机科学中,树(Tree)是一种抽象数据类型,用于模拟具有树状结构特征的数据集合。树是由一组节点(Nodes)和连接这些节点的边(Edges)组成的,其中一个节点被指定为根节点(Root),其余节点分层组织成子树(Subtrees)。树的每个节点除了根节点外,都恰好连接到一条来自另一个节点的边,这个节点被称为其父节点(Parent Node)。每个节点可以连接到零个或多个子节点,子节点的节点被称为父节点的孩子节点(Child Node)。        

        树的定义如下:

  1. 根节点(Root):树中的唯一顶层节点,没有父节点。
  2. 边(Edge):连接两个节点的线段,表示节点之间的关联关系。
  3. 节点(Node):树中的基本元素,每个节点可以包含一个或多个数据项,以及指向其子节点的指针。
  4. 父节点(Parent Node):拥有一个或多个子节点的节点。
  5. 孩子节点(Child Node): 一个节点的直接后继节点。
  6. 叶子节点(Leaf Node):没有子节点的节点称为叶子节点,又称为终端节点。
  7. 深度(Depth):从根节点到该节点的唯一路径上的边的数量。
  8. 高度(Height):从一个节点到其深度最大的叶子节点的路径上的边的数量。
  9. 子树(Subtree): 树中的任意一个节点以及它的后代节点构成的子集,形成一个新的树。

        树的定义为我们提供了一种非常有用的数据结构,可用于表示层次化数据,例如文件系统、组织结构、家谱等等。树也是许多重要算法和数据结构的基础,例如二叉搜索树、堆、图等。

2.二叉树

1.二叉树的定义

        二叉树是一种树型结构,它的特点是每个节点至多只有两颗子树,并且二叉树的子树有左右之分,其次序不能任意颠倒。

2.二叉树的性质

1.在二叉树的第i层上,最多可以有2^(i-1)个节点

2.深度为k的二叉树至多有2^k-1个节点

3.对任何一二叉树T,如果其终端节点数为n0,度为2的节点数为n2,则n0 = n2+1

3.二叉树的存储结构

1.二叉树链表存储表示

        二叉树一般使用二叉链表表示。

2.二叉树链表常用操作

1.定义

        我们使用data表示二叉树数据域,leftChild和rightChild表示二叉树的左右节点。

        因此二叉树定义如下:

  1. // 二叉树节点的数据类型
  2. typedef char TElementType;
  3. // 二叉树结点的定义
  4. typedef struct BiTNode {
  5. TElementType data;
  6. struct BiTNode * leftChild, * rightChild;
  7. } BiTNode, * BiTree;
2.先序遍历二叉树
  1. // 先序遍历二叉树
  2. void preOrderTraverse(BiTree tree) {
  3. if (tree != nullptr) {
  4. cout << tree->data << " "; // 访问根节点
  5. preOrderTraverse(tree->leftChild); // 先序遍历左子树
  6. preOrderTraverse(tree->rightChild); // 先序遍历右子树
  7. }
  8. }
3.中序遍历二叉树
  1. // 中序遍历二叉树
  2. void inOrderTraverse(BiTree tree) {
  3. if (tree != nullptr) {
  4. inOrderTraverse(tree->leftChild); // 中序遍历左子树
  5. cout << tree->data << " "; // 访问根节点
  6. inOrderTraverse(tree->rightChild); // 中序遍历右子树
  7. }
  8. }
4.后序遍历二叉树
  1. // 后序遍历二叉树
  2. void postOrderTraverse(BiTree tree) {
  3. if (tree != nullptr) {
  4. postOrderTraverse(tree->leftChild); // 后序遍历左子树
  5. postOrderTraverse(tree->rightChild); // 后序遍历右子树
  6. cout << tree->data << " "; // 访问根节点
  7. }
  8. }
5.判断二叉树是否为空
  1. // 判断二叉树是否为空
  2. bool biTreeEmpty(BiTree tree) {
  3. return tree == nullptr;
  4. }
6.创建二叉树
  1. // 创建二叉树T
  2. void createBiTree(BiTree &tree) {
  3. TElementType data;
  4. cin >> data;
  5. if (data == '#') { // '#'表示空节点
  6. tree = nullptr;
  7. } else {
  8. tree = new BiTNode{data, nullptr, nullptr};
  9. createBiTree(tree->leftChild); // 创建左子树
  10. createBiTree(tree->rightChild); // 创建右子树
  11. }
  12. }
7.求二叉树深度
  1. // 返回二叉树的深度
  2. int biTreeDepth(BiTree tree) {
  3. if (tree == nullptr) {
  4. return 0;
  5. } else {
  6. int leftDepth = biTreeDepth(tree->leftChild); // 左子树的深度
  7. int rightDepth = biTreeDepth(tree->rightChild); // 右子树的深度
  8. return max(leftDepth, rightDepth) + 1; // 返回左右子树深度的最大值加1
  9. }
  10. }
8.返回二叉树根节点
  1. // 返回二叉树的根节点
  2. BiTNode* Root(BiTree tree) {
  3. return tree;
  4. }
9.求二叉树的节点个数
  1. // 返回二叉树的节点个数
  2. int biTreeSize(BiTree tree) {
  3. if (tree == nullptr) {
  4. return 0;
  5. } else {
  6. return biTreeSize(tree->leftChild) + biTreeSize(tree->rightChild) + 1;
  7. }
  8. }
10.完整代码
  1. #include <iostream>
  2. #include <queue>
  3. using namespace std;
  4. typedef char TElementType;
  5. // 二叉树节点的定义
  6. typedef struct BiTNode {
  7. TElementType data;
  8. struct BiTNode * leftChild, * rightChild;
  9. } BiTNode, * BiTree;
  10. // 先序遍历二叉树
  11. void preOrderTraverse(BiTree tree) {
  12. if (tree != nullptr) {
  13. cout << tree->data << " "; // 访问根节点
  14. preOrderTraverse(tree->leftChild); // 先序遍历左子树
  15. preOrderTraverse(tree->rightChild); // 先序遍历右子树
  16. }
  17. }
  18. // 中序遍历二叉树
  19. void inOrderTraverse(BiTree tree) {
  20. if (tree != nullptr) {
  21. inOrderTraverse(tree->leftChild); // 中序遍历左子树
  22. cout << tree->data << " "; // 访问根节点
  23. inOrderTraverse(tree->rightChild); // 中序遍历右子树
  24. }
  25. }
  26. // 后序遍历二叉树
  27. void postOrderTraverse(BiTree tree) {
  28. if (tree != nullptr) {
  29. postOrderTraverse(tree->leftChild); // 后序遍历左子树
  30. postOrderTraverse(tree->rightChild); // 后序遍历右子树
  31. cout << tree->data << " "; // 访问根节点
  32. }
  33. }
  34. // 判断二叉树是否为空
  35. bool biTreeEmpty(BiTree tree) {
  36. return tree == nullptr;
  37. }
  38. // 创建二叉树T
  39. void createBiTree(BiTree &tree) {
  40. TElementType data;
  41. cin >> data;
  42. if (data == '#') { // '#'表示空节点
  43. tree = nullptr;
  44. } else {
  45. tree = new BiTNode{data, nullptr, nullptr};
  46. createBiTree(tree->leftChild); // 创建左子树
  47. createBiTree(tree->rightChild); // 创建右子树
  48. }
  49. }
  50. // 返回二叉树的深度
  51. int biTreeDepth(BiTree tree) {
  52. if (tree == nullptr) {
  53. return 0;
  54. } else {
  55. int leftDepth = biTreeDepth(tree->leftChild); // 左子树的深度
  56. int rightDepth = biTreeDepth(tree->rightChild); // 右子树的深度
  57. return max(leftDepth, rightDepth) + 1; // 返回左右子树深度的最大值加1
  58. }
  59. }
  60. // 返回二叉树的根节点
  61. BiTNode* Root(BiTree tree) {
  62. return tree;
  63. }
  64. // 返回二叉树的节点个数
  65. int biTreeSize(BiTree tree) {
  66. if (tree == nullptr) {
  67. return 0;
  68. } else {
  69. return biTreeSize(tree->leftChild) + biTreeSize(tree->rightChild) + 1;
  70. }
  71. }
  72. // 查找以某个节点为根的子树中的最小节点
  73. BiTree findMin(BiTree tree) {
  74. while (tree->leftChild != nullptr) {
  75. tree = tree->leftChild;
  76. }
  77. return tree;
  78. }
  79. // 删除某个节点
  80. BiTree deleteNode(BiTree tree, TElementType key) {
  81. if (tree == nullptr) {
  82. return nullptr;
  83. }
  84. if (key < tree->data) { // 要删除的节点在左子树中
  85. tree->leftChild = deleteNode(tree->leftChild, key);
  86. } else if (key > tree->data) { // 要删除的节点在右子树中
  87. tree->rightChild = deleteNode(tree->rightChild, key);
  88. } else { // 找到要删除的节点
  89. // 如果节点是叶子节点或者只有一个子节点
  90. if (tree->leftChild == nullptr) {
  91. BiTree temp = tree->rightChild;
  92. delete tree;
  93. return temp;
  94. } else if (tree->rightChild == nullptr) {
  95. BiTree temp = tree->leftChild;
  96. delete tree;
  97. return temp;
  98. }
  99. // 如果节点有两个子节点,用右子树的最小节点替换当前节点
  100. BiTree temp = findMin(tree->rightChild);
  101. tree->data = temp->data;
  102. tree->rightChild = deleteNode(tree->rightChild, temp->data);
  103. }
  104. return tree;
  105. }
  106. // 查找二叉树中的节点
  107. void value(BiTree tree, TElementType & element) {
  108. if (tree != nullptr && tree->data == element) {
  109. element = tree->data;
  110. }
  111. }
  112. // 给节点赋值
  113. void assign(BiTree tree, TElementType & element, TElementType value) {
  114. if (tree != nullptr && tree->data == element) {
  115. tree->data = value;
  116. }
  117. }
  118. // 返回非根节点
  119. void parent(BiTree tree, TElementType & element) {
  120. if (tree == nullptr) {
  121. return;
  122. }
  123. queue<BiTree> q;
  124. q.push(tree);
  125. while (!q.empty()) {
  126. BiTree front = q.front();
  127. q.pop();
  128. if (front->leftChild != nullptr && front->leftChild->data == element) {
  129. element = front->data;
  130. return;
  131. }
  132. if (front->rightChild != nullptr && front->rightChild->data == element) {
  133. element = front->data;
  134. return;
  135. }
  136. if (front->leftChild != nullptr) {
  137. q.push(front->leftChild);
  138. }
  139. if (front->rightChild != nullptr) {
  140. q.push(front->rightChild);
  141. }
  142. }
  143. }
  144. // 返回左节点
  145. void leftChild(BiTree tree, TElementType & leftElement) {
  146. if (tree != nullptr && tree->leftChild != nullptr) {
  147. leftElement = tree->leftChild->data;
  148. }
  149. }
  150. // 返回右节点节点
  151. void rightChild(BiTree tree, TElementType & rightElement) {
  152. if (tree != nullptr && tree->rightChild != nullptr) {
  153. rightElement = tree->rightChild->data;
  154. }
  155. }
  156. // 返回做左兄弟节点
  157. void leftSibling(BiTree tree, TElementType & leftSiblingElement) {
  158. parent(tree, tree->data);
  159. if (tree->leftChild != nullptr) {
  160. leftChild(tree->leftChild, leftSiblingElement);
  161. }
  162. }
  163. // 返回做右兄弟节点
  164. void rightSibling(BiTree tree, TElementType & rightSiblingElement) {
  165. parent(tree, tree->data);
  166. if (tree->rightChild != nullptr) {
  167. rightChild(tree->rightChild, rightSiblingElement);
  168. }
  169. }
  170. //插入节点
  171. void insertChild(BiTree tree, TElementType p, TElementType LR, BiTree c) {
  172. queue<BiTree> q;
  173. q.push(tree);
  174. while (!q.empty()) {
  175. BiTree front = q.front();
  176. q.pop();
  177. if (front->data == p) {
  178. if (LR == 'L') {
  179. front->leftChild = c;
  180. } else {
  181. front->rightChild = c;
  182. }
  183. return;
  184. }
  185. if (front->leftChild != nullptr) {
  186. q.push(front->leftChild);
  187. }
  188. if (front->rightChild != nullptr) {
  189. q.push(front->rightChild);
  190. }
  191. }
  192. }
  193. // 层序遍历二叉树,对每个节点调用函数visit一次且仅一次
  194. void leveOrderTraverse(BiTree T) {
  195. if (T == nullptr) {
  196. return;
  197. }
  198. queue<BiTree> q;
  199. q.push(T);
  200. while (!q.empty()) {
  201. BiTree front = q.front();
  202. cout << front->data << " ";
  203. q.pop();
  204. if (front->leftChild != nullptr) {
  205. q.push(front->leftChild);
  206. }
  207. if (front->rightChild != nullptr) {
  208. q.push(front->rightChild);
  209. }
  210. }
  211. }
  212. // 层序遍历二叉树
  213. void levelOrderTraverse(BiTree tree) {
  214. if (tree == nullptr) {
  215. return;
  216. }
  217. queue<BiTree> q;
  218. q.push(tree);
  219. while (!q.empty()) {
  220. BiTree front = q.front();
  221. cout << front->data << " ";
  222. q.pop();
  223. if (front->leftChild != nullptr) {
  224. q.push(front->leftChild);
  225. }
  226. if (front->rightChild != nullptr) {
  227. q.push(front->rightChild);
  228. }
  229. }
  230. }
  231. // 测试二叉树操作
  232. void testBitTree() {
  233. // 创建二叉树
  234. BiTree root = nullptr;
  235. cout << "请输入二叉树的先序遍历序列,空节点用#表示:" << endl;
  236. createBiTree(root);
  237. // 先序遍历
  238. cout << "先序遍历结果:";
  239. preOrderTraverse(root);
  240. cout << endl;
  241. // 中序遍历
  242. cout << "中序遍历结果:";
  243. inOrderTraverse(root);
  244. cout << endl;
  245. // 后序遍历
  246. cout << "后序遍历结果:";
  247. postOrderTraverse(root);
  248. cout << endl;
  249. // 释放内存
  250. delete root;
  251. }
  252. int main() {
  253. // 创建二叉树
  254. BiTree root = nullptr;
  255. cout << "请输入二叉树的先序遍历序列,空节点用#表示:" << endl;
  256. createBiTree(root);
  257. // 先序遍历
  258. cout << "先序遍历结果:";
  259. preOrderTraverse(root);
  260. cout << endl;
  261. // 中序遍历
  262. cout << "中序遍历结果:";
  263. inOrderTraverse(root);
  264. cout << endl;
  265. // 后序遍历
  266. cout << "后序遍历结果:";
  267. postOrderTraverse(root);
  268. cout << endl;
  269. // 层序遍历
  270. cout << "层序遍历结果:";
  271. levelOrderTraverse(root);
  272. cout << endl;
  273. // 二叉树深度
  274. cout << "二叉树深度:" << biTreeDepth(root) << endl;
  275. // 二叉树节点个数
  276. cout << "二叉树节点个数:" << biTreeSize(root) << endl;
  277. // 删除节点
  278. TElementType key;
  279. cout << "请输入要删除的节点值:" << endl;
  280. cin >> key;
  281. root = deleteNode(root, key);
  282. // 层序遍历
  283. cout << "删除节点后的层序遍历结果:";
  284. levelOrderTraverse(root);
  285. cout << endl;
  286. // 释放内存
  287. delete root;
  288. return 0;
  289. }

       控制台输入信息:ABD##E##CF##G##

        控制台打印信息如下:

3.遍历二叉树和线索二叉树

1.遍历二叉树

        这里遍历二叉树有先序遍历二叉树、中序遍历二叉树、后序遍历二叉树三种方法。

1.先序遍历二叉树

        若二叉树为空,则空操作;否则

1.访问根节点

2.先序遍历左子树

3.先序遍历后子树

2.中序遍历二叉树

        若二叉树为空,则空操作;否则

1..中序遍历左子树

2.访问根节点

3.中序遍历右子树

3.后序遍历二叉树

        若二叉树为空,则空操作;否则

1.后序遍历左子树

2.后序遍历右子树

3.访问根节点

2.线索二叉树

        线索二叉树是对普通二叉树进行改进的一种数据结构,它的主要目的是提高对二叉树的遍历效率。在普通二叉树中,为了实现中序遍历等操作,需要使用递归或者栈来实现,而线索二叉树则通过添加额外的线索信息,使得在遍历过程中能够直接找到下一个或上一个节点,从而减少了递归或栈的使用。

        线索二叉树的节点结构通常包含原有的数据域,以及左右子树的指针,同时还包含了指向前驱节点和后继节点的线索指针。这些线索指针可以用来指向前驱节点或后继节点,从而实现在遍历过程中的快速定位。

        线索二叉树分为前序线索二叉树、中序线索二叉树和后序线索二叉树。其中,中序线索二叉树应用最为广泛,因为中序线索二叉树能够实现对二叉树的中序遍历操作,并且在遍历过程中不需要使用递归或者栈,从而提高了遍历的效率。

        总的来说,线索二叉树是通过添加额外的线索信息,使得在遍历过程中能够直接找到下一个或上一个节点,从而减少了递归或栈的使用,提高了对二叉树的遍历效率。

1.定义

  1. // 二叉树节点的定义
  2. typedef char TElementType;
  3. // 线索二叉树节点的定义
  4. typedef struct ThreadNode {
  5. TElementType data;
  6. struct ThreadNode * leftChild, * rightChild;
  7. int ltag, rtag; // 线索标志,0表示孩子,1表示线索
  8. } ThreadNode, * ThreadTree;

2.中序线索化函数

  1. // 中序线索化函数
  2. void inThread(ThreadTree &p, ThreadNode* &pre) {
  3. if (p != nullptr) {
  4. inThread(p->leftChild, pre); // 左子树线索化
  5. if (p->leftChild == nullptr) { // 左孩子为空,建立前驱线索
  6. p->leftChild = pre;
  7. p->ltag = 1;
  8. }
  9. if (pre != nullptr && pre->rightChild == nullptr) { // 建立前驱的后继线索
  10. pre->rightChild = p;
  11. pre->rtag = 1;
  12. }
  13. pre = p; // 更新前驱节点
  14. inThread(p->rightChild, pre); // 右子树线索化
  15. }
  16. }

3.中序遍历线索二叉树

  1. // 中序遍历线索二叉树
  2. void inOrderTraverse(ThreadTree &tree) {
  3. ThreadNode *p = tree->leftChild;
  4. while (p != tree) {
  5. while (p->ltag == 0) {
  6. p = p->leftChild;
  7. }
  8. cout << p->data << " ";
  9. while (p->rtag == 1 && p->rightChild != tree) {
  10. p = p->rightChild;
  11. cout << p->data << " ";
  12. }
  13. p = p->rightChild;
  14. }
  15. }

4.创建线索二叉树

  1. void createThreadBiTree(ThreadTree &tree) {
  2. TElementType data;
  3. cin >> data;
  4. if (data == '#') { // '#'表示空节点
  5. tree = nullptr;
  6. } else {
  7. tree = new ThreadNode{data, nullptr, nullptr, 0, 0};
  8. createThreadBiTree(tree->leftChild); // 创建左子树
  9. createThreadBiTree(tree->rightChild); // 创建右子树
  10. }
  11. }

5.完整代码

  1. #include <iostream>
  2. using namespace std;
  3. // 二叉树节点的定义
  4. typedef char TElementType;
  5. // 线索二叉树节点的定义
  6. typedef struct ThreadNode {
  7. TElementType data;
  8. struct ThreadNode * leftChild, * rightChild;
  9. int ltag, rtag; // 线索标志,0表示孩子,1表示线索
  10. } ThreadNode, * ThreadTree;
  11. // 中序线索化函数
  12. void inThread(ThreadTree &p, ThreadNode* &pre) {
  13. if (p != nullptr) {
  14. inThread(p->leftChild, pre); // 左子树线索化
  15. if (p->leftChild == nullptr) { // 左孩子为空,建立前驱线索
  16. p->leftChild = pre;
  17. p->ltag = 1;
  18. }
  19. if (pre != nullptr && pre->rightChild == nullptr) { // 建立前驱的后继线索
  20. pre->rightChild = p;
  21. pre->rtag = 1;
  22. }
  23. pre = p; // 更新前驱节点
  24. inThread(p->rightChild, pre); // 右子树线索化
  25. }
  26. }
  27. // 中序遍历线索二叉树
  28. void inOrderTraverse(ThreadTree &tree) {
  29. ThreadNode *p = tree->leftChild;
  30. while (p != tree) {
  31. while (p->ltag == 0) {
  32. p = p->leftChild;
  33. }
  34. cout << p->data << " ";
  35. while (p->rtag == 1 && p->rightChild != tree) {
  36. p = p->rightChild;
  37. cout << p->data << " ";
  38. }
  39. p = p->rightChild;
  40. }
  41. }
  42. // 创建线索二叉树
  43. void createThreadBiTree(ThreadTree &tree) {
  44. TElementType data;
  45. cin >> data;
  46. if (data == '#') { // '#'表示空节点
  47. tree = nullptr;
  48. } else {
  49. tree = new ThreadNode{data, nullptr, nullptr, 0, 0};
  50. createThreadBiTree(tree->leftChild); // 创建左子树
  51. createThreadBiTree(tree->rightChild); // 创建右子树
  52. }
  53. }
  54. // 测试线索二叉树操作
  55. void testThreadBitTree() {
  56. // 创建线索二叉树
  57. ThreadTree root = nullptr;
  58. cout << "请输入二叉树的先序遍历序列,空节点用#表示:" << endl;
  59. createThreadBiTree(root);
  60. // 中序线索化
  61. ThreadNode *pre = nullptr;
  62. inThread(root, pre);
  63. // 设置线索头节点
  64. pre->rightChild = root;
  65. pre->rtag = 1;
  66. root->rightChild = pre;
  67. // 中序遍历线索二叉树
  68. cout << "中序遍历结果:";
  69. inOrderTraverse(root);
  70. cout << endl;
  71. // 释放内存
  72. delete root;
  73. }
  74. int main() {
  75. // 测试线索二叉树操作
  76. testThreadBitTree();
  77. return 0;
  78. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/489160
推荐阅读
相关标签
  

闽ICP备14008679号