首页 > 代码库 > 数据结构-伸展树
数据结构-伸展树
声明:本文是对某高中生的竞赛论文学习的文章
介绍:
二叉查找树能够支持多种动态集合操作。对于一个含有n个结点的完全二叉树,这些操作的最还情况运行时间是O(lgn),但如果树是含有n个结点的线性链,则这些操作的最坏情况运行时间为O(n)。而像红黑树、AVL树这种二叉查找树的变形在最坏情况下,仍能保持较好性能。
本文将要介绍的伸展树也是二叉查找树的变形,它对空间要求及编程难度的要求相对不高。
伸展树:
伸展树与二叉查找树一样,具有有序性。即伸展树的每一个结点x满足:该结点的左子树中的每个元素都小于x,而其右子树的每一个元素都大于x。与二叉查找树不同的是,伸展树可以自我调整,也就是伸展操作(Splay)。
伸展操作Splay(x,S)
伸展操作是在保持伸展树有序的前提下,通过一系列旋转将伸展树T中的元素x调整至树的根部。在调整的过程中,分以下三种情况处理:
1、结点x的父节点y是根节点。
这时,如果x是y的左孩子,我们进行一次Zig(右旋)操作;
如果x是y的右孩子,我们进行一次Zag(左旋)操作;
2、结点x的父节点y不是根节点,y的父节点为z。
且x与y同时是各自父节点的左孩子,这时我们进行Zig-Zig操作;
或者是同时是各自父节点的右孩子,这时我们进行一次Zag-Zag操作。
3、结点x的父节点y不是根节点,y的父节点为z。
且x与y分别是其父节点的左孩子和右孩子,这时我们进行一次Zig-Zag操作。
或者是x与y分别是其父节点的右孩子和左孩子,这时我们进行一次Zag-Zig操作。
如下图所示,执行Splay(1,T),将元素1调整的树根的位置,执行Splay(2)将元素2调整到树根的位置。从直观上看,树比原来“平衡”多了,而伸展操作的过 程并不复杂,只需要左旋和右旋。
基本操作:
利用Splay可以在伸展树上T上作如下操作,
查找:判断元素x是否在伸展树T表示的有序集中;
跟二叉查找树的查找操作一样,在伸展树中查找x,如果x在伸展树中,则再执行Splay(x,T)调整伸展树。
插入:将元素x插入到伸展树T表示的有序集中;
跟二叉查找树的插入操作一样,将元素x插入到伸展树的相应位置,在执行Splay(x,T)调整伸展树。
删除:将元素x从伸展树T表示的有序集中删除;
首先,查找的元素x所在的位置,如果x没有孩子或者只有一个孩子,直接删除掉x,再对x的父节点执行Splay,将x的父节点调整到根位置。否 则,查找x的后继y,用y代替x的位置,然后执行Splay(y,T),将y调整到根节点位子。
合并:将两个伸展树T1、T2合并成一个伸展树。其中S1中的所有元素都小于T2中的元素。
首先,找到T1中最大的元素x,在执行Splay(x,T1)将x调整到T1的根位置,然后将T2作为x的右子树就得到新的伸展树T。
划分:以元素x为边界,将伸展树T划分成两个伸展树T1和T2,其中,T1中的元素都小于x,T2中的元素都大于x。
首先,执行查找操作,将元素x调整为根位置,x的左子树为T1,右子树为T2。
除了上述操作外,伸展树还可以有其他的操作。
时间复杂度:
所有的操作其平摊复杂度为O(lgn)。
程序实现:
1 package datastructure; 4 import java.util.List; 5 import java.util.ArrayList; 6 10 public class SplayTree { 11 class TreeNode{ 12 private int key; 13 private String data; 14 private TreeNode left; 15 private TreeNode right; 16 private TreeNode parent; 17 18 public TreeNode(int key,String data,TreeNode left,TreeNode right,TreeNode parent) { 19 this.key = key; 20 this.data =http://www.mamicode.com/ data; 21 this.left = left; 22 this.right = right; 23 this.parent = parent; 24 } 25 public int getKey() { 26 return key; 27 } 28 public String toString(){ 29 String leftKey = (left == null ? " " : String.valueOf(left.key)); 30 String rightKey = (right == null ? " " : String.valueOf(right.key)); 31 return "(" + leftKey +"," + key + "," + rightKey + ")"; 32 } 33 } 34 private TreeNode root = null; 35 public boolean IsEmpty() 36 { 37 if(root == null) 38 { 39 return true; 40 } 41 else 42 { 43 return false; 44 } 45 } 46 public TreeNode Insert(int key) { 47 TreeNode parentNode = null; 48 TreeNode newNode = new TreeNode(key, "v", null, null, null); 49 TreeNode pNode = root; 50 if(pNode == null) 51 { 52 root = newNode; 53 return root ; 54 } 55 while(pNode != null) 56 { 57 parentNode = pNode; 58 if(key < pNode.key) 59 { 60 pNode = pNode.left; 61 } 62 else if(key > pNode.key) 63 { 64 pNode = pNode.right; 65 } 66 else 67 { 68 return pNode;//树中已存在与新结点的关键字相同的结点 69 } 70 } 71 newNode.parent = parentNode; 72 if(newNode.key < parentNode.key) 73 { 74 parentNode.left = newNode; 75 return parentNode.left; 76 } 77 else 78 { 79 parentNode.right = newNode; 80 return parentNode.right; 81 } 82 83 84 } 85 private List<TreeNode> nodeList = new ArrayList<TreeNode>(); 86 87 public List<TreeNode> getNodeList() { 88 return nodeList; 89 } 90 91 //获取中序列表 92 public List<TreeNode> Inorder_SplayTree_WalkList() 93 { 94 if(nodeList != null) 95 { 96 nodeList.clear(); 97 } 98 Inorder_SplayTree_Walk(root); 99 return nodeList;100 }101 //中序遍历树102 private void Inorder_SplayTree_Walk(TreeNode root)103 {104 if(root != null)105 {106 Inorder_SplayTree_Walk(root.left);107 nodeList.add(root);108 Inorder_SplayTree_Walk(root.right);109 }110 }111 //给定关键字key,查找到该结点112 private TreeNode Search(int key)113 {114 TreeNode pNode = root;115 while(pNode != null && pNode.key != key)116 {117 if(key<pNode.key)118 {119 pNode = pNode.left;120 }121 else {122 pNode = pNode.right;123 }124 }125 return pNode;126 }127 //左旋转:x的右孩子y不空,以x与y之间的链为之架128 private void leftRotate(TreeNode pNode)129 {130 TreeNode xNode = pNode;131 TreeNode yNode = xNode.right;132 xNode.right = yNode.left;133 if(yNode.left != null)134 {135 yNode.left.parent = xNode;136 }137 yNode.parent = xNode.parent;138 if(xNode.parent == null)139 {140 root = yNode;141 }142 else if(xNode.parent.left == xNode)143 {144 xNode.parent.left = yNode;145 }else {146 xNode.parent.right = yNode;147 }148 yNode.left = xNode;149 xNode.parent = yNode;150 }151 //右旋转:x的左孩子y不空,以x与y之间的链为支架右旋转152 private void rightRotate(TreeNode pNode)153 {154 TreeNode xNode = pNode;155 TreeNode yNode = xNode.left;156 xNode.left = yNode.right;157 if(yNode.right != null)158 {159 yNode.right.parent = xNode;160 }161 yNode.parent = xNode.parent;162 if(xNode.parent == null)163 {164 root = yNode;165 }else if(xNode.parent.left == xNode)166 {167 xNode.parent.left = yNode;168 }169 else {170 xNode.parent.right = yNode;171 }172 yNode.right = xNode;173 xNode.parent = yNode; 174 }175 176 public void Splay(int key)177 {178 TreeNode xNode = Search(key);179 if(xNode == null)180 {181 return;182 }183 SplayT(xNode,this.root);184 }185 186 //伸展树的Splay操作展操作是在保持伸展树有序的前提下,187 //通过一系列旋转将伸展树T中的元素x调整至树的根部。188 //在调整的过程中,分以下三种情况处理:189 private void SplayT(TreeNode xNode,TreeNode Troot)190 {191 192 if(Troot == null || xNode == Troot)193 {194 return ;195 }196 while(xNode.parent != null) //x是否到达根位置197 {198 // 1、结点x的父节点y是根节点。199 if(xNode.parent == Troot)200 {201 if (xNode.parent.left == xNode)202 {203 //如果x是y的左孩子,我们进行一次Zig(右旋)操作;204 rightRotate(xNode.parent);205 }206 else 207 {208 //如果x是y的右孩子,我们进行一次Zag(左旋)操作;209 leftRotate(xNode.parent);210 }211 }212 else213 {214 //2、结点x的父节点y不是根节点,y的父节点为z。215 TreeNode yNode = xNode.parent;216 if(yNode != null)217 {218 TreeNode zNode = yNode.parent;219 if(xNode == yNode.left && yNode ==zNode.left)220 {221 //x与y同时是其父节点的左孩子222 rightRotate(zNode);223 rightRotate(yNode);224 }225 else if(xNode == yNode.right && yNode == zNode.right)226 {227 //x与y同时是其父节点的右孩子228 leftRotate(zNode);229 leftRotate(yNode); 230 }else if (xNode==yNode.left && yNode == zNode.right) 231 {232 //x是其父节点的左孩子,y是父节点的右孩子233 rightRotate(xNode.parent);234 leftRotate(xNode.parent); 235 236 }else237 {238 //x是其父节点的右孩子,y是父节点的左孩子239 leftRotate(xNode.parent);240 rightRotate(xNode.parent);241 }242 }243 }244 245 } 246 }247 //伸展树的查找操作 248 public void SplayTree_Search(int x,TreeNode Troot)249 {250 TreeNode xNode = Search(x);251 if(xNode == null)252 {253 return; 254 }255 SplayT(xNode, Troot);256 }257 //伸展树的插入操作258 public void SplayTree_Insert(int x,TreeNode Troot)259 {260 TreeNode xNode = Insert(x);261 if(xNode == null)262 {263 return; 264 }265 SplayT(xNode, Troot);266 }267 //伸展树的删除操作268 public void SplayTree_Delete(int x,TreeNode Troot) throws Exception269 {270 TreeNode xNode = Search(x);271 if(xNode == null)272 {273 throw new Exception("树中不存在要删除的元素x");274 }275 SplayTree_Remove(xNode,Troot); 276 }277 private void SplayTree_Remove(TreeNode xNode,TreeNode Troot) throws Exception278 {279 280 if(xNode == null)281 {282 return;283 }284 TreeNode parentNode = xNode.parent;285 if(xNode.left == null && xNode.right == null)286 {287 if(parentNode.left == xNode)288 {289 parentNode.left = null;290 SplayT(parentNode, Troot);291 }292 if(parentNode.right == xNode)293 {294 parentNode.right = null;295 SplayT(parentNode,Troot);296 }297 }298 if(xNode.left != null && xNode.right == null)299 {300 if(parentNode.left == xNode)301 {302 xNode.left.parent = parentNode;303 parentNode.left = xNode.left;304 SplayT(parentNode, Troot);305 }306 if(parentNode.right == xNode)307 {308 xNode.left.parent = parentNode;309 parentNode.right = xNode.left;310 SplayT(parentNode,Troot);311 } 312 }313 if(xNode.left == null && xNode.right != null)314 {315 if(parentNode.left == xNode)316 {317 xNode.right.parent = parentNode;318 parentNode.left = xNode.right;319 SplayT(parentNode, Troot);320 }321 if(parentNode.right == xNode)322 {323 xNode.right.parent = parentNode;324 parentNode.right = xNode.right;325 SplayT(parentNode, Troot);326 }327 }328 //x的左右儿子都不为空,就删除后继,让后继的内容,代替当前内容 329 TreeNode successorNode = SplayTree_Successor(xNode);330 xNode.key = successorNode.key;331 xNode.data =http://www.mamicode.com/ successorNode.data;332 SplayTree_Remove(successorNode,Troot);333 SplayT(successorNode, Troot);334 }335 public TreeNode SplayTree_Successor(TreeNode xNode) throws Exception336 {337 if(xNode == null)338 {339 return null;340 }341 TreeNode pNode = xNode;342 //若右子树不空,则为右子树中最小的关键字343 if(pNode.right != null)344 {345 return Tree_Minimum(pNode.right);346 }347 TreeNode parentNode = xNode.parent;348 //若右子树为空,且x有一个后继y.则y是x的最低祖先结点,且y的左儿子也是x的祖先349 while(parentNode != null && pNode == parentNode.right)350 {351 pNode = parentNode;352 parentNode = pNode.parent;353 }354 return parentNode; 355 }356 public TreeNode Tree_Maxmum(TreeNode Troot) throws Exception357 {358 if(Troot == null)359 {360 throw new Exception("树为空");361 }362 TreeNode xNode = Troot;363 while(xNode.right != null)364 {365 xNode = xNode.right;366 }367 return xNode;368 }369 public TreeNode Tree_Minimum(TreeNode Troot) throws Exception370 {371 if(Troot == null)372 {373 throw new Exception("树为空");374 }375 TreeNode xNode = Troot;376 while(xNode.left != null)377 {378 xNode = xNode.left;379 }380 return xNode; 381 }382 public void SplayTree_Minimum(TreeNode Troot) throws Exception383 {384 TreeNode xNode = Tree_Minimum(Troot);385 if(xNode == null)386 {387 return;388 }389 SplayT(xNode,Troot);390 }391 public TreeNode SplayTree_Join(TreeNode Troot1,TreeNode Troot2) throws Exception392 {393 TreeNode xNode = Tree_Maxmum(Troot1);394 if(xNode == null)395 {396 return null;397 }398 SplayT(xNode, Troot1);399 xNode.right = Troot2;400 Troot2.parent = xNode;401 return xNode;402 403 }404 405 }