二叉树基本操作续一:二叉树建立、节点数统计
在上一篇:二叉树基本操作 中,我们描述了二叉树的递归遍历函数。在这里主要是给出这些函数的测试代码,为了测试更加方便,我们实现了三个新的函数:建立二叉树、统计二叉树叶子节点数量、统计二叉树总节点数量。(二叉树的定义用上篇文章中的定义)
二叉树建立:
1 tree_pointer create_bin_tree() 2 { 3 tree_pointer node; 4 int x; 5 scanf("%d", &x); 6 if (x == 0) { 7 node = NULL; 8 }else { 9 node = (tree_pointer) malloc (sizeof(struct node)); 10 node->data = x; 11 printf("Enter %d left_child: ", node->data); 12 node->left_child = create_bin_tree(); 13 printf("Enter %d right_child: ", node->data); 14 node->right_child = create_bin_tree(); 15 } 16 17 return node; 18 }
从上篇的定义中我们知道二叉树节点里面存储的是整数,create函数为了便于处理,约定存储的数据不能为0,输入0时,表示这个节点为空。
仔细观察这个函数,就会发现这个函数和前序递归遍历函数很相像,最大的区别就是把遍历函数中的printf操作替换成了:当输入一个合法的需要插入的节点值时,就分配新的节点存储这个值,然后再分别用当前节点的左右儿子作为参数进行递归调用。
统计叶子节点数和总的节点数:
1 //注意这两个函数的返回值类型不是void,而是int 2 3 //统计叶子节点数量 4 int leaf_num(tree_pointer ptr) 5 { 6 if (ptr) { //当前节点不为空 7 if (!ptr->left_child && !ptr->right_child) { 8 //如果当前节点左右儿子都是空,则这个节点为叶子,返回 1 9 //如果当前节点为root,说明这颗二叉树只有一个根节点,叶子节点自然为1 10 return 1; 11 } else { 12 //否则,递归调用左右儿子,把返回值相加,即得到总叶子节点数量 13 return leaf_num(ptr->left_child) + leaf_num(ptr->right_child); 14 } 15 } 16 return 0; 17 } 18 19 //统计全部节点数量 20 int node_num(tree_pointer ptr) 21 { 22 if (ptr) { //当前节点不为空 23 //节点数+1 ,再加上递归调用左右儿子返回的节点数,即得到总节点数量 24 return 1 + node_num(ptr->left_child) + node_num(ptr->right_child); 25 } 26 return 0; 27 }
如果对递归概念不是很熟悉的话,看到这两个函数会稍微有一点糊涂。但是仔细观察一下,会发现这两个函数其实也是和之前的create函数类似的:同样都是是前序遍历的一个变种。
在叶子节点统计函数中:preorder中的printf函数现在变成了一个if判断,如果该节点左右儿子都为空,即当前节点为叶子节点,就返回 1,但如果存在左儿子或者右儿子的话,就说明当前节点不是叶子,就进入else流程,递归统计左右儿子,把统计到的数量相加,就得到叶子节点数。换种说法就是:对一颗二叉树进行前序遍历,每遇到一个节点就判断当前节点是否是叶子节点,是的话就返回 1,不是的话继续遍历,代码13行,就把这些递归遍历中返回的 1 相加在了一起,最后得到的就是叶子节点数量。(其实回顾上篇中描述的遍历函数的执行过程可以发现:在遍历函数中也可以在一个是否是叶子节点的if判断,如果是,就输出,然后直接return。因为如果节点是叶子节点,它的左右儿子都为空,所以无论什么遍历,以这个叶子节点为根节点的二叉树(回顾二叉树定义),遍历输出都一样。直接返回从系统层面看,少了两次函数调用,即少了两次入栈出栈和if判断)。
在总节点统计函数中:就是把前序遍历的三个步骤,printf ,preorder(ptr->lc),preorder(ptr->rc),的返回值直接相加。(有printf说明当前节点不为空,所以前序遍历中的printf在node_num函数中就变成了 +1)。
正如上一篇中说的:二叉树是一个递归定义的数据结构,所以它的大部分操作都非常适合用递归方式实现,而这些操作中最重要的就是三种递归遍历方式,其他很多操作都可以基于这三种递归遍历操作变形。
这些函数的完成测试代码如下:
(注意:代码中iter_开头的遍历函数,是三种遍历函数的迭代函数,本文中并没有给出实现,下篇文章中单独给出函数实现)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define MAXSIZE 100 5 6 typedef struct node *tree_pointer; 7 typedef struct node { 8 int data; 9 tree_pointer left_child, right_child; 10 }; 11 12 tree_pointer create_bin_tree(); 13 void preorder(tree_pointer ptr); 14 void iter_preorder(tree_pointer ptr); 15 void inorder(tree_pointer ptr); 16 void iter_inorder(tree_pointer ptr); 17 void postorder(tree_pointer ptr); 18 void iter_postorder(tree_pointer ptr); 19 int leaf_num(tree_pointer ptr); 20 void show_tree(tree_pointer ptr); 21 int node_num(tree_pointer ptr); 22 23 int main(void) 24 { 25 tree_pointer root = NULL; 26 int choice = 0; 27 do { 28 printf("\n"); 29 printf(" BinaryTree \n"); 30 printf(" *******************\n"); 31 printf(" * *\n"); 32 printf(" * Main Menu *\n"); 33 printf(" * 1. create: *\n"); 34 printf(" * 2. preorder/iter_preorder: *\n"); 35 printf(" * 3. inorder/iter_inorder: *\n"); 36 printf(" * 4. postorder/iter_postorder: *\n"); 37 printf(" * 5. leaf_num: *\n"); 38 printf(" * 6. show_tree: *\n"); 39 printf(" * 7. node_num: *\n"); 40 printf(" * 8. quit!!! *\n"); 41 printf(" *******************\n"); 42 printf(" Enter your choice (1-8): "); 43 scanf("%d", &choice); 44 45 switch (choice) { 46 case 1: 47 printf("Begin create: enter root: \n"); 48 root = create_bin_tree(); 49 printf("Finished!!!\n"); 50 break; 51 case 2: 52 printf("\n preorder: \n"); 53 preorder(root); 54 printf("\n iter_preorder: \n"); 55 iter_preorder(root); 56 break; 57 case 3: 58 printf("\n inorder: \n"); 59 inorder(root); 60 printf("\n iter_inorder: \n"); 61 iter_inorder(root); 62 break; 63 case 4: 64 printf("\n postorder: \n"); 65 postorder(root); 66 printf("\n iter_postorder: \n"); 67 iter_postorder(root); 68 break; 69 case 5: 70 printf("\n leaf_num: \n"); 71 printf(" %d\n", leaf_num(root)); 72 break; 73 case 6: 74 printf("\n show_tree: \n"); 75 show_tree(root); 76 break; 77 case 7: 78 printf("\n node_num: \n %d\n", node_num(root)); 79 break; 80 case 8: 81 printf("\n quit.\n"); 82 exit(0); 83 break; 84 default: 85 break; 86 } 87 }while (choice <= 8); 88 89 return 0; 90 } 91 92 tree_pointer create_bin_tree() 93 { 94 tree_pointer node; 95 int x; 96 scanf("%d", &x); 97 if (x == 0) { 98 node = NULL; 99 }else { 100 node = (tree_pointer) malloc (sizeof(struct node)); 101 node->data = x; 102 printf("Enter %d left_child: ", node->data); 103 node->left_child = create_bin_tree(); 104 printf("Enter %d right_child: ", node->data); 105 node->right_child = create_bin_tree(); 106 } 107 108 return node; 109 } 110 111 void preorder(tree_pointer ptr) 112 { 113 if (ptr) { 114 printf("\t%d", ptr->data); 115 preorder(ptr->left_child); 116 preorder(ptr->right_child); 117 } 118 } 119 void iter_preorder(tree_pointer ptr) 120 { 121 } 122 123 void inorder(tree_pointer ptr) 124 { 125 if (ptr) { 126 inorder(ptr->left_child); 127 printf("\t%d", ptr->data); 128 inorder(ptr->right_child); 129 } 130 } 131 void iter_inorder(tree_pointer ptr) 132 { 133 134 } 135 136 void postorder(tree_pointer ptr) 137 { 138 if (ptr) { 139 postorder(ptr->left_child); 140 postorder(ptr->right_child); 141 printf("\t%d", ptr->data); 142 } 143 } 144 void iter_postorder(tree_pointer ptr) 145 {} 146 147 int leaf_num(tree_pointer ptr) 148 { 149 if (ptr) { 150 if (!ptr->left_child && !ptr->right_child) { 151 return 1; 152 } else { 153 return leaf_num(ptr->left_child) + leaf_num(ptr->right_child); 154 } 155 } 156 return 0; 157 } 158 159 void show_tree(tree_pointer ptr) 160 { 161 } 162 163 int node_num(tree_pointer ptr) 164 { 165 if (ptr) { 166 return 1 + node_num(ptr->left_child) + node_num(ptr->right_child); 167 } 168 return 0; 169 }