【数据结构】二叉树-C语言版(二)

简介: 【数据结构】二叉树-C语言版

二叉树的遍历

二叉树共有4种遍历方式:

深度优先遍历有3种:

(1) 前序遍历(先根遍历)  根->左->右    A B D NULL NULL NULL C E NULL NULL F NULL NULL

(2) 中序遍历(中根遍历)  左->根->右    NULL D NULL B NULL A NULL E NULL C NULL F NULL  

(3) 后序遍历(后根遍历)  左->右->根    NULL NULL D NULL B NULL NULL E NULL NULL F C A

广度优先遍历有1种:

(4) 层序遍历               一层一层遍历   A B C D NULL E F NULL NULL NULL NULL NULL NULL

使用队列进行广度优先遍历的过程:

另外,普通二叉树增删改查没有意义如果是为了存储数据,应该用线性表更简单, 用二叉树反而复杂了,并且插入删除还不好定义在哪个位置进行插入删除。排序二叉树的增删改查才有意义,如如下搜索二叉树,左边比父亲小,右边比父亲大:

查找时,每次都和根节点比较,如果比根节点小,就去左子树查找,否则去右子树查找,最多查找高度次,因此查找的时间复杂度为O(N)。极端情况下,效率会退化成O(N),和链表差不多,可以用AVL树,红黑树,平衡树来解决。

因此二叉树实现先序、中序、后序、层序遍历 ,不需要实现增删改


二叉树的遍历代码

040-Queue.h修改为如下:040-Queue.c见【数据结构】队列-C语言版

1. #pragma once
2. #include<stdio.h>
3. #include<stdbool.h>
4. #include<assert.h>
5. #include<stdlib.h>
6. 
7. //加入树节点的前置声明,队列的节点变成树,而非原来的int
8. struct BinaryTreeNode;
9. typedef struct BinaryTreeNode* QDataType;
10. 
11. //typedef int QDataType;
12. typedef struct QueueNode
13. {
14.   struct QueueNode* next;
15.   QDataType data;
16. }QueueNode;
17. 
18. typedef struct Queue
19. {
20.   QueueNode* head;
21.   QueueNode* tail;
22. }Queue;
23. 
24. //初始化
25. void QueueInit(Queue* pq);
26. 
27. //销毁
28. void QueueDestroy(Queue* pq);
29. 
30. //插入数据
31. void QueuePush(Queue* pq, QDataType x);
32. 
33. //删除数据
34. void QueuePop(Queue* pq);
35. 
36. //取队头数据
37. QDataType QueueFront(Queue* pq);
38. 
39. //取队尾数据
40. QDataType QueueRear(Queue* pq);
41. 
42. //判断队是否已满
43. bool QueueEmpty(Queue* pq);
44. 
45. //求队列元素个数
46. int QueueSize(Queue* pq);
47.

046-BinaryTree.h

1. #pragma once
2. #define  _CRT_SECURE_NO_WARNINGS  1
3. #include<stdio.h>
4. #include<stdlib.h>
5. #include<math.h>
6. #include "040-Queue.h"
7. 
8. typedef char BTDataType;
9. 
10. typedef struct BinaryTreeNode
11. {
12.   struct BinaryTreeNode* left;
13.   struct BinaryTreeNode* right;
14.   BTDataType data;
15. }BTNode;
16. 
17. //创建结点
18. BTNode* CreateTreeNode(BTDataType x);
19. 
20. //先序遍历
21. void PreOrder(BTNode* root);
22. 
23. //中序遍历
24. void InOrder(BTNode* root);
25. 
26. //后序遍历
27. void PostOrder(BTNode* root);
28. 
29. //求结点个数思路一
30. void TreeSize1(BTNode* root,int* psize);
31. 
32. //求结点个数思路二
33. int TreeSize2(BTNode* root);
34. 
35. //求叶子结点个数
36. int TreeLeafSize(BTNode* root);
37. 
38. //求树的高度
39. int TreeDepth(BTNode* root);
40. 
41. //求第k层结点个数
42. int TreeKLeafSize(BTNode* root,int k);
43. 
44. //查找树中值为x的节点
45. BTNode* TreeFind(BTNode* root, BTDataType x);
46. 
47. //二叉树销毁一级指针
48. void BinaryTreeDestroy1(BTNode* root);
49. 
50. //二叉树销毁二级指针
51. void BinaryTreeDestroy2(BTNode** root);
52. 
53. //广度优先遍历:1.根入队  2.出队头,把队头的孩子入队
54. //A入队,A出队,BC入队,B出队,D入队,C出队,EF入队,E出队,F出队
55. 
56. //广度优先遍历,需在上篇文章【数据结构】队列-C语言版的040-Queue.h开头加入树节点的前置声明:见040-Queue.h
57. void TreeLevelOrder(BTNode* root);
58. 
59. //判断二叉树是否是完全二叉树
60. bool BinaryTreeComplete(BTNode* root);

046-BinaryTree.c

1. #include"046-BinaryTree.h"
2. 
3. //创建结点
4. BTNode* CreateTreeNode(BTDataType x)
5. {
6.  BTNode* node = (BTNode*)malloc(sizeof(BTNode));
7.  node->data = x;
8.  node->left = NULL;
9.  node->right = NULL;
10. 
11.   return node;
12. }
13. 
14. //先序遍历
15. void PreOrder(BTNode* root)
16. {
17.   if (root == NULL)
18.   {
19.     printf("NULL ");
20.     return;
21.   }
22.   printf("%c ", root->data);
23.   PreOrder(root->left);
24.   PreOrder(root->right);
25. }
26. 
27. //中序遍历
28. void InOrder(BTNode* root)
29. {
30.   if (root == NULL)
31.   {
32.     printf("NULL ");
33.     return;
34.   }
35. 
36.   InOrder(root->left);
37.   printf("%c ", root->data);
38.   InOrder(root->right);
39. }
40. 
41. //后序遍历
42. void PostOrder(BTNode* root)
43. {
44.   if (root == NULL)
45.   {
46.     printf("NULL ");
47.     return;
48.   }
49. 
50.   PostOrder(root->left);
51.   PostOrder(root->right);
52.   printf("%c ", root->data);
53. }
54. 
55. 
56. //求结点个数,思路一:遍历计数的方式
57. void TreeSize1(BTNode* root,int* psize)
58. {
59.   if (root == NULL)
60.   {
61.     return;
62.   }
63.   (*psize)++;
64.   TreeSize1(root->left, psize);
65.   TreeSize1(root->right, psize);
66. }
67. 
68. //求结点个数思路二,先序遍历(1+左树结点个数+右树结点个数)
69. int TreeSize2(BTNode* root)
70. {
71.   return root == NULL ? 0 : TreeSize2(root->left) + TreeSize2(root->right) + 1;
72. }
73. 
74. //求叶子结点个数
75. int TreeLeafSize(BTNode* root)
76. {
77.   if (root == NULL)
78.   {
79.     return 0;
80.   }
81. 
82.   if (root->left == NULL && root->right == NULL)
83.   {
84.     return 1;
85.   }
86. 
87.   return TreeLeafSize(root->left) + TreeLeafSize(root->right);
88. 
89. }
90. 
91. //求树的高度
92. int TreeDepth(BTNode* root)
93. {
94.   if (root == NULL)
95.   {
96.     return 0;
97.   }
98.   return fmax(TreeDepth(root->left), TreeDepth(root->right)) + 1;
99. }
100. 
101. //求第k层结点个数
102. int TreeKLeafSize(BTNode* root,int k)
103. {
104.  if (root == NULL)
105.  {
106.    return 0;
107.  }
108. 
109.  if (k == 1)
110.  {
111.    return 1;
112.  }
113. 
114.  return TreeKLeafSize(root->left, k - 1) + TreeKLeafSize(root->right, k - 1);
115. }
116. 
117. //查找树中值为x的节点
118. BTNode* TreeFind(BTNode* root, BTDataType x)
119. {
120.  if (root == NULL)
121.  {
122.    return NULL;
123.  }
124. 
125.  //自己不是
126.  if (root->data == x)
127.  {
128.    return root;
129.  }
130. 
131.  //到左子树中去找
132.  BTNode* lret = TreeFind(root->left, x);
133.  if (lret)
134.  {
135.    return lret;
136.  }
137. 
138.  //到右子树中去找
139.  BTNode* rret = TreeFind(root->right, x);
140.  if (rret)
141.  {
142.    return rret;
143.  }
144. 
145.  //左右两边都没有找到
146.  return NULL;
147. }
148. 
149. //二叉树销毁一级指针
150. void BinaryTreeDestroy1(BTNode* root)
151. {
152.  if (root == NULL)
153.  {
154.    return;
155.  }
156. 
157.  BinaryTreeDestroy1(root->left);
158.  BinaryTreeDestroy1(root->right);
159.  free(root);
160. }
161. 
162. //二叉树销毁二级指针
163. void BinaryTreeDestroy2(BTNode** pproot)
164. {
165.  if (*pproot == NULL)
166.  {
167.    return;
168.  }
169. 
170.  BinaryTreeDestroy2(&(*pproot)->left);
171.  BinaryTreeDestroy2(&(*pproot)->right);
172.  free(*pproot);
173.  *pproot = NULL;
174. }
175. 
176. //广度优先遍历
177. void TreeLevelOrder(BTNode* root)
178. {
179.  Queue q;
180.  QueueInit(&q);
181. 
182.  //入根结点
183.  if (root)
184.  {
185.    QueuePush(&q, root);
186.  }
187. 
188.  //出根,入左右孩子
189.  while (!QueueEmpty(&q))
190.  {
191.    BTNode* front = QueueFront(&q);
192.    QueuePop(&q);
193. 
194.    printf("%c ", front->data);
195.    if (front->left)
196.    {
197.      QueuePush(&q, front->left);
198.    }
199. 
200.    if (front->right)
201.    {
202.      QueuePush(&q, front->right);
203.    }
204.  }
205. 
206.  QueueDestroy(&q);
207. }
208. 
209. //判断二叉树是否是完全二叉树
210. bool BinaryTreeComplete(BTNode* root)
211. {
212.  Queue q;
213.  QueueInit(&q);
214.  if (root)
215.  {
216.    QueuePush(&q, root);
217.  }
218. 
219.  while (!QueueEmpty(&q))
220.  {
221. 
222.    BTNode* front = QueueFront(&q);
223.    //出根
224.    QueuePop(&q);
225. 
226.    //出的节点为空,跳出循环,准备判断队列后面还有没有非空结点
227.    if (front == NULL)
228.    {
229.      break;
230.    }
231. 
232.    //入左右孩子
233.    QueuePush(&q, front->left);
234.    QueuePush(&q, front->right);
235.  }
236. 
237.  //若队头结点即空结点的下一个结点非空,则不完全
238.  while (!QueueEmpty(&q))
239.  {
240.    BTNode* front = QueueFront(&q);
241.    QueuePop(&q);
242. 
243.    if (front)
244.    {
245.      return false;
246.    }
247.  }
248. 
249.  QueueDestroy(&q);
250.  return true;
251. }

046-test.c

1. #include"046-BinaryTree.h"
2. int main()
3. {
4.  BTNode* A = CreateTreeNode('A');
5.  BTNode* B = CreateTreeNode('B');
6.  BTNode* C = CreateTreeNode('C');
7.  BTNode* D = CreateTreeNode('D');
8.  BTNode* E = CreateTreeNode('E');
9.  BTNode* F = CreateTreeNode('F');
10. 
11.   A->left = B;
12.   A->right = C;
13.   B->left = D;
14.   C->left = E;
15.   C->right = F;
16. 
17.   //先序遍历
18.   PreOrder(A);//递归:1.子问题(根  左子树  右子树)  2.结束条件 
19.   printf("\n");
20. 
21.   //中序遍历
22.   InOrder(A);
23.   printf("\n");
24. 
25.   //后序遍历
26.   PostOrder(A);
27.   printf("\n");
28. 
29.   //求结点个数思路一调用
30.   int size = 0;
31.   TreeSize1(A, &size);
32.   printf("TreeSize = %d\n", size);
33. 
34.   //求结点个数思路二调用
35.   printf("TreeSize = %d\n", TreeSize2(A));
36. 
37.     //求叶子结点个数
38.   printf("TreeLeafSize = %d\n", TreeLeafSize(A));
39. 
40.   //求树的高度
41.   printf("TreeDepth = %d\n", TreeDepth(A));
42. 
43.   //第K层结点个数
44.   printf("TreeKLeafSize = %d\n", TreeKLeafSize(A, 3));
45. 
46.   //查找结点
47.   BTNode* ret = TreeFind(A, 'F');
48.   printf("TreeFind = %c\n", ret->data);
49. 
50.   //层序遍历
51.   TreeLevelOrder(A);
52. 
53.     printf("\n");
54. 
55.   //判断二叉树是不是完全二叉树
56.   printf("%d", BinaryTreeComplete(A));
57. 
58.   //二叉树销毁一级指针
59.   //BinaryTreeDestroy1(A);
60.   //A = NULL;
61. 
62.   //二叉树销毁二级指针
63.   BinaryTreeDestroy2(&A);
64. 
65. 
66. 
67.   return 0;
68. }
69.

代码运行结果如下:

先序遍历的递归调用过程

中序遍历的递归调用过程

后序遍历的递归调用过程


相关文章
|
17天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
17天前
|
C语言
数据结构基础详解(C语言):图的基本概念_无向图_有向图_子图_生成树_生成森林_完全图
本文介绍了图的基本概念,包括图的定义、无向图与有向图、简单图与多重图等,并解释了顶点度、路径、连通性等相关术语。此外还讨论了子图、生成树、带权图及几种特殊形态的图,如完全图和树等。通过这些概念,读者可以更好地理解图论的基础知识。
|
19天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
19天前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
19天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。
|
19天前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
算法 C语言
C语言数据结构(15)--二叉树的前序、中序、后序遍历
本文目录 1. 背景 2. 前序遍历 3. 中序遍历 4. 后序遍历 5. 层序遍历 6. 代码实现
344 0
C语言数据结构(15)--二叉树的前序、中序、后序遍历
|
16天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
19天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
19天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。