"排序二叉树"之探幽
/*怎么理解排序二叉树呢?在二叉树的基本定义上增加两个基本条件:
(1)所有左子树的节点数值都小于此节点的数值;
(2)所有右节点的数值都大于此节点的数值。
*/
1 /************************头文件部分**************************/
2 /********************************************************
3 filename :tree_head.h
4 time :2013-11-13
5 copyrignt:none
6 author :cheng
7 **********************************************************/
8
9 #ifndef _DOUBLE_TREE_
10 #define _DOUBLE_TREE_
11
12 #include <assert.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16
17 typedef enum{
18 false = 0,true = !false
19 }bool;
20
21 /*
22 二叉树节点的数据结构,我们看到每一个数据节点有三个指针,分别是:指向父母的指针,指向左孩子的指针,指向右孩子的指针。每一个节点都是通过指针相互连接的。相连指针的关系都是父子关系。
23 */
24 typedef struct _sort_double_tree
25 {
26 int data;
27 struct _sort_double_tree* parent;
28 struct _sort_double_tree* left_child;
29 struct _sort_double_tree* right_child;
30
31 }TREE_NODE;
32
33 /*提供一个测试用的结构*/
34 typedef struct _func_mode_manager
35 {
36 void* (*find_data_in_tree_node)(const TREE_NODE* pTreeNode, int data);
37 int (*count_node_number_in_tree)(const TREE_NODE* pTreeNode);
38 void (*print_all_node_data)(const TREE_NODE* pTreeNode);
39 bool (*delete_node_from_tree)(TREE_NODE** ppTreeNode, int data);
40 bool (*insert_node_into_tree)(TREE_NODE **ppTreeNode,int data);
41
42 }Manager_mode;
43
44 /*一些功能函数*/
45 TREE_NODE* create_tree_node(int data);
46 TREE_NODE* create_double_tree(TREE_NODE *node,int *data,int mum);
47 TREE_NODE* find_data_in_tree_node(const TREE_NODE* pTreeNode, int data);
48 TREE_NODE* find_max_node(const TREE_NODE *pTreeNode);
49 int count_node_number_in_tree(const TREE_NODE* pTreeNode);
50 void print_all_node_data(const TREE_NODE* pTreeNode);
51 int calculate_height_of_tree(const TREE_NODE* pTreeNode);
52 bool insert_node_into_tree(TREE_NODE** ppTreeNode, int data);
53 bool delete_node_from_tree(TREE_NODE** ppTreeNode, int data);
54
55 Manager_mode* alloc_test_mode(void);
56
57 #endif //endif tree_head.h
58
59 /***************************功能函数实现部分**********************/
60 #include "tree_head.h"
61
62 static bool _insert_node_into_tree(TREE_NODE** ppTreeNode, int data, TREE_NODE* pParent);
63
64 static bool _delete_node_from_tree(TREE_NODE* pTreeNode);
65 /************************************************
66 函数名:alloc_test_mode
67 功能:初始化Manager_mode结构的成员
68 返回:Manager_mode结构类型的变量
69 *************************************************/
70 Manager_mode* alloc_test_mode(void)
71 {
72 Manager_mode* manager=NULL;
73 manager = (Manager_mode*)malloc(sizeof(Manager_mode));
74 assert(manager != NULL);
75
76 manager->find_data_in_tree_node=find_data_in_tree_node;
77 manager->count_node_number_in_tree=count_node_number_in_tree;
78 manager->print_all_node_data=print_all_node_data;
79 manager->delete_node_from_tree=delete_node_from_tree;
80 manager->insert_node_into_tree=insert_node_into_tree;
81
82 return manager;
83 }
84 /************************************************
85 函数名:create_tree_node
86 功能:创建一个二叉节点
87 返回:返回一个初始化的节点
88 *************************************************/
89 TREE_NODE* create_tree_node(int data)
90 {
91 TREE_NODE* pTreeNode = NULL;
92 pTreeNode = (TREE_NODE*)malloc(sizeof(TREE_NODE));
93 assert(NULL != pTreeNode);
94
95 memset(pTreeNode, 0, sizeof(TREE_NODE));
96 pTreeNode->data = data;
97 return pTreeNode;
98 }
99 /************************************************
100 函数名:create_double_tree
101 功能:创建一颗排序二叉树
102 返回:返回传入的头节点
103 *************************************************/
104 TREE_NODE* create_double_tree(TREE_NODE *node,int *data,int num)
105 {
106 assert((node != NULL) && (data !=NULL) && (num > 0));
107 int i=0;
108 bool flag = true;
109 while((i < num) && flag){
110 flag = insert_node_into_tree(&node,data[i]);
111 i++;
112 }
113
114 return node;
115 }
116 /************************************************
117 函数名:insert_node_into_tree
118 功能:向排序二叉树中插入结点
119 返回:成功返回true,好、失败返回false
120 *************************************************/
121 /*分析:
122 一般来说,二叉树的插入主要分为以下两个步骤:
123 1) 对当前的参数进行判断,因为需要考虑到头结点,所以我们使用了指针的指针作为函数的输入参数
124 2) 分情况讨论:
125 如果原来二叉树连根节点都没有,那么这个新插入的数据就是根节点;
126 如果原来的二叉树有根节点,那我们判断这个数据是否存在过,如果存在,那么返回;如果不存在,那么继续插入数据。
127 那继续插入的数据怎么保存呢?又要分三种情况:
128 1)如果插入的数据小于当前节点的数据,那么往当前节点的左子树方向继续寻找插入位置
129 2)如果插入的数据大于当前插入的位置,那么往当前节点的右子树方向继续寻找插入位置
130 3)如果方向当前的节点为空,那么表示插入的位置找到了,插入数据即可 */
131 static bool _insert_node_into_tree(TREE_NODE** ppTreeNode, int data, TREE_NODE* pParent) ;
132 bool insert_node_into_tree(TREE_NODE** ppTreeNode, int data)
133 {
134 bool FALSE= false;
135 bool TRUE = true ;
136 if(NULL == ppTreeNode)
137 return FALSE;
138
139 if(NULL == *ppTreeNode)
140 {
141 *ppTreeNode = (TREE_NODE*)create_tree_node(data);
142 assert(NULL != *ppTreeNode);
143 return TRUE;
144 }
145 return _insert_node_into_tree(ppTreeNode, data, NULL);
146 }
147 static bool _insert_node_into_tree(TREE_NODE** ppTreeNode, int data, TREE_NODE* pParent)
148 {
149 bool TRUE = true ;
150 if(NULL == *ppTreeNode)
151 {
152 *ppTreeNode = create_tree_node(data);
153 assert(NULL != *ppTreeNode);
154 (*ppTreeNode)->parent = pParent;
155 return TRUE;
156 }
157 if(data < (*ppTreeNode)->data)
158 return _insert_node_into_tree(&(*ppTreeNode)->left_child, data, *ppTreeNode);
159 else
160 return _insert_node_into_tree(&(*ppTreeNode)->right_child, data, *ppTreeNode);
161 }
162
163 /************************************************
164 函数名:find_data_in_tree_node
165 功能:在排序二叉树中查询一个数据
166 返回:返回传入的头结点
167 *************************************************/
168 TREE_NODE* find_data_in_tree_node(const TREE_NODE* pTreeNode, int data)
169 {
170 if(NULL == pTreeNode)
171 return NULL;
172
173 if(data == pTreeNode->data)
174 return (TREE_NODE*)pTreeNode;
175 else if(data < pTreeNode->data)
176 return find_data_in_tree_node(pTreeNode->left_child, data);
177 else
178 return find_data_in_tree_node(pTreeNode->right_child, data);
179 }
180 /*分析:我们的查找是按照递归迭代进行的。因为整个二叉树是一个排序二叉树,所以我们的数据只需要和每一个节点依次比较就可以了,如果数值比节点数据小,那么向左继续遍历;反之向右继续遍历。如果遍历下去遇到了NULL指针,只能说明当前的数据在二叉树中还不存在 .*/
181
182 /************************************************
183 函数名:find_max_node
184 功能:在排序二叉树中寻找一个最大结点
185 返回:返回传入的头结点
186 *************************************************/
187 TREE_NODE* find_max_node(const TREE_NODE *pTreeNode)
188 {
189 if(NULL == pTreeNode)
190 return NULL;
191 if(((NULL == pTreeNode->left_child )&&(NULL == pTreeNode->right_child))||
192 ((NULL == pTreeNode->right_child)&&(NULL != pTreeNode->left_child)))
193 return pTreeNode;
194 else
195 return pTreeNode->right_child;
196 }
197 /************************************************
198 函数名:count_node_number_in_tree
199 功能:统计排序二叉树中结点的个数
200 返回:返回结点的个数
201 *************************************************/
202 int count_node_number_in_tree(const TREE_NODE* pTreeNode)
203 {
204 if(NULL == pTreeNode)
205 return 0;
206
207 return 1 + count_node_number_in_tree(pTreeNode->left_child)
208 + count_node_number_in_tree(pTreeNode->right_child);
209 }
210 /*******************************************************
211 函数名:print_all_node_data
212 功能:打印一颗排序二叉树各个结点的data值
213 返回:无
214 *******************************************************/
215 void print_all_node_data(const TREE_NODE* pTreeNode)
216 {
217 if(pTreeNode){
218 print_all_node_data(pTreeNode->left_child);
219 printf("%d\t", pTreeNode->data);
220 print_all_node_data(pTreeNode->right_child);
221 }
222 }
223 分析:因为二叉树本身的特殊性,按顺序打印二叉树的函数本身也比较简单。首先打印左子树的节点,然后打印本节点的数值,最后打印右子树节点的数值,这样所有节点的数值就都可以打印出来了。
224
225 /************************************************
226 函数名:calculate_height_of_tree
227 功能:计算树的高度
228 返回:树的高度
229 *************************************************/
230 int calculate_height_of_tree(const TREE_NODE* pTreeNode)
231 {
232 int left, right;
233 if(NULL == pTreeNode)
234 return 0;
235
236 left = calculate_height_of_tree(pTreeNode->left_child);
237 right = calculate_height_of_tree(pTreeNode->right_child);
238 return (left > right) ? (left + 1) : (right + 1);
239 }
240
241 /************************************************
242 函数名:delete_node_from_tree
243 功能:删除指定的数据并释放该结点空间
244 返回:成功返回true,失败返回false
245 *************************************************/
246 分析:
247 相比较节点的添加,平衡二叉树的删除要复杂一些。因为在删除的过程中,你要考虑到不同的情况,针对每一种不同的情况,你要有针对性的反应和调整。所以在代码编写的过程中,我们可以一边写代码,一边写测试用例。编写测试用例不光可以验证我们编写的代码是否正确,还能不断提高我们开发代码的自信心。这样,即使我们在开发过程对代码进行修改或者优化也不会担心害怕。
248 在排序二叉树的删除过程当中,我们应该怎么做呢?大体上分为下面三个步骤:
249 1)判断参数的合法性,判断参数是否在当前的二叉树当中
250 2)删除的节点是根节点,此时应该怎么调整
251 3)删除的节点是普通节点,此时又应该怎么调整
252 static bool _delete_node_from_tree(TREE_NODE* pTreeNode) ;
253 bool delete_node_from_tree(TREE_NODE** ppTreeNode, int data)
254 {
255 bool FALSE= false;
256 bool TRUE = true ;
257 TREE_NODE* pTreeNode;
258 TREE_NODE* pLeftMax;
259
260 if(NULL == ppTreeNode || NULL == *ppTreeNode)
261 return FALSE;
262
263 pTreeNode = find_data_in_tree_node(*ppTreeNode, data);
264 if(NULL == pTreeNode)
265 return FALSE;
266
267 if(*ppTreeNode == pTreeNode)
268 {
269 if(NULL == pTreeNode->left_child && NULL == pTreeNode->right_child){
270 *ppTreeNode = NULL;
271 }
272 else if(NULL != pTreeNode->left_child && NULL == pTreeNode->right_child){
273 *ppTreeNode = pTreeNode->left_child;
274 pTreeNode->left_child->parent = NULL;
275 }
276 else if(NULL == pTreeNode->left_child && NULL != pTreeNode->right_child){
277 *ppTreeNode = pTreeNode->right_child;
278 pTreeNode->right_child->parent = NULL;
279 }
280 else
281 {
282 pLeftMax = find_max_node(pTreeNode->left_child);
283 if(pLeftMax == pTreeNode->left_child)
284 {
285 *ppTreeNode = pTreeNode->left_child;
286 (*ppTreeNode)->right_child = pTreeNode->right_child;
287 (*ppTreeNode)->right_child->parent = *ppTreeNode;
288 (*ppTreeNode)->parent = NULL;
289 }
290 else
291 {
292 pTreeNode->data = pLeftMax->data;
293 pLeftMax->parent->right_child = pLeftMax->left_child;
294 pLeftMax->left_child->parent = pLeftMax->parent;
295 pTreeNode = pLeftMax;
296 }
297 }
298 free(pTreeNode);
299 return TRUE;
300 }
301 return _delete_node_from_tree(pTreeNode);
302 }
303
304 static bool _delete_node_from_tree(TREE_NODE* pTreeNode)
305 {
306 bool TRUE = true ;
307 TREE_NODE* pLeftMax;
308
309 if(NULL == pTreeNode-> left_child && NULL == pTreeNode->right_child)
310 {
311 if(pTreeNode == pTreeNode->parent->left_child)
312 pTreeNode->parent->left_child = NULL;
313 else
314 pTreeNode->parent->right_child = NULL;
315 }
316 else if(NULL != pTreeNode->left_child && NULL == pTreeNode->right_child)
317 {
318 pTreeNode->left_child->parent = pTreeNode->parent;
319
320 if(pTreeNode == pTreeNode->parent->left_child)
321 pTreeNode->parent->left_child = pTreeNode->left_child;
322 else
323 pTreeNode->parent->right_child = pTreeNode->left_child;
324 }
325 else if(NULL == pTreeNode->left_child && NULL != pTreeNode->right_child)
326 {
327 pTreeNode->right_child->parent = pTreeNode->parent;
328
329 if(pTreeNode == pTreeNode->parent->left_child)
330 pTreeNode->parent->left_child = pTreeNode->right_child;
331 else
332 pTreeNode->parent->right_child = pTreeNode->right_child;
333 }
334 else
335 {
336 pLeftMax = find_max_node(pTreeNode->left_child);
337 if(pLeftMax == pTreeNode->left_child)
338 {
339
340 if(pTreeNode == pTreeNode->parent->left_child)
341 pTreeNode->parent->left_child = pTreeNode->left_child;
342 else
343 pTreeNode->parent->right_child = pTreeNode->left_child;
344
345 pTreeNode->left_child->parent = pTreeNode->parent;
346 pTreeNode->left_child->right_child = pTreeNode->right_child;
347 pTreeNode->right_child->parent = pTreeNode-> left_child;
348
349 }
350 else
351 {
352 pTreeNode->data = pLeftMax->data;
353 pLeftMax->parent->right_child = pLeftMax->left_child;
354 pLeftMax->left_child->parent = pLeftMax->parent;
355 pTreeNode = pLeftMax;
356 }
357 }
358
359 free(pTreeNode);
360 return TRUE;
361 }
362
363 /******************下面是完整的测试例子(包含两种测试方法)************************/
364
365 #include "tree_head.h"
366
367 /***************************************************************************
368 name:main
369 function description:
370 call same sub function be used to test the create double tree
371 parameters:none
372 return:none
373 **************************************************************************/
374
375 int main(void)
376 {
377
378 int arr[12]={11,8,7,6,5,4,3,2,1,13,12};
379 TREE_NODE *node = NULL;
380 TREE_NODE *parent=NULL;
381 TREE_NODE *find=NULL;
382 parent = create_tree_node(10);
383 node = create_double_tree(parent,arr,12);
384
385 #if 1
386 printf("\nprint data in double tree\n");
387 print_all_node_data(node);
388 int num = count_node_number_in_tree(node);
389 printf("\nnode number is %d\n",num);
390 find = find_data_in_tree_node(node,10);
391 printf("\nfind the data1 is %d\n",find->data);
392 find = find_data_in_tree_node(node,1);
393 printf("\nfind the data2 is %d\n",find->data);
394 find = find_data_in_tree_node(node,9);
395 if(find == NULL)
396 printf("\nthe data %d isn`t in double tree\n",9);
397 if(delete_node_from_tree(&node , 10)){ //delete parent
398 printf("\nafter delete parent print data in double tree\n");
399 print_all_node_data(node);
400 }
401 if(delete_node_from_tree(&node , 7)){
402 printf("\nafter delete left child print data in double tree\n");
403 print_all_node_data(node);
404 }
405 if(delete_node_from_tree(&node , 11)){
406 printf("\nafter delete right child print data in double tree\n");
407 print_all_node_data(node);
408 }
409 free(node);
410 free(parent);
411 free(find);
412 #endif
413
414 #if 0
415 Manager_mode *this = NULL;
416 int num,data,count=0;
417 char ch[20]={0};
418 this = alloc_test_mode();
419 printf("please input test function num such as \n");
420 printf("\n\t0:test_print,1:test_count,2:test_find,3:test_delete ==>:");
421 scanf("%d",&num);
422 switch(num)
423 {
424 case 0:
425 this->print_all_node_data(node);
426 break;
427 case 1:
428 count=this->count_node_number_in_tree(node);
429 printf("\nnode number is %d\n",count);
430 break;
431 case 2:
432 printf("please input your want find number\n");
433 scanf("%d",&data);
434 find=this->find_data_in_tree_node(node,data);
435 if(find)
436 printf("\nfind the data is %d\n",find->data);
437 else
438 printf("\nthe data %d isn`t in double tree\n",data);
439 break ;
440 case 3:
441 printf("please input your want delete number:");
442 scanf("%d",&data);
443 if(this->delete_node_from_tree(&node,data))
444 printf("delete :%d ok\n",data);
445 else
446 printf("sorry no such data %d\n",data);
447 break;
448 default:
449 printf("you input number isn`t [0,1,2,3]\n");
450 printf("print the double tree\n");
451 this->print_all_node_data(node);
452 exit(1);
453 }
454 free(node);
455 free(parent);
456 if(find!= NULL)
457 free(find);
458 free(this);
459 #endif
460 return 0;
461 }