当前位置:   article > 正文

“二叉树遍历“详解 以及 二叉树的实现_二叉树的遍历打印

二叉树的遍历打印

目录

一.二叉树的遍历

1.二叉树的遍历的解释:

2.二叉树的遍历有三种递归结构

(1) 实现先序遍历:

(2) 实现中序遍历:

(3) 实现后序遍历: 

(4) 二叉树的层序遍历

 层序遍历代码:

二.二叉树的递归实现相关函数讲解

1.求二叉树节点个数

①错误示例1:局部变量count可以吗?—不可以

②错误示例2:局部静态变量可以吗?—不可以

③错误示例3 能过但是不安全的做法:

④正确做法:遍历思路里面的正确方法

⑤最佳做法:不用遍历的做法,思路是:子问题,

分治定义:

2.求二叉树叶子节点个数

思路1:遍历+计数

思路2:分治

3.求二叉树的第K层节点个数

4.求二叉树的深度

三.补充剩下的二叉树实现相关函数

1.二叉树查找值为x的节点

2.二叉树的销毁

3.判断二叉树是否是完全二叉树

四.完整实现:

五.层序遍历完整实现 队列+二叉树

Queue.h

Queue.c

Test.c

运行结果:


一.二叉树的遍历

1.二叉树的遍历的解释:

二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历 是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

2.二叉树的遍历有三种递归结构

1. 前序遍历(Preorder Traversal 也叫先根遍历)——访问根结点的操作发生在遍历其左右子树之前。       
                即访问顺序为:根 -> 左子树 -> 右子树
2. 中序遍历(Inorder Traversal 也叫先根遍历)——访问根结点的操作发生在遍历其左右子树之中(间)。
                即访问顺序为:左子树 -> 根 -> 右子树
3. 后序遍历(Postorder Traversal 也叫先根遍历)——访问根结点的操作发生在遍历其左右子树之后。

               即访问顺序为:左子树 -> 右子树 -> 根

4.层序遍历

下面是3种遍历的顺序示意图:注:NULL(属于谁)

接下来我们依旧围绕此二叉树实现三种遍历:

(1) 实现先序遍历

我们详细走一遍先序遍历,后面的中序遍历,后序遍历用图解释

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <assert.h>
  4. typedef int BTDataType;
  5. typedef struct BinaryTreeNode //定义二叉树结构体
  6. {
  7. struct BinaryTreeNode* left;
  8. struct BinaryTreeNode* right;
  9. BTDataType data;
  10. }BTNode;
  11. BTNode* BuyBTNode(BTDataType x) //创建二叉树节点函数
  12. {
  13. BTNode* node = (BTNode*)malloc(sizeof(BTNode));
  14. if (node == NULL)
  15. {
  16. printf("malloc fail\n");
  17. exit(-1);
  18. }
  19. node->data = x;
  20. node->left = node->right = NULL;
  21. return node;
  22. }
  23. BTNode* CreatBinaryTree() //手动创建一个如上图所示的二叉树
  24. {
  25. BTNode* node1 = BuyBTNode(1);
  26. BTNode* node2 = BuyBTNode(2);
  27. BTNode* node3 = BuyBTNode(3);
  28. BTNode* node4 = BuyBTNode(4);
  29. BTNode* node5 = BuyBTNode(5);
  30. BTNode* node6 = BuyBTNode(6);
  31. node1->left = node2;
  32. node1->right = node4;
  33. node2->left = node3;
  34. node4->left = node5;
  35. node4->right = node6;
  36. return node1;
  37. }
  38. void PrevOrder(BTNode* root) { //先序遍历函数
  39. if (root == NULL) {
  40. printf("NULL ");
  41. return;
  42. }
  43. printf("%d ", root->data);
  44. PrevOrder(root->left);
  45. PrevOrder(root->right);
  46. }

先序遍历函数解析:下图调用时有13个函数栈帧,标识即表示栈帧又表示打印顺序

栈帧1:首先传入二叉树的根root 节点1,打印1后 分为节点1左子树的先序遍历和节点1右子树的先序遍历,先沿着节点1左子树的先序遍历走到栈帧2

栈帧2:打印2后 分为节点2左子树的先序遍历和节点2右子树的先序遍历,继续沿着节点2左子树的先序遍历走到栈帧3

栈帧3:打印3后 分为节点3左子树的先序遍历和节点3右子树的先序遍历,继续沿着节点3左子树的先序遍历走到栈帧4

栈帧4:栈帧4发现节点3的左子树是NULL,则打印NULL并返回栈帧4结束 同时结束栈帧3中的PrevOrder(root->left); ,继续沿着节点3右子树的先序遍历走(即执行栈帧3中的PrevOrder(root->right);

栈帧5:栈帧5发现节点3的右子树是NULL,则打印NULL并返回栈帧5结束 同时结束栈帧3中的PrevOrder(root->right); ,此时栈帧3结束 同时结束栈帧2中的PrevOrder(root->left);,继续沿着节点2右子树的先序遍历走(即执行栈帧2中的PrevOrder(root->right);)

栈帧6:栈帧6发现节点2的右子树是NULL,则打印NULL并返回栈帧6结束 同时结束栈帧2中的PrevOrder(root->right); ,此时栈帧2结束 同时结束栈帧1中的PrevOrder(root->left);,继续沿着节点1右子树的先序遍历走(即执行栈帧1中的PrevOrder(root->right);)  把图放中间方便观察
 

栈帧7:先打印4后 分为节点4左子树的先序遍历和节点4右子树的先序遍历,继续沿着节点4左子树的先序遍历走到栈帧8

栈帧8:先打印5后 分为节点5左子树的先序遍历和节点5右子树的先序遍历,继续沿着节点5左子树的先序遍历走到栈帧9

栈帧9:栈帧9发现节点5的左子树是NULL,则打印NULL并返回栈帧9结束 同时结束栈帧8中的PrevOrder(root->left); ,继续沿着节点5右子树的先序遍历走(即执行栈帧8中的PrevOrder(root->right);

栈帧10:栈帧10发现节点5的右子树是NULL,则打印NULL并返回栈帧10结束 同时结束栈帧8中的PrevOrder(root->right); ,此时栈帧8结束 同时结束栈帧7中的PrevOrder(root->left);,继续沿着节点4右子树的先序遍历走(即执行栈帧7中的PrevOrder(root->right);)

栈帧11:先打印6后 分为节点6左子树的先序遍历和节点6右子树的先序遍历,继续沿着节点6左子树的先序遍历走到栈帧12

栈帧12:栈帧9发现节点6的左子树是NULL,则打印NULL并返回栈帧12结束 同时结束栈帧11中的PrevOrder(root->left); ,继续沿着节点6右子树的先序遍历走(即执行栈帧11中的PrevOrder(root->right);

栈帧13:栈帧13发现节点6的右子树是NULL,则打印NULL并返回栈帧13结束 同时结束栈帧11中的PrevOrder(root->right); ,此时栈帧11结束 同时结束栈帧7中的PrevOrder(root->right);,栈帧7结束 同时结束栈帧1中的PrevOrder(root->right); ,此时栈帧1结束 同时结束整个递归循环

(2) 实现中序遍历:

  1. void InOrder(BTNode* root) {
  2. if (root == NULL){
  3. printf("NULL ");
  4. return;
  5. }
  6. InOrder(root->left);
  7. printf("%d ", root->data);
  8. InOrder(root->right);
  9. }

  看图即可,过程跟先序一样,红色的数字是传入的节点数,蓝色数字表示打印顺序:

(3) 实现后序遍历: 

  1. void PostOrder(BTNode* root) {
  2. if (root == NULL){
  3. printf("NULL ");
  4. return;
  5. }
  6. PostOrder(root->left);
  7. PostOrder(root->right);
  8. printf("%d ", root->data);
  9. }

 看图即可,过程跟先序一样,仍然是红色数字是传入的节点数,蓝色数字表示打印顺序:

(4) 二叉树的层序遍历

层序遍历是一层一层遍历二叉树,例如上面的这个树,打印后就是:1 2 4 3 5 6  

思路:创建一个队列(先进先出的单链表)借助队列先进先出的性质。上一层的节点出的时候,带下一层的节点进去。

特别注意的是:如果你只是把节点的值放进队列,那么打印并pop完这个值后将无法找到他的孩子,所以我们必须把整个节点都入进队列

因此需要把Queue.h中的 typedef int QDataType; 修改为 typedef struct BinaryTreeNode* QDataType;  但是struct BinaryTreeNode 这个结构体是在Test.c中定义的,那么Queue.h中的 typedef struct BinaryTreeNode* QDataType; 将无法找到此结构体,那我们想:可以把Test.c中的 #include"Queue.h"  ,放到定义 struct BinaryTreeNode 结构体的后面,当预处理时 #include"Queue.h" 被代码替换,

#include"Queue.h" 中的 typedef struct BinaryTreeNode* QDataType; 通过向上找就可以找到定义的结构体,这样总没错了吧? ——还是有错,因为不仅Test.c使用 QDataType,Queue.c也要使用QDataType,上面的操作仅仅只是让Test.c可以正常使用了,所以我们不如直接在Queue.h中的typedef struct BinaryTreeNode* QDataType; 前添上 结构体声明 struct BinaryTreeNode; ,这样两个.c文件就都可以找到此结构体了。(在Test.c中我们定义结构体:

typedef struct BinaryTreeNode
{
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
    BTDataType data;
}BTNode; 但是结构体声明中我们并不能直接声明 BTNode; ,还是要声明完整结构体名称,即;

整个过程:下面的二叉树,先把节点1放入队列(队列状态:1),打印并pop 节点1,随后把它的孩子放入队列,他的孩子正好就是下一层的前两个节点 节点2 和 节点4(队列状态:2 4);

打印并pop 节点2(队列状态:4),随后把它的孩子节点3放入队列,他的孩子正好就是下一层的前一个节点 节点3 右树为空就不用做事情;(队列状态:4 3)

该节点4了,打印并pop 节点4(队列状态:3),随后把它的孩子节点5,6放入队列,他的孩子正好就是下一层的第二个和第三个节点;(队列状态:3 5 6)

该节点3了,打印并pop 节点3(队列状态:5 6),发现它的孩子都是NULL,NULL节点不用放,不用做任何事情;(队列状态:5 6)

该节点5了,打印并pop 节点5(队列状态:6),发现它的孩子都是NULL,NULL节点不用放,不用做任何事情;(队列状态:6)

该节点6了,打印并pop 节点6(队列状态:NULL),发现它的孩子都是NULL,NULL节点不用放,不用做任何事情;(队列状态:NULL)

队列空,则结束!(可以看下面的动态图演示层序过程)

 层序遍历代码:

  1. #include"Queue.h"
  2. //创建二叉树等工程请在本文章 序号五 完整代码看......
  3. void LevelOrder(BTNode* root)
  4. {
  5. Queue q;
  6. QueueInit(&q);
  7. if (root) //根节点若不是NULL,将根节点入队列-开始
  8. {
  9. QueuePush(&q, root);
  10. }
  11. while (!QueueEmpty(&q)) //队列不为空时
  12. {
  13. BTNode* front = QueueFront(&q); //获得队列第一个元素
  14. QueuePop(&q); //获得此元素后就可以直接pop扔掉了
  15. printf("%d ", front->data); //打印队列第一个元素
  16. if (front->left) //左孩子若不是NULL,将左孩子入队列
  17. {
  18. QueuePush(&q, front->left);
  19. }
  20. if (front->right) //右孩子若不是NULL,将右孩子入队列
  21. {
  22. QueuePush(&q, front->right);
  23. }
  24. }
  25. printf("\n");
  26. QueueDestroy(&q);
  27. }
  28. int main()
  29. {
  30. BTNode* tree = CreatBinaryTree();
  31. LevelOrder(tree); //层序遍历
  32. return 0;
  33. }

二.二叉树的递归实现相关函数讲解

1.求二叉树节点个数

我们先手动创建一个上图所示的二叉树:

  1. typedef int BTDataType;
  2. typedef struct BTNode
  3. {
  4. struct BTNode* left;
  5. struct BTNode* right;
  6. BTDataType data;
  7. }BTNode;
  8. BTNode* BuyBTNode(BTDataType x)
  9. {
  10. BTNode* node = (BTNode*)malloc(sizeof(BTNode));
  11. if (node == NULL)
  12. {
  13. printf("malloc fail\n");
  14. exit(-1);
  15. }
  16. node->data = x;
  17. node->left = node->right = NULL;
  18. return node;
  19. }
  20. BTNode* CreatBinaryTree()
  21. {
  22. BTNode* node1 = BuyBTNode(1);
  23. BTNode* node2 = BuyBTNode(2);
  24. BTNode* node3 = BuyBTNode(3);
  25. BTNode* node4 = BuyBTNode(4);
  26. BTNode* node5 = BuyBTNode(5);
  27. BTNode* node6 = BuyBTNode(6);
  28. node1->left = node2;
  29. node1->right = node4;
  30. node2->left = node3;
  31. node4->left = node5;
  32. node4->right = node6;
  33. return node1;
  34. }

下面代码都是在此基础上进行的

受前面的二叉树遍历的引导,我们不禁思考:若求二叉树节点个数,只要把每个非空节点的打印改成计数不就可以了吗?思想:遍历+计数,那直接上手写代码是非常容易考虑不全的,我们需要一步一步探索正确答案并纠正~

错误示例1:局部变量count可以吗?—不可以

  1. int BTreeSize(BTNode* root)
  2. {
  3. int count = 0;
  4. if (root == NULL)
  5. return count;
  6. ++count;
  7. BTreeSize(root->left);
  8. BTreeSize(root->right);
  9. return count;
  10. }

我们直接把打印改成计数后,写出这样的代码。这样是有问题的:count作为局部变量,在每一次递归调用自己时都会刷新为0,然后再计数就没有意义了。

错误示例2:局部静态变量可以吗?—不可以

  1. //多次调用会有问题,没办法每次初始化为0
  2. int BTreeSize(BTNode* root)
  3. {
  4. static int count = 0;
  5. if (root == NULL)
  6. return count;
  7. ++count;
  8. BTreeSize(root->left);
  9. BTreeSize(root->right);
  10. return count;
  11. }
  12. int main()
  13. {
  14. BTNode* tree = CreatBinaryTree();
  15. printf("size:%d\n", BTreeSize(tree));
  16. printf("size:%d\n", BTreeSize(tree)); //调用多次就会叠加,就错了
  17. printf("size:%d\n", BTreeSize(tree));
  18. return 0;
  19. }

我们可能会想到用static修饰的静态变量就可以计数了,但是这样也是有问题的,当你多次调用BTreeSize 这个求二叉树节点个数的函数 时,第一次静态变量count已经加到6,再调用 BTreeSize(tree) 时count的初始值就是6,第二次打印节点个数就是12,第三次就是18,会叠加,所以不行。

错误示例3 能过但是不安全的做法

那我用个全局变量count计数,每次count置0行吗?此时函数也没有返回值了

  1. //线程安全的问题,这个以后linux学了大家就知道了
  2. int count = 0;
  3. void BTreeSize(BTNode* root)
  4. {
  5. if (root == NULL)
  6. return;
  7. ++count;
  8. BTreeSize(root->left);
  9. BTreeSize(root->right);
  10. }
  11. int main()
  12. {
  13. BTNode* tree = CreatBinaryTree();
  14. count = 0;
  15. BTreeSize(tree);
  16. printf("size:%d\n", count);
  17. count = 0;
  18. BTreeSize(tree);
  19. printf("size:%d\n", count);
  20. count = 0;
  21. BTreeSize(tree);
  22. printf("size:%d\n", count);
  23. return 0;
  24. }

答案是不行~全局变量同时被调用,同时count++,会有线程安全的问题,会使count错乱,这里作为了解,反正尽量不用全局变量。

正确做法:遍历思路里面的正确方法

思想:遍历+计数 用这种思路,这里要用传址调用并且每次使用次函数时也要手动把count给成0。

  1. void BTreeSize(BTNode* root, int* pcount)
  2. {
  3. if (root == NULL)
  4. {
  5. return 0;
  6. }
  7. (*pcount)++;
  8. BTreeSize(root->left, pcount);
  9. BTreeSize(root->right, pcount);
  10. }
  11. int main()
  12. {
  13. BTNode* tree = CreatBinaryTree();
  14. int count1 = 0;
  15. BTreeSize(tree, &count1);
  16. printf("size:%d\n", count1);
  17. int count2 = 0;
  18. BTreeSize(tree, &count2);
  19. printf("size:%d\n", count2);
  20. return 0;
  21. }

最佳做法:不用遍历的做法,思路是:子问题,

1、空树,最小规模子问题,节点个数返回0
2、非空,左子树节点个数+右子树节点个数+1 (自己)

传入根节点root,如果根节点root是NULL就无节点,返回0;如果根节点root不是NULL就化为两个子问题计算左树节点数和右树节点数相加,并加上1(自己)。

  1. int BTreeSize (BTNode* root)
  2. {
  3. return root == NULL ? 0 : BTreeSize(root->left) + BTreeSize(root->right) + 1;
  4. }
  5. int main()
  6. {
  7. BTNode* tree = CreatBinaryTree();
  8. printf("%d ", BTreeSize (tree));
  9. return 0;
  10. }

其实这种子问题的思想也叫作分治思想:

分治定义:

把复杂的问题,分成更小规模的子问题,子问题再分成更小规模的子问题。直到子问题不可再分割,直接能出结果。

2.求二叉树叶子节点个数

思路1:遍历+计数

遍历整个二叉树,遇到叶子就++,当root为空时就返回。

  1. void BTreeLeafSize1(BTNode* root,int* pcount)
  2. {
  3. if (root == NULL)
  4. return;
  5. if (root->left == NULL && root->right == NULL)
  6. {
  7. (*pcount)++;
  8. }
  9. BTreeLeafSize1(root->left, pcount);
  10. BTreeLeafSize1(root->right, pcount);
  11. }
  12. int main()
  13. {
  14. BTNode* tree = CreatBinaryTree();
  15. int count = 0;
  16. BTreeLeafSize1(tree,&count);
  17. printf("BTreeLeafSize1:%d\n ", count);
  18. return 0;
  19. }


思路2:分治

把二叉树叶子个数分为左子树叶子个数+右子树叶子个数,如果root是叶子就返回1,是空就返回0

  1. int BTreeLeafSize(BTNode* root)
  2. {
  3. if (root == NULL)
  4. return 0;
  5. if (root->left == NULL && root->right == NULL)
  6. return 1;
  7. return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
  8. }
  9. int main()
  10. {
  11. BTNode* tree = CreatBinaryTree();
  12. printf("BTreeLeafSize:%d\n", BTreeLeafSize(tree));
  13. return 0;
  14. }

3.求二叉树的第K层节点个数

思想:
1、空树,返回0
2、非空,且k== 1,返回1
3、非空,且k> 1,转换成左子树k-1层节点个数+右子树k-1层节点个数

  1. int BTreeKLevelSize(BTNode* root,int k)
  2. {
  3. assert(k>=1);
  4. if (root == NULL)
  5. return 0;
  6. if (k == 1)
  7. return 1;
  8. return BTreeKLevelSize(root->left, k - 1) + BTreeKLevelSize(root->right, k - 1);
  9. }
  10. int main()
  11. {
  12. BTNode* tree = CreatBinaryTree();
  13. printf("BTreeKLevelSize:%d\n ", BTreeKLevelSize(tree, 2));
  14. return 0;
  15. }

4.求二叉树的深度

分治思想:比较左子树高度和右子树高度,返回大的那个子树的深度+1,当节点是NULL时返回0,当左右子树都为NULL时,0和0比较后+1,即返回1(因为就自己一层)

  1. int BTreeDepth(BTNode* root)
  2. {
  3. if (root == NULL)
  4. return 0;
  5. int leftDepth = BTreeDepth(root->left);
  6. int rightDepth = BTreeDepth(root->right);
  7. return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
  8. }
  9. int main()
  10. {
  11. BTNode* tree = CreatBinaryTree();
  12. printf("BTreeDepth:%d\n", BTreeDepth(tree));
  13. return 0;
  14. }

 不懂的请看完整递归图:

三.补充剩下的二叉树实现相关函数

1.二叉树查找值为x的节点

 查找函数思路很简单:从根节点开始,如果节点为NULL就返回NULL,如果节点值=x就返回这个节点地址,继续向下判断孩子,如果这个节点的左孩子不为空,说明找到了,找到就返回左孩子;如果右孩子不为空,就返回右孩子;当都为空,走到最后,说明没找到,没找到就返回NULL。

  1. // 二叉树查找值为x的节点
  2. BTNode* BTreeFind(BTNode* root, BTDataType x)
  3. {
  4. if (root == NULL) //如果节点为空就返回NULL
  5. return NULL;
  6. if (root->data == x) //如果节点值=x就返回这个节点
  7. return root;
  8. BTNode* ret1 = BTreeFind(root->left, x);
  9. if (ret1) 如果节点的左孩子不为空,说明找到了,找到就返回左孩子
  10. {
  11. return ret1;
  12. }
  13. BTNode* ret2 = BTreeFind(root->right, x);
  14. if (ret2) //如果节点的右孩子不为空,说明找到了,找到就返回右孩子
  15. {
  16. return ret2;
  17. }
  18. return NULL; //当走到这里的时候,说明没找到,没找到就返回NULL
  19. }
  20. int main()
  21. {
  22. BTNode* tree = CreatBinaryTree();
  23. for (int i = 1; i <= 7; ++i)
  24. {
  25. printf("Find:%d,%p\n", i, BTreeFind(tree, i));
  26. }
  27. BTNode* ret = BTreeFind(tree, 5);
  28. if (ret)
  29. {
  30. ret->data = 50;
  31. }
  32. return 0;
  33. }

运行结果:

2.二叉树的销毁

  1. void BTreeDestory(BTNode* root)
  2. {
  3. if (root == NULL)
  4. {
  5. return;
  6. }
  7. BTreeDestory(root->left);
  8. BTreeDestory(root->right);
  9. free(root);
  10. }

销毁要一个节点一个节点销毁,如果你上来就free了根节点,那下面的节点都找不到了,所以应该先free左右孩子节点,最后free根节点,因此应该用后序遍历的方式free每一个节点,左孩子->右孩子->根 依次free。如果是空节点就不用free,直接返回即可。

3.判断二叉树是否是完全二叉树

  1. void BTreeDestroy(BTNode* root)
  2. {
  3. if (root == NULL)
  4. return ;
  5. BTreeDestroy( root->left );
  6. BTreeDestroy( root->right );
  7. free(root);
  8. }
  9. //2.判断二叉树是否是完全二叉树
  10. bool BTreeComplete(BTNode* root)
  11. {
  12. Queue q;
  13. QueueInit(&q);
  14. if (root) // 3.如果二叉树不为空,先放根节点
  15. QueuePush(&q, root);
  16. while (!QueueEmpty(&q)) //4.当队列不空就层序遍历
  17. {
  18. BTNode* front = QueueFront(&q);
  19. QueuePop(&q);
  20. if (front == NULL) //5.看队头数据是否是空,是空就break进入下面检查后面是否有非空
  21. break;
  22. QueuePush(&q, front->left); //6.走到这说明没遇到空节点,继续层序遍历
  23. QueuePush(&q, front->right);
  24. }
  25. while (!QueueEmpty(&q)) //7.走到这说明已经遇到空节点,开始检查后面是否有非空,继续层序遍历找非空节点
  26. {
  27. BTNode* front = QueueFront(&q);
  28. QueuePop(&q);
  29. if (front) // 8.空节点后面出到非空节点,那么说明不是完全二叉树
  30. {
  31. QueueDestory(&q); //9.不是完全二叉树就返回false,返回前记的销毁队列,否则内存泄漏
  32. return false;
  33. }
  34. }
  35. //10.走到这说明第一次遇到空节点后,后面都是空节点,说明是完全二叉树,是就返回true,返回前要销毁队列
  36. QueueDestory(&q);
  37. return true;
  38. }
  39. int main()
  40. {
  41. BTNode* tree = CreatBinaryTree(); // 1.创建上图所示的二叉树
  42. printf("%d ",BTreeComplete(tree)); // 11.打印返回的布尔值
  43. BTreeDestroy(tree); // 12.对二叉树销毁
  44. tree = NULL;
  45. return 0;
  46. }

整体思路:

1、层序遍历,空节点也进队列
2、出到空节点以后,出队列中所有数据,如果全是空,就是完全二叉树,如果有非空,就不是
(详情请见上面代码注释过程)

运行结果就是0

四.完整实现:

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<time.h>
  4. #include<stdlib.h>
  5. #include<assert.h>
  6. typedef int BTDataType;
  7. typedef struct BinaryTreeNode
  8. {
  9. BTDataType data;
  10. struct BinaryTreeNode* left;
  11. struct BinaryTreeNode* right;
  12. }BTNode;
  13. BTNode* BuyBTNode(BTDataType x)//————————————————————————————————————————————————————
  14. {
  15. BTNode* node = (BTNode*)malloc(sizeof(BTNode));
  16. if (node == NULL)
  17. {
  18. printf("malloc fail\n");
  19. exit(-1);
  20. }
  21. node->data = x;
  22. node->left = node->right = NULL;
  23. return node;
  24. }
  25. //创建一个二叉树—————————————————————————————————————————————————————————
  26. BTNode* CreatBinaryTree()
  27. {
  28. BTNode* node1 = BuyBTNode(1);
  29. BTNode* node2 = BuyBTNode(2);
  30. BTNode* node3 = BuyBTNode(3);
  31. BTNode* node4 = BuyBTNode(4);
  32. BTNode* node5 = BuyBTNode(5);
  33. BTNode* node6 = BuyBTNode(6);
  34. //BTNode* node7 = BuyBTNode(7);
  35. node1->left = node2;
  36. node1->right = node4;
  37. node2->left = node3;
  38. //node2->right = node7;
  39. node4->left = node5;
  40. node4->right = node6;
  41. return node1;
  42. }
  43. // 二叉树销毁————————————————————————————————————————————————————————————
  44. void BTreeDestory(BTNode* root)
  45. {
  46. if (root == NULL)
  47. {
  48. return;
  49. }
  50. BTreeDestory(root->left);
  51. BTreeDestory(root->right);
  52. free(root);
  53. }
  54. // 二叉树节点个数(2种)————————————————————————————————————————————————————
  55. void BTreeSize1(BTNode* root, int* pcount)
  56. {
  57. if (root == NULL)
  58. {
  59. return 0;
  60. }
  61. (*pcount)++;
  62. BTreeSize(root->left, pcount);
  63. BTreeSize(root->right, pcount);
  64. }
  65. int BTreeSize(BTNode* root)
  66. {
  67. return root == NULL ? 0 : BTreeSize(root->left) + BTreeSize(root->right) + 1;
  68. }
  69. // 二叉树叶子节点个数(2种)————————————————————————————————————————————————————
  70. int BTreeLeafSize(BTNode* root)
  71. {
  72. if (root == NULL)
  73. return 0;
  74. if (root->left == NULL && root->right == NULL)
  75. return 1;
  76. return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);
  77. }
  78. void BTreeLeafSize1(BTNode* root, int* pcount)
  79. {
  80. if (root == NULL)
  81. return;
  82. if (root->left == NULL && root->right == NULL)
  83. {
  84. (*pcount)++;
  85. }
  86. BTreeLeafSize1(root->left, pcount);
  87. BTreeLeafSize1(root->right, pcount);
  88. }
  89. // 二叉树第k层节点个数————————————————————————————————————————————————————————————
  90. int BTreeKLevelSize(BTNode* root, int k)
  91. {
  92. assert(k >= 1);
  93. if (root == NULL)
  94. return 0;
  95. if (k == 1)
  96. return 1;
  97. return BTreeKLevelSize(root->left, k - 1) + BTreeKLevelSize(root->right, k - 1);
  98. }
  99. // 二叉树的深度
  100. int BTreeDepth(BTNode* root)
  101. {
  102. if (root == NULL)
  103. return 0;
  104. int leftDepth = BTreeDepth(root->left);
  105. int rightDepth = BTreeDepth(root->right);
  106. return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
  107. }
  108. // 二叉树查找值为x的节点——————————————————————————————————————————————————————————
  109. BTNode* BTreeFind(BTNode* root, BTDataType x)
  110. {
  111. if (root == NULL)
  112. return NULL;
  113. if (root->data == x)
  114. return root;
  115. BTNode* ret1 = BTreeFind(root->left, x);
  116. if (ret1)
  117. {
  118. return ret1;
  119. }
  120. BTNode* ret2 = BTreeFind(root->right, x);
  121. if (ret2)
  122. {
  123. return ret2;
  124. }
  125. return NULL;
  126. }
  127. // 二叉树前序遍历 ———————————————————————————————————————————————————————————————
  128. void PrevOrder(BTNode* tree) //前序遍历 根-左子树-右子树
  129. {
  130. if (tree == NULL)
  131. {
  132. printf("NULL ");
  133. return;
  134. }
  135. printf("%d ", tree->data);
  136. PrevOrder(tree->left);
  137. PrevOrder(tree->right);
  138. }
  139. // 二叉树中序遍历———————————————————————————————————————————————————————————————
  140. void InOrder(BTNode* root) {
  141. if (root == NULL) {
  142. printf("NULL ");
  143. return;
  144. }
  145. InOrder(root->left);
  146. printf("%d ", root->data);
  147. InOrder(root->right);
  148. }
  149. // 二叉树后序遍历———————————————————————————————————————————————————————————————
  150. void PostOrder(BTNode* root) {
  151. if (root == NULL) {
  152. printf("NULL ");
  153. return;
  154. }
  155. PostOrder(root->left);
  156. PostOrder(root->right);
  157. printf("%d ", root->data);
  158. }
  159. // 层序遍历—————————————————————————————————————————————————————————————————————
  160. void BinaryTreeLevelOrder(BTNode* root); //因为要用到队列,在最后单独列出
  161. // 判断二叉树是否是完全二叉树—————————————————————————————————————————————————————
  162. bool BTreeComplete(BTNode* root)
  163. {
  164. Queue q;
  165. QueueInit(&q);
  166. if (root)
  167. QueuePush(&q, root);
  168. while (!QueueEmpty(&q))
  169. {
  170. BTNode* front = QueueFront(&q);
  171. QueuePop(&q);
  172. if (front == NULL)
  173. break;
  174. QueuePush(&q, front->left);
  175. QueuePush(&q, front->right);
  176. }
  177. while (!QueueEmpty(&q))
  178. {
  179. BTNode* front = QueueFront(&q);
  180. QueuePop(&q);
  181. // 空后面出到非空,那么说明不是完全二叉树
  182. if (front)
  183. {
  184. QueueDestory(&q);
  185. return false;
  186. }
  187. }
  188. QueueDestory(&q);
  189. return true;
  190. }
  191. int main()
  192. {
  193. BTNode* tree = CreatBinaryTree();
  194. //int count = 0;
  195. //BTreeSize1(tree ,&count);
  196. //printf("%d ", count);
  197. printf("BTreeSize:%d\n", BTreeSize(tree));
  198. printf("BTreeLeafSize:%d\n", BTreeLeafSize(tree));
  199. int count = 0;
  200. BTreeLeafSize1(tree, &count);
  201. printf("BTreeLeafSize1:%d\n", count);
  202. printf("BTreeKLevelSize:%d\n", BTreeKLevelSize(tree, 2));
  203. BTreeDepth(tree);
  204. printf("BTreeDepth:%d\n", BTreeDepth(tree));
  205. BTreeFind(tree, 6);
  206. printf("BTreeFind:%d", BTreeFind(tree, 6)->data);
  207. printf("%d ",BTreeComplete(tree));
  208. BTreeDestroy(tree); //对二叉树销毁
  209. tree = NULL;
  210. return 0;
  211. }

五.层序遍历完整实现 队列+二叉树

Queue.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<stdbool.h>
  5. #include<assert.h>
  6. struct BinaryTreeNode;
  7. typedef struct BinaryTreeNode* QDataType;
  8. typedef struct QueueNode
  9. {
  10. QDataType data;
  11. struct QueueNode* next;
  12. }QNode;
  13. typedef struct Queue
  14. {
  15. QNode* head;
  16. QNode* tail;
  17. }Queue;
  18. void QueueInit(Queue* pq);
  19. void QueueDestory(Queue* pq);
  20. void QueuePush(Queue* pq, QDataType x);
  21. void QueuePop(Queue* pq);
  22. bool QueueEmpty(Queue* pq);
  23. size_t QueueSize(Queue* pq);
  24. QDataType QueueFront(Queue* pq);
  25. QDataType QueueBack(Queue* pq);

Queue.c

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include"Queue.h"
  3. void QueueInit(Queue* pq)
  4. {
  5. assert(pq);
  6. pq->head = NULL;
  7. pq->tail = NULL;
  8. }
  9. void QueueDestroy(Queue* pq) //复盘!!!
  10. {
  11. assert(pq);
  12. QNode* cur = pq->head;
  13. while (cur)
  14. {
  15. QNode* next = cur->next;
  16. free(cur);
  17. cur = next;
  18. }
  19. pq->head =pq->tail = NULL;
  20. }
  21. void QueuePush(Queue* pq, QDataType x)
  22. {
  23. assert(pq);
  24. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  25. assert(newnode);
  26. newnode->next = NULL;
  27. newnode->data = x;
  28. if (pq->tail == NULL)
  29. {
  30. assert(pq->head==NULL);
  31. pq->head = pq->tail = newnode;
  32. }
  33. else
  34. {
  35. pq->tail->next = newnode;
  36. pq->tail = newnode; //写的时候漏了!!!
  37. }
  38. }
  39. void QueuePop(Queue* pq)
  40. {
  41. assert(pq);
  42. assert(pq->head && pq->tail);
  43. if (pq->head == pq->tail)
  44. {
  45. free(pq->head);
  46. pq->head = pq->tail = NULL;
  47. }
  48. else
  49. {
  50. QNode* next = pq->head->next;
  51. free(pq->head);
  52. pq->head = next;
  53. }
  54. }
  55. bool QueueEmpty(Queue* pq)
  56. {
  57. assert(pq);
  58. //return pq->head == NULL && pq->tail == NULL;
  59. return pq->head == NULL;
  60. }
  61. size_t QueueSize(Queue* pq)
  62. {
  63. assert(pq);
  64. size_t size = 0;
  65. QNode* cur = pq->head;
  66. while (cur)
  67. {
  68. size++;
  69. cur = cur->next;
  70. }
  71. return size;
  72. }
  73. QDataType QueueFront(Queue* pq)
  74. {
  75. assert(pq);
  76. assert(pq->head);
  77. return pq->head->data;
  78. }
  79. QDataType QueueBack(Queue* pq)
  80. {
  81. assert(pq);
  82. assert(pq->tail);
  83. return pq->tail->data;
  84. }

Test.c

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include"Queue.h"
  3. typedef int BTDataType;
  4. typedef struct BinaryTreeNode
  5. {
  6. struct BinaryTreeNode* left;
  7. struct BinaryTreeNode* right;
  8. BTDataType data;
  9. }BTNode;
  10. BTNode* BuyBTNode(BTDataType x)
  11. {
  12. BTNode* node = (BTNode*)malloc(sizeof(BTNode));
  13. if (node == NULL)
  14. {
  15. printf("malloc fail");
  16. exit(-1);
  17. }
  18. node->data = x;
  19. node->left = node->right = NULL;
  20. return node;
  21. }
  22. BTNode* CreatBinaryTree()
  23. {
  24. BTNode* node1 = BuyBTNode(1);
  25. BTNode* node2 = BuyBTNode(2);
  26. BTNode* node3 = BuyBTNode(3);
  27. BTNode* node4 = BuyBTNode(4);
  28. BTNode* node5 = BuyBTNode(5);
  29. BTNode* node6 = BuyBTNode(6);
  30. node1->left = node2;
  31. node1->right = node4;
  32. node2->left = node3;
  33. node4->left = node5;
  34. node4->right = node6;
  35. return node1;
  36. }
  37. //思路:
  38. //1、先把跟入队列,借助队列,先进先出的性质。
  39. //2、上 - -层的节点出的时候,带下一层的节点进去。
  40. void LevelOrder(BTNode* root)
  41. {
  42. Queue q;
  43. QueueInit(&q);
  44. if (root)
  45. {
  46. QueuePush(&q, root);
  47. }
  48. while (!QueueEmpty(&q))
  49. {
  50. BTNode* front = QueueFront(&q);
  51. QueuePop(&q);
  52. printf("%d ", front->data);
  53. if (front->left)
  54. {
  55. QueuePush(&q, front->left);
  56. }
  57. if (front->right)
  58. {
  59. QueuePush(&q, front->right);
  60. }
  61. }
  62. printf("\n");
  63. QueueDestroy(&q);
  64. }
  65. int main()
  66. {
  67. BTNode* tree = CreatBinaryTree();
  68. LevelOrder(tree); //层序遍历
  69. return 0;
  70. }

运行结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/669834
推荐阅读
相关标签
  

闽ICP备14008679号