算法作业4 2-3-4树

一、算法要求:

        

  1. 构建2-3-4树
  2. 从一个数据文件中依次读入关键字序列构建树,测试建树时间;
  3. 测试遍历产生顺序输出序列需要的时间;
  4. 测试构建完成树后删除指定关键字集合的时间。

 

二、算法思路:

  2-3-4树是一种阶为4的B树。它是一种自平衡的数据结构,可以在O(logN)的时间内查找、插入和删除,这里的n是树中元素的数目。

  2-3-4树是这样一种数据结构,满足如下性质:

1)  每个节点有1、2或3个key,分别称为2-node,3-node,4-node。

2)  每个节点的keys把区间进行了划分,以4-nde为例,key1、key2、key3分别夹在subtree1, subtree2和subtree2, subtree3和subtree3, subtree4之间。

 

要求1: 构建2-3-4

  从文件中读取数据,把每个数据通过2-3-4树的插入操作,插入树中,完成建树,插入操作间要求2。

 

要求2: 插入操作,插入新元素newkey

  2-3-4树的插入,从root向叶子节点按照search规律遍历。如果发现newkey已经在树中,则算法结束。

  如果newkey不在树上,这里分两种情况讨论。

  一,如果待插入的叶节点只有1个或者2个元素,则直接把newkey放进叶节点的空闲位置,算法结束。

 

图1 插入11图示

 

  二,待插入的叶节点已经有3个元素,根据2-3-4树的规则,每个节点的元素个数在1、2、3之间,故当newkey要加入到这个叶子时,就违反了2-3-4树的定义。这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含1个元素的子节点,同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的元素个数超过3,则要继续向上分裂,以此类推。若已经到了根节点,根节点的分裂,使得树加高一层。

  我们发现,如果按照上述思路,程序需要在插入时回溯。为了避免回溯,可以以另一种思路完成插入,即在从root开始往下搜索的过程中,一旦遇到已满的节点(即元素个数为3),里面对该节点进行分裂。这样的好处是,能保证在叶子节点需要分裂时,其父节点一定是非满的,从而不需要再向上回溯。

 

 

 

 

图2 插入12图示

 

要求3: 删除操作,删除元素key

  和插入操作类似,在删除2-3-4树节点时,为了避免回溯,当遇到需要合并的节点时就立即执行合并。2-3-4树的删除思路如下:

从root向叶子节点按照search规律遍历,分三种情况讨论:

  一,如果key在叶节点leafnode中,则直接从leafnode中删除key。算法结束。

 

 

图3 删除11图示

 

  情况二和三能保证当在叶子节点找到key时,肯定能从兄弟处借节点,或合并成功而不会引起父节点的元素个数少于3

 

  二,如果key在分支节点node中:

  A)如果node的左分支节点left至少包含2个元素,则找出key值的直接前驱(即找出left子树的最右的元素prev),替换key,并在left子树中递归删除prev。

 

 

图4 删除9图示

 

  B)如果node的右分支节点right至少包含2个关键字,则找出key值的直接后继(即找出right子树的最左的元素next),替换key,并在right子树中递归删除next。

 

图5 删除7图示

 

  C)最坏的情况是,如果left和right都只有1个关键字,则将key与right的那个元素合并到left中,使得left有3个元素,再从left子树中递归删除key。

 

 

图6 删除5图示

 

 

  三,如果key不在分支节点node中,则必然在node的某个分支节点subtree[i]中,如果subtree[i]节点只有1个元素,则:

  A)如果subtree[i]的有左兄弟,且左兄弟subtree[i - 1]拥有至少2个关键字,则将node中夹在subtree[i]和subtree[i - 1]中间的元素node->key[i - 1]降至subtree[i]中,将subtree[i - 1]的最大元素上升至node->key[i - 1]中。

 

  B)如果subtree[i]的没有左兄弟,则一定有右兄弟。若右兄弟subtree[i + 1]拥有至少2个关键字,则将node中夹在subtree[i]和subtree[i + 1]中间的元素node->key[i]降至subtree[i]中,将subtree[i + 1]的最大元素上升至node->key[i]中。

  

 

图7 删除4图示

 

  C)如果subtree[i - 1]与subtree[i + 1]都只拥有1个元素,则将subtree[i]与其中一个兄弟合并,将node的一个元素降至合并的节点中,成为中间元素。

 

图8 删除4图示

 

三、算法伪代码:

1. 插入操作伪代码:

 1 //插入元素到2-3-4树
 2 //root为树根节点,key为待插入的元素
 3 Insert(root, key)
 4 begin
 5 If root is empy
 6         new a root and insert key into it
 7         return
 8 if root->keynum == 3
 9         new a root
10         newroot->subtree[0] ← root
11         split children
12         insert to nonfull(newroot, key)
13         return
14 else
15         insert to nonfull(root, key)
16 end        

 

 1 //插入到未满元素
 2 //node为当前节点,key为待插入元素
 3 Insert to nonfull(node, key)
 4 begin
 5 if key is exist
 6       return
 7 if node is leaf
 8       insert key into node
 9 else
10       if node subtree->keynum == 3
11       split children
12       insert nonfull(node subtree, key)
13 end

 

2. 删除操作伪代码:

 1 //从2-3-4树中删除元素
 2 //root为树根节点,key为待删除元素
 3 delete(root, key)
 4 begin
 5 if root is 2-node
 6          if root->subtree[0] and root->subtree[1] are 2-node
 7                 merge children
 8                 delete root
 9                 delete_nonone(root, key)
10                 return root->subtree[0]
11          else
12                 delete_nonone(root, key)
13                 return root
14 else
15          delete_nonone(root, key)
16 end

 

 1 //删除未空元素
 2 //node为当前节点,key为待删除元素
 3 delete_nonone(node, key)
 4 begin
 5 if node is leaf
 6            remove key from node
 7 else
 8            if key is in internal node
 9                     if node have left subtree
10                              replace key by its predecessor
11                              delete_nonone(node’s left subtree, predecessor)
12            else if node have right subtree
13                              replace key by its successor
14                              delete_nonone(node’s right subtree, successor)
15            else
16                              merge children
17                              delete(node’s left subtree, key)
18            else
19                      if node[i]->keynum == 1
20                              if node’s left subtree->keynum == 1
21                                      shift to right children
22                              else if node’s right subtree->keynum == 1
23 shift to left children
24                              else
25                                       merge children
26                                       delete_nonone(node’s subtree, key)
27            else
28                              delete_nonone(node’s subtree, key)
29 end

                                    

3. 顺序遍历伪代码:

 1 OrdeTravel(root)
 2 Begin
 3 If root == NULL return
 4 If root->type == TwoNode
 5          OrdeTravel (rt->subtree[0])
 6          Print key[0]
 7          OrdeTravel (rt->subtree[1])
 8 else if root->type == ThreeNode
 9          OrdeTravel (rt->subtree[0])
10          print(key[0])
11          OrdeTravel (rt->subtree[1])
12          print(key[1])
13          OrdeTravel (rt->subtree[2])
14 else
15          OrdeTravel (rt->subtree[0])
16          print(key[0])
17          OrdeTravel (rt->subtree[1])
18          print(key[1])
19          OrdeTravel (rt->subtree[2])
20          print(key[2])
21          OrdeTravel (rt->subtree[3])
22 end

 

四、程序效率分析:

2-3-4树的自平衡树,所有叶子节点都在同一层上,所以插入、删除和查找的时间复杂度都是树的高度,即T(N) = O(lgN)。

 

以下是测试数据:

数据量

建树时间

删除时间

顺序遍历时间

10K

0.022s

0s

0.181s

100K

0.283s

0s

1.761s

1M

3.626s

0s

16.801s

10M

41.418s

0.015s

168.596s

服务器一亿

172.86s

21.28s(删除m个)

-

 

五、程序运行截图:

A) 简单测试数据:

 

B)建树测试:

 

 

 

C)简单删除测试:

 

posted @ 2015-11-11 23:37  斑鱼  阅读(357)  评论(0编辑  收藏  举报