当前位置:文档之家› 树的四种分类

树的四种分类

Search trees:实例--二叉搜索树什么是二叉搜索树二叉搜索树(Binary Search Tree)是一棵有序的二叉树,所以我们也可以称它为二叉排序树(不知道二叉树的童鞋,先看看二叉树:传送门)。

具有以下性质的二叉树我们称之为二叉搜索树:若它的左子树不为空,那么左子树上的所有值均小于它的根节点;若它的右子树不为空,那么右子树上所有值均大于它的根节点。

它的左子树和右子树分别也为二叉搜索树。

2、二叉搜索树的结构二叉搜索树能够高效的进行一下操作:①插入一个数值②查询是否包含某个数值③删除某个数值根据实现的不同,还可以实现其他各种操作,这是一种使用性很高的数据结构。

我们来看一个例子:这就是二叉搜索树的存储结构,所有的节点,都满足左子树上的比自己小,而右子树上的所有节点比自己大。

二叉搜索树因为其有序性,所以它能够高效的管理数的集合(1)查询我们查找是否存在17:<1>根节点是7,因为小于17,所以去右子树查找<2>走到节点12,还是小于17,所以继续往右子树找<3>走到节点17,此时找到17。

(2)插入我们使用查找的方式进行插入,以数字6为例,如图所示:(3)删除删除操作相对之前的其他两种操作,就略显复杂一些。

一般来说我们可以分为三种情况:<1>需要删除的节点没有左儿子,那么就把右儿子提上去<2>需要删除的节点的左儿子没有右儿子,那么就把左儿子提上去<3>不满足上述的两种情况的话,就把左子树中最大的节点放到要删除的节点上。

3、二叉搜索树的复杂度无论我们执行哪一个操作,其所花的时间都和树的高度成正比。

我们不难得知,二叉搜索树的平均复杂度为O(log n)。

4、二叉搜索树的实现通过上述的了解,我们大致已经知道二叉搜索树的工作原理。

所以现在我们就来简单的实现二叉搜索树基本的增删查的功能,代码如下:[cpp]view plain copy1.//表示节点的结构体2.struct node{3.int val;4.node*lch,*rch;5.};6.//插入整数x7.node*insert(node*p,int x){8.if(p==NULL){9.node*newNode=new node;10.newNode->val=x;11.newNode->lch=newNode->rch=NULL;12.p=newNode;13.}else{14.if(x<p->val)p->lch=insert(p->lch,x);15.else p->rch=insert(p->rch,x);16.}17.return p;18.}19.//查找整数x20.bool find(node*p,int x){21.if(p==NULL)return false;22.else if(p->val==x)return true;23.else if(p->val>x)return find(p->lch,x);24.else return find(p->rch,x);25.}26.//删除整数x27.node*remove(node*p,int x){28.if(p==NULL)return NULL;29.else if(x<p->val)p->lch=remove(p->lch,x);30.else if(x>p->val)p->rch=remove(p->rch,x);31.//情况<1>32.else if(p->lch==NULL){33.node*q=p->rch;34.delete p;35.return q;36.}37.//情况<2>38.else if(p->lch->rch==NULL){39.node*q=p->lch;40.q->rch=p->rch;41.delete p;42.return q;43.}44.//情况<3>45.else{46.node*q;47.for(q=p->lch;q->rch->rch!=NULL;q=q->rch);48.node*r=q->rch;49.q->rch=r->lch;50.r->lch=p->lch;51.r->rch=p->rch;52.delete p;53.return r;54.}55.return p;56.}Heaps;实例--斐波那契堆斐波纳契堆(Fibonacci Heap)于1984年由Michael L.Fredman与Robert E.Tarjan 提出,1987年公开发表,名字来源于运行时分析所使用的斐波那契数。

斐波那契堆同二项堆(Binomial Heap)一样,也是一种可合并堆(Mergeable Heap)。

与二项堆一样,斐波那契堆是由一组最小堆有序树构成,但堆中的树并不一定是二项树。

与二项堆中树都是有序的不同,斐波那契堆中的树都是有根而无序的。

特点:不涉及删除元素的操作有O(1)的平摊时间。

Extract-Min和Delete的数目和其它相比较小时效率更佳。

关键思想在于尽量延迟对堆的维护稠密图每次Decrease-key只要O(1)的平摊时间,和二项堆的O(lgn)相比是巨大的改进。

斐波那契堆的结构较二项堆更松散。

因此对结构的维护可以到方便时再做。

斐波那契堆中的树是有根而无序的。

每个节点包含一个指向其父节点的指针p[x],以及一个指向其任一子女的指针child[x](指向子女双链表)。

每个孩子有left[x]和right[x]。

(意义:在O(1)的时间内去掉一个节点,或者在O(1)的时间内合并双链表。

)其它域:degree[x]存储子女个数。

mark[x]指示自从x上一次成为另一节点的子女以来,它是否失掉一个孩子。

一个给定的斐波那契堆可以通过一个指向其含有最小关键字树的指针来访问。

斐波那契堆的关键思想在于尽量延迟对堆的维护。

创建一个新的斐波那契堆:MAKE_Fib_Heap有O(1)的代价。

插入一个节点:分三步进行:1,为新的节点置p,child,left,right,mark等域。

时间消耗:O(1)。

2,将包含x的根表和根表H连接。

时间消耗:O(1)。

3,在O(1)的时间内调整指向该堆的指针min[x]时间消耗:O(1)。

以节点数表示势。

势的增加为1,实际代价为O(1)。

所以平摊代价为O(1)。

寻找最小节点:min[x]指向的节点即为最小节点。

因为势没有变化,所以这个操作的平摊代价为O(1)。

合并两个斐波那契堆:分为3步:1。

合并根表2。

设置新的min[h]3。

重置n[x]。

抽取最小节点:这是最复杂的工作。

被延迟的对根表的调整工作最终由这个操作进行。

1。

去掉最小值,将其每个孩子都加入根表。

2。

将相同度数树的合并。

调整根表的步骤1。

在根表中找出两个具有相同度数的根x和y,且key[x]《key[y]2。

将y与x连接。

将y从根表里去掉,成为x一个孩子,并增加degree[x]。

减小一个节点的权1。

若此减小不影响堆序,不作调整。

2。

若影响堆序,则从堆中删除该节点,将其加入根表。

并检查其父亲的mark位。

若为false,则停止,并将其置为true。

若为true,则删除其父亲,继续递归向上执行。

直到一个节点mark域为false或该节点为根节点为止。

删除一个节点:1。

将该节点权调整至最小2。

抽取最小值。

证明最大度数界:证明删除或extract-min的平摊时间为O(lgn)。

引理1:设x为斐波那契堆中任一节点,并假设degree[x]=k。

设y1,y2,。

yk表示按与x链接的次序排列的x的子女,从最早的到最迟的,则对i=2,3,...,k,有degree[y1]>=0且degree[yi]>=i-2证明:显然degree[y1]》=0对i>=2,注意到y1被链接到x上时,y1,y2,。

yi-1都是x的子女,故我们必有degree[x]>=i-1又仅当degree[x]=degree[yi]时,才将yi链接到x上。

故此时必有degree[yi>=i-1,在此之后,节点yi至多失去一个孩子,因为失去两个就被切断了。

所以degree[yi]>=i-2引理2:对所有的整数k>=0,Fk+2=1+sum(Fi)[0<=i<=k],F为斐波那契数。

用数学归纳法证明。

并可证明不等式Fk+2>=G^k,其中G为黄金分割率。

(1+5^0.5)/2=1.161803...引理3:设x为斐波那契堆中任一节点,并设k=degree[x],那么,size(x)>=Fk+2>=G^k。

推论:在一个包含n个节点的斐波那契堆中节点的最大度数为O(lgn)。

对于斐波那契堆上的各种可合并操作,关键思想是尽可能久地将工作推后。

例如,当向斐波那契堆中插入新结点或合并两个斐波那契堆时,并不去合并树,而是将这个工作留给EXTRACT-MIN操作Spatial data partitioning trees:实例--Burkhard-Keller树BK树或者称为Burkhard-Keller树,是一种基于树的数据结构,被设计于快速查找近似字符串匹配,比方说拼写检查器,或模糊查找,当搜索”aeek”时能返回”seek”和”peek”。

为何BK-Trees这么酷,因为除了穷举搜索,没有其他显而易见的解决方法,并且它能以简单和优雅的方法大幅度提升搜索速度。

在定义BK树之前,我们需要预先定义一些操作。

为了索引和搜索字典,我们需要一种比较字符串的方法。

编辑距离(Levenshtein Distance)是一种标准的方法,它用来表示经过插入、删除和替换操作从一个字符串转换到另外一个字符串的最小操作步数。

其它字符串函数也同样可接受(比如将调换作为原子操作),只要能满足以下一些条件。

除了字符串匹配、查找回文串、查找重复子串等经典问题以外,日常生活中我们还会遇到其它一些怪异的字符串问题。

比如,有时我们需要知道给定的两个字符串“有多像”,换句话说两个字符串的相似度是多少。

1965年,俄国科学家Vladimir Levenshtein给字符串相似度做出了一个明确的定义叫做Levenshtein距离,我们通常叫它“编辑距离”。

字符串A到B的编辑距离是指,只用插入、删除和替换三种操作,最少需要多少步可以把A变成B。

例如,从FAME到GATE需要两步(两次替换),从GAME到ACM则需要三步(删除G和E 再添加C)。

相关主题