发散二叉搜索树

根插入方法中,我们通过旋转把插入节点带到树根位置。这里,我们考虑如何使得旋转操作使树达到平衡。我们不考虑递归地使用把新近插入节点带到树顶部的单一旋转操作,而考虑把节点从作为树根的孙子(根的两代,也就是与根相距两层)之一的一个位置带到树顶部的两个旋转操作。首先,我们执行一次旋转,使节点成为树根的一个孩子。然后,执行另一次旋转,使它成为树根。根据从根到正被插入的节点的两个链接是否以相同的方式定向,存在两种本质不同的情况。当从根到正被插入的节点的两个链接以相同方式定向(例如:节点到根需要经过两个左链接,或者节点到根需要经过两个右链接)时,我们可以直接在树根执行两次相同的旋转操作,将相应节点移动到树根位置。当从根到正被插入的节点的两个链接以不同方式定向(例如:节点到根需要经过一个左链接和一个右链接,或者节点到根需要经过一个右链接和一个左链接)时,我们可以使用标准根插入方法。通过以上方式建立的二叉搜索树就是发散二叉搜索树。发散插入和标准根插入之间的区别也许显得不太合理,但它非常重要:发散操作消除了最坏情况下的二次水平性能,而这正是标准二叉搜索树的主要缺陷。

发散插入和之前根插入算法的不同之处仅在于一个实质细节:如果搜索路径的方向是“左-左”或“右-右”,则该节点可用一次双重旋转从树的顶部而不是树的底部带到树根。

对于源自树根的搜索路径,本程序检查其中的两个步骤的四种可能性,并执行适当的旋转操作:

左-左:在树根右旋转两次;

左-右:在左孩子左旋转,然后在树根右旋转;

右-右:在树根左旋转两次;

右-左:在右孩子右旋转,然后在树根左旋转。

 1 private:
 2     void splay(link& h, Item x)
 3     { if (h == 0)
 4        { h == new node(x, 0, 0, 1); return;}
 5        if (x.key() < h->item.key())
 6        { link& hl = h->l; int N = h->N;
 7           if (hl == 0)
 8           { h == new node(x, 0, h, N+1); return;}
 9           if (x.key() < hl->item.key())
10           { splay(hl->l, x); rotR(h);}
11           else { splay(hl->r, x); rotL(hl);}
12           rotR(h);
13        }
14        else
15        { link &hr = h->r; int N = h->N;
16           if (hr == 0)
17           { h = new node(x, h, 0, N+1); return;}
18           if (hr->item.key() < x.key())
19           {splay(hr->r, x); rotL(h);}
20           else { splay(hr->l, x); rotR(hr);}
21           rotL(h);
22         }
23      }
24 public:       
25     void insert(Item item)
26     {splay(head, item);}

 

posted @ 2020-05-13 13:45  ~宁静致远~  阅读(156)  评论(0编辑  收藏  举报