数据结构和算法分析学习笔记(三)--二叉查找树的懒惰删除(lazy deletion)

    这次的问题来自《数据结构与算法分析(C++描述)》的习题4.16,如下:

--------------------------

4.16  重做二叉查找树类以实现懒惰删除.注意,这将影响所有的例程.特别具有挑战性的是findMin和findMax,它们现在必须递归的完成.

--------------------------

    这题没有参考答案,我也不敢保证自己写的是对的,有不对的地方请指正,谢谢.先做一下说明,首先这只是一般的二叉查找树,不是AVL树.其次其中的printTree()函数只是将树中的结点按升序打印出来,不是像树的结构那样打印(可以参见这里的print()函数).最后,我定义的数据结构是在有重复项的情况下使用的.也就是说,每一个结点有一个记录数据出现次数的成员count(非负整数).当第一次插入某一数据时,count为1,以后每插入一次这一数据则它的count加1.而当某一数据没有被实际删除且它的count大于0时,对它的删除操作就是将它的count减1.当逻辑上被删除的结点(count值为0的结点)的数目达到实际上存在的总结点数目的一半时,就对二叉查找树进行真正的删除操作(delete).

    下面在课本中给出的一般二叉查找树结构(参见书中图4-16至图4-28)的基础上进行修改,得到我们所需要的结构.首先自然是结点的结构,如前所述,需要对结点BinaryNode类增加一个记录数据出现次数的成员count.而在BSTree类中,我们需要两个int型的私有成员分别统计树的总结点数和其中在逻辑上已经被删除的结点数.接下来是contains(),makeEmpty(),printTree()和insert(),这些都比较简单.

    我与课本上makeEmpty( BinaryNode * & t )(置空操作)的不同是,我没有在最后将节点置为NULL,而是在调用这个函数之后将实参置为NULL,不知道这样做会不会有什么错误.对于insert函数,我们可以看到这种结构的好处,对于那些仅在逻辑上被删除的数据,重新插入它只需要将count加1并且将delSize减1.

 1 template <typename Object>
2 class BSTree
3 {
4 public:
5 BSTree( ){theSize=delSize=0;root=NULL;}
6 ~BSTree( )
7 {
8 makeEmpty(root);
9 theSize=delSize=0;
10 }
11
12 bool contains( const Object & x ) const//是否包含某值
13 {return contains(x,root);}
14 bool isEmpty( ) const//是否为空
15 {return theSize-delSize==0;}
16 void printTree( ) const//打印树
17 {printTree(root);}
18
19 void makeEmpty( )//置空
20 {
21 makeEmpty(root);
22 theSize=delSize=0;root=NULL;
23 }
24 void insert( const Object & x )//插入值
25 {insert(x,root);}
26
27 private:
28 struct BinaryNode
29 {
30 Object element;//Object类型的值
31 BinaryNode *left;//左子树
32 BinaryNode *right;//右子树
33 int count; //计数
34
35 BinaryNode( const Object & theElement, BinaryNode *lt, BinaryNode *rt ,int c=1)//第一次插入值为1
36 : element( theElement ), left( lt ), right( rt ), count(c) { }
37 };
38
39 BinaryNode *root;//根结点
40 int theSize;
41 int delSize;
42
43 void insert( const Object & x, BinaryNode * & t ) ;
44
45 bool contains( const Object & x, BinaryNode *t ) const;
46 void makeEmpty( BinaryNode * & t );
47 void printTree( BinaryNode *t ) const
48 {
49 if(t)
50 {
51 printTree(t->left);
52 if(t->count>0)std::cout<<t->element<<" ";
53 printTree(t->right);
54 }
55 }
56 };
 1 template <typename Object>
2 bool BSTree<Object>::contains( const Object & x, BinaryNode *t ) const
3 {
4 if(!t)return false;
5 if(x<t->element)
6 return contains(x,t->left);
7 else if(x>t->element)
8 return contains(x,t->right);
9 else if(t->count>0)
10 return true;
11 else
12 return false;
13 }
14
15 template <typename Object>
16 void BSTree<Object>::makeEmpty( BinaryNode * & t )
17 {
18 if(t)
19 {
20 makeEmpty(t->left);
21 makeEmpty(t->right);
22 delete t;
23 }
24 }
25
26 template <typename Object>
27 void BSTree<Object>::insert( const Object & x, BinaryNode * & t )
28 {
29 if(t==NULL)
30 {
31 ++theSize;
32 t=new BinaryNode(x,NULL,NULL);//count默认为1
33 }
34 else if(x<t->element)
35 insert(x,t->left);
36 else if(x>t->element)
37 insert(x,t->right);
38 else if(t->count++==0)
39 --delSize;//如果count的值原本为0,则将其加1的同时要将delSize减1.
40 }

    接下来是remove操作,现在remove也显得代价更小,当删除某项数据时,只需要将它的count减1而不需要从树真正删除它.而当它的count为0时,则需要将delSize加1以表示又有一个结点在逻辑上已经被删除.最后再检查delSize是否达到了theSize的一半.如果达到,则执行真正的delete操作.

 1 template <typename Object>
2 void BSTree<Object>::remove( const Object & x, BinaryNode * & t )
3 {
4 if(t==NULL)return;
5 if(x<t->element)
6 remove(x,t->left);
7 else if(x>t->element)
8 remove(x,t->right);
9 else if(t->count>0&&--(t->count)==0&&(++delSize>=theSize-delSize))
10 //若count大于0,则减1;若减1后为0,则delSize加1;
11 //若delSize达到theSize的一半,则执行delete操作
12 {
13 delete_nodes(root);//真正的删除操作
14 theSize-=delSize;//更新总结点数
15 delSize=0;//delSize归零
16 }
17 }

    现在的问题是delete_nodes( BinaryNode * & t )例程的实现,我们要删除树中所有count为0的结点.我这里使用了递归的方法,如果某个结点不必删除,那么就继续对它的左子树和右子树执行delete_nodes().回想一般二叉查找树的删除,对于叶子结点,直接删除即可,这在delete_nodes()中也是一样.而对于只有一个非空子树的结点,直接删除后将它的非空子树"拼接"上即可,但这时需要对拼接上的子树继续执行delete_nodes().

    最后就是删除那些两个子树都非空的结点,我们还是在它的右子树上找到一个值最小的结点(注意:要在count大于0的结点中找),我们假设这个点为min.然后将t的值和count值都替换成min相应的值,同时将min的count值修改为0以保证稍后删除.最后,继续对t的左子树和右子树执行delete_nodes()操作.这里面一个可能存在的情况是t的右子树中所有结点的count值都为0,那么我们可以将它的右子树置空,这样t就变成了一个只有一个非空子树的结点.

    在实际的编程中,我觉得比较费脑筋的就是findMin()findMax()例程.以findMin(BinaryNode *t)为例,首先要找到t中的最小值,然后再升序一步步往上寻找第一个count大于0的结点.我采用的递归的方法,这一例程返回bool值,false表示没有找到最小值,也就意味着t为空或者t中所有结点的count均为0.而为了保存查找到的最小(最大)结点,我给BSTree类增加了两个私有成员,两个BinaryNode *类型的指针min和max.另外,我还提供了两个公有的函数,分别返回整颗树的最小和最大值,但是这两个函数需要在保证整颗树非空的情况下使用.

 1 private:
2 BinaryNode *min;
3 BinaryNode *max;
4
5 template <typename Object>
6 bool BSTree<Object>::findMin( BinaryNode *t )
7 {
8 if(t)
9 {
10 if(findMin(t->left))return true;//在t的左子树中找到count大于0的点,查找结束.
11 if(t->count>0){min=t;return true;}//找到count大于0的点,查找结束.
12 return findMin(t->right);//否则继续查找右子树
13 }
14 return false;//空结点则返回false
15 }
16
17 template <typename Object>
18 bool BSTree<Object>::findMax( BinaryNode *t)
19 {
20 if(t)
21 {
22 if(findMax(t->right))return true;
23 if(t->count>0){max=t;return true;}
24 return findMax(t->left);
25 }
26 return false;
27 }
28
29 template <typename Object>
30 void BSTree<Object>::delete_nodes(BinaryNode * & t )
31 {
32 if(t==NULL)return;//空子树,do nothing
33 if(t->count==0)//t需要被删除
34 {
35 if((t->left!=NULL)&&(t->right!=NULL)&&findMin(t->right))
36 //t的两个子树均非空,且在它的右子树中找到了可用的最小结点
37 {
38 t->element=min->element;
39 t->count=min->count;
40 min->count=0;//右子树中的最小结点即将被删除
41 delete_nodes(t->left);
42 delete_nodes(t->right);
43 }
44 else
45 {
46 if((t->left!=NULL)&&(t->right!=NULL))
47 //t的右子树中的所有结点的count均为0,则将其置空.
48 {makeEmpty(t->right);t->right=NULL;}
49 BinaryNode *oldnode=t;
50 t=t->left?t->left:t->right;
51 delete oldnode;
52 if(t!=NULL)delete_nodes(t);//若新的t非空,继续对它执行删除操作
53 }
54 }
55 else//t不需要被删除,继续在它的子树中查找
56 {
57 delete_nodes(t->left);
58 delete_nodes(t->right);
59 }
60 }
61
62 public:
63 const Object & findMin( )//返回最小值
64 {
65 if(!findMin(root))
66 std::cerr<<"The tree is Empty!"<<std::endl;
67 return min->element;
68 }
69
70 const Object & findMax( )//返回最大值
71 {
72 if(!findMax(root))
73 std::cerr<<"The tree is Empty!"<<std::endl;
74 return max->element;
75 }

    最后提供我的BSTree.h文件,有不对的地方请指正,谢谢.
    BSTree.h



转载于:https://www.cnblogs.com/heqile/archive/2011/12/08/2280120.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/261097.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javascript类型系统——正则表达式RegExp类型

原文:javascript类型系统——正则表达式RegExp类型 目录 [1]对象 [2]实例属性 [3]静态属性[4]实例方法前面的话 前面已经介绍过javascript中正则表达式的基础语法。javascript的RegExp类表示正则表达式&#xff0c;String和RegExp都定义了方法&#xff0c;使用正则表达式可以进…

abap调用Linux命令,ABAP中输入write命令使用

1.默认输出:如果没有指定输出长度或格式,系统自动按数据类型默认值输出.eg:data i1 type i value 12345678 . write i1.数据类型I默认长度为11位,右对齐,而i1只有8位长度,所以左边以空格填充.结果: 12345678.2.指定输出.语法1: write [/p(l)] v1[o(sl)]其中:v1可以是变量…

校验值的计算----移位算法

一直以来都是从互联网获得知识&#xff0c;感觉没贡献过什么。 最近做项目研究了一个算法&#xff0c;就写一个共享出来&#xff0c;给需要的人一些参考。 说明&#xff1a; 有一个40字节的数组&#xff0c;前38个字节表示数据&#xff0c;后两个字节表示校验值。 校验值是前面…

【转】符串搜索工具及XenoCode字符串自动解密工具

【http://www.cnblogs.com/chengchen/archive/2008/07/14/1242244.html】 我们在使用OD破解软件的时候&#xff0c;字符串搜索是最常用的功能之一。但是在DONET平台下似乎没有什么比较好的软件&#xff0c;于是自己动手写了一个软件。这个软件可以自动搜索DONET程序集中的所有的…

编译Ruby2.0 问题解决

compiling readline.creadline.c: In function ‘Init_readline’:readline.c:1886:26: error: ‘Function’ undeclared (first use in this function) rl_pre_input_hook (Function *)readline_pre_input_hook; ^readline.c:1886:26: note: ea…

linux 封装python,基于linux下python学习(封装)

一、面向对象类的设计&#xff1a;在程序开发中&#xff0c;要设计一个类&#xff0c;通常需要满足以下三个要求&#xff1a;1、类名 这类事物的名字&#xff0c;满足大驼峰命名法2、属性 这类事物具有什么样的特征3、 方法 这类事物具有什么样的行为eg:小明今年18岁&#xf…

图解RAID 0, RAID 1, RAID 5, RAID 10

RAID(Redundant Array of Independent Disk 独立冗余磁盘阵列)技术是加州大学伯克利分校1987年提出&#xff0c;最初是为了组合小的廉价磁盘来代替大的昂贵磁盘&#xff0c;同时希望磁盘失效时不会使对数据的访问受损 失而开发出一定水平的数据保护技术。RAID就是一种由多块廉价…

程序员的不归路

程序员的不归路 数百条线程为何频繁断开&#xff0c;回调函数为何迟迟不回&#xff0c;oracle的数据为何屡遭黑手&#xff0c;连环嵌套循环案&#xff0c;究竟是何人所为&#xff1f;&#xff1f; 诡异的bug背后究竟是人是鬼&#xff0c;传来的对象次次为空的事件又影藏着什么&…

Spring JPA 中的Repository体系

为什么80%的码农都做不了架构师&#xff1f;>>> Repository体系 org.springframework.data.repository.Repository<T, ID> ---- Repository体系的顶级接口,是个空接口 interface CrudRepository<T, ID extends Serializable> extends Repository<T,…

Codeforces 258B Little Elephant and Elections

题意&#xff1a;有7个人从m个数中任选一个不重复的&#xff0c;其中4和7是幸运数&#xff0c;一个人的幸运值等于他所选的数字中所有4的个数7的个数。求一个人的幸运值比其他6人幸运值总和大的方案数。 1 #include <iostream>2 #define MOD 10000000073 using namespace…

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)

Python 数据分析 Matplotlib篇 plot设置线条样式(第2讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

装 linux后 win7消失了,win7系统重装后ubuntu启动消失不见的解决方法

很多小伙伴都遇到过win7系统重装后ubuntu启动消失不见的困惑吧&#xff0c;一些朋友看过网上零散的win7系统重装后ubuntu启动消失不见的处理方法&#xff0c;并没有完完全全明白win7系统重装后ubuntu启动消失不见是如何解决的&#xff0c;今天小编准备了简单的解决办法&#xf…

微服务扩展新途径:Messaging

【编者按】服务编排是微服务设置的一个重要方面。本文在利用 ActiveMQ 虚拟话题来实现这一目标的同时&#xff0c;还会提供实用性指导。文章系国内 ITOM 管理平台 OneAPM 编译呈现。 目前&#xff0c;微服务使用已十分普遍&#xff0c;利用服务编排&#xff08;而不是服务编制&…

C语言发送邮件

c语言发送邮件Linux下使用c语言发送邮件 领导交代一个任务&#xff0c;需要将服务器上的df -hl的执行结果定时发给他。 尝试使用sendmail来发邮件&#xff0c;但是后来放弃了&#xff0c;并不是所有的服务器上都安装了sendmail。 于是&#xff0c;就用c写一个吧&#xff0c;还…

linux虚拟主机泛解析,Apache虚拟主机的配置和泛域名解析实现代码

Apache虚拟主机的配置和泛域名解析实现代码更新时间&#xff1a;2012年03月11日 00:28:19 作者&#xff1a;Apache虚拟主机的配置和泛域名解析实现代码&#xff0c;需要的朋友可以参考下虚拟主机的配置基于IP地址的虚拟主机配置Listen 80DocumentRoot /www/jb51ServerName ww…

GetBuffer与ReleaseBuffer的用法

GetBuffer与ReleaseBuffer的用法&#xff0c;CString剖析2008-06-07 22:39 这是一个非常容易被用错的函数&#xff0c;主要可能是由于大家对它的功能不太了解。其实点破的话&#xff0c;也不是那么深奥。 GetBuffer(int size)是用来返回一个你所指定大小可写内存的成员方法。它…

Linux 路径和目录问题

相对路径和绝对路径 绝对路径&#xff1a; 一定要从跟目录写起&#xff0c;比如/usr/share.doc 相对路径&#xff0c;不是由/写起的&#xff0c;相对指的是相对于目前的工作目录的路径。比如cd ../man&#xff0c;..指的是上一级目录 目录的相关操作&#xff1a; . 此层目录 ..…

关于js中function(e) e的理解

e是事件&#xff0c;在firefox中只能在事件现场使用window.event&#xff0c;所以只有把event传给函数使用。 为了兼容FF和其它浏览器&#xff0c;一般会在函数里重新给e赋值&#xff1a;e window.event || e;也就是说&#xff0c;如果window.event存在&#xff0c;则该浏览器…

socket用法linux,linux socket编程,要用到哪些函数,和用法介绍?_Linux_天涯问答_天涯社区...

4、获得地址信息/* 来源: http://www.jfox.info/c/a/ic/18071a.html */#include #include /* for strncpy */#include #include #include #include #include intmain(){int fd;struct ifreq ifr;fd socket(AF_INET, SOCK_DGRAM, 0);/* I want to get an IPv4 IP address */if…

深入浅出 RPC - 浅出篇+深入篇

摘自: http://blog.csdn.net/mindfloating/article/details/39473807 近几年的项目中&#xff0c;服务化和微服务化渐渐成为中大型分布式系统架构的主流方式&#xff0c;而 RPC 在其中扮演着关键的作用。在平时的日常开发中我们都在隐式或显式的使用 RPC&#xff0c;一些刚入行…