"排序二叉树"之探幽
/*怎么理解排序二叉树呢?在二叉树的基本定义上增加两个基本条件:
(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 }
"排序二叉树"之探幽的更多相关文章
- C++11 智能指针unique_ptr使用 -- 以排序二叉树为例
用智能指针可以简化内存管理.以树为例,如果用普通指针,通常是在插入新节点时用new,在析构函数中调用delete:但有了unique_ptr类型的智能指针,就不需要在析构函数中delete了,因为当u ...
- 记忆化搜索 codevs 2241 排序二叉树
codevs 2241 排序二叉树 ★ 输入文件:bstree.in 输出文件:bstree.out 简单对比时间限制:1 s 内存限制:128 MB [问题描述] 一个边长为n的正三 ...
- 数据结构与算法系列研究五——树、二叉树、三叉树、平衡排序二叉树AVL
树.二叉树.三叉树.平衡排序二叉树AVL 一.树的定义 树是计算机算法最重要的非线性结构.树中每个数据元素至多有一个直接前驱,但可以有多个直接后继.树是一种以分支关系定义的层次结构. a.树是n ...
- c++(排序二叉树线索化)
前面我们谈到了排序二叉树,还没有熟悉的同学可以看一下这个,二叉树基本操作.二叉树插入.二叉树删除1.删除2.删除3.但是排序二叉树也不是没有缺点,比如说,如果我们想在排序二叉树中删除一段数据的节点怎么 ...
- c++(排序二叉树删除)
相比较节点的添加,平衡二叉树的删除要复杂一些.因为在删除的过程中,你要考虑到不同的情况,针对每一种不同的情况,你要有针对性的反应和调整.所以在代码编写的过程中,我们可以一边写代码,一边写测试用例.编写 ...
- c++(排序二叉树)
前面我们讲过双向链表的数据结构.每一个循环节点有两个指针,一个指向前面一个节点,一个指向后继节点,这样所有的节点像一颗颗珍珠一样被一根线穿在了一起.然而今天我们讨论的数据结构却有一点不同,它有三个节点 ...
- JavaScript实现排序二叉树的相关算法
1.创建排序二叉树的构造函数 /** * 创建排序二叉树的构造函数 * @param valArr 排序二叉树中节点的值 * @constructor */ function BinaryTree(v ...
- Java编程的逻辑 (42) - 排序二叉树
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- (原创)像极了爱情的详解排序二叉树,一秒get
排序二叉树(建立.查找.删除) 二叉树我们已经非常熟悉了,但是除了寻常的储存数据.遍历结构,我们还能用二叉树做什么呢? 我们都知道不同的遍历方式会对相同的树中产生不同的序列结果,排序二叉树就是利用二叉 ...
随机推荐
- 我的第一个MVC程序(SpringMVC的环境搭建与实例运用)
做一个完整点的mvc框架的搭建流程吧 Spring包含jar包下载 SpingMVC是基于Spring的一种关于web的解决方案,所以,使用springMVC,首先要准备有关Spring的一些jar包 ...
- [刘阳Java]_精选20道Java多线程面试题
1. 多线程使用的优缺点? 优点: (1)多线程技术使程序的响应速度更快 (2)当前没有进行处理的任务可以将处理器时间让给其它任务 (3)占用大量处理时间的任务可以定期将处理器时间让给其它任务 (4) ...
- node fs-extra文件操作模块的使用(支持同步、异步)
用法参见:fs-extra
- like %和-的区别与使用
通配符的分类: %百分号通配符:表示任何字符出现任意次数(可以是0次). 下划线通配符:表示只能匹配单个字符,不能多也不能少,就是一个字符. like操作符: LIKE作用是指示mysql后面的搜索模 ...
- [考试总结]noip模拟12
菜 今天总体来说 菜爆了,打了 \(3\) 个暴力,没有一个是正解,并且每一个分数都低得要命... 主要还是太菜了... 第一题开题发现和昨天 \(T3\) 一样,然而因为还没学可持久化数据结构就咕掉 ...
- Apache Flink jobmanager/logs路径遍历CVE-2020-17519
影响版本 1.11.0 1.11.1 1.11.2 poc http://192.168.49.2:8081/jobmanager/logs/..%252f..%252f..%252f..%252f. ...
- js 倒计时退出
<p>您已提交成功!<span id='submit_second'></span></p> $("#submit_second") ...
- Dubbo 实现一个Route Factory(用于灰度发布)
Dubbo 可以实现的扩展很多, 官方文档在这: https://dubbo.apache.org/zh/docs/v2.7/dev/impls/ (太简单了....) 下面我们实现一个Route F ...
- ASP.NET Core下FreeSql的仓储事务
ASP.NET Core下FreeSql的仓储事务 第一步:配置 Startup.cs 注入 引入包 dotnet add package FreeSql dotnet add package Fre ...
- Arp欺骗和DNS投毒
中间人攻击 ARP缓存攻击 ARP(Address Resolution Protocol,地址解析协议)是一个位于TCP/IP协议栈中的网络层,负责将某个IP地址解析成对应的MAC地址.简单来说,就 ...