java——紅黑樹 RBTree
阿新 • • 發佈:2018-11-06
對於完全隨機的資料,普通的二分搜尋樹就很好用,只是在極端情況下會退化成連結串列。
對於查詢較多的情況,avl樹很好用。
紅黑樹犧牲了平衡性,但是它的統計效能更優(綜合增刪改查所有的操作)。
紅黑樹java實現(不完整,沒有進行刪除節點的操作):
(預設左傾紅黑樹)
package RedBlackTree; //從任意一個節點到葉子節點,經過的黑色節點是一樣的——紅黑樹是保持“黑平衡”的二叉樹 //因為23樹中的每一個節點到葉子節點的深度是相同的 //紅黑樹在嚴格意義上不是平衡二叉樹,最大高度:2logn 時間複雜度是O(logn) //儲存的資料經常需要增加或者刪除時 使用紅黑樹要優於avl樹public class RBTree<K extends Comparable<K>, V> { private static final boolean RED = true; private static final boolean BLACK = false; private class Node { public K key; public V value; public Node left, right; //表示顏色 public booleancolor; public Node(K key, V value) { this.key = key; this.value = value; left = null; right = null; //add時新新增的節點總是要進行融合,所以節點預設為紅色 color = RED; } } private Node root; private int size;public RBTree() { root = null; size = 0; } public int getSize() { return size; } public boolean isEmpty() { return size == 0; } // 判斷節點node的顏色 private boolean isRed(Node node){ if(node == null) return BLACK; return node.color; } // node x // / \ 左旋轉 / \ // T1 x ---------> node T3 // / \ / \ // T2 T3 T1 T2 private Node leftRotate(Node node){ Node x = node.right; // 左旋轉 node.right = x.left; x.left = node; x.color = node.color; node.color = RED; return x; } // node x // / \ 右旋轉 / \ // x T2 -------> y node // / \ / \ // y T1 T1 T2 private Node rightRotate(Node node){ Node x = node.left; // 右旋轉 node.left = x.right; x.right = node; x.color = node.color; node.color = RED; return x; } // 顏色翻轉 private void flipColors(Node node){ node.color = RED; node.left.color = BLACK; node.right.color = BLACK; } // 向紅黑樹中新增新的元素(key, value) public void add(K key, V value){ root = add(root, key, value); root.color = BLACK; // 最終根節點為黑色節點 } // 向以node為根的紅黑樹中插入元素(key, value),遞迴演算法 // 返回插入新節點後紅黑樹的根 private Node add(Node node, K key, V value){ if(node == null){ size ++; return new Node(key, value); // 預設插入紅色節點 } if(key.compareTo(node.key) < 0) node.left = add(node.left, key, value); else if(key.compareTo(node.key) > 0) node.right = add(node.right, key, value); else // key.compareTo(node.key) == 0 node.value = value; if (isRed(node.right) && !isRed(node.left)) node = leftRotate(node); if (isRed(node.left) && isRed(node.left.left)) node = rightRotate(node); if (isRed(node.left) && isRed(node.right)) flipColors(node); return node; } // 返回以node為根節點的二分搜尋樹中,key所在的節點 private Node getNode(Node node, K key){ if(node == null) return null; if(key.equals(node.key)) return node; else if(key.compareTo(node.key) < 0) return getNode(node.left, key); else // if(key.compareTo(node.key) > 0) return getNode(node.right, key); } public boolean contains(K key){ return getNode(root, key) != null; } public V get(K key){ Node node = getNode(root, key); return node == null ? null : node.value; } public void set(K key, V newValue){ Node node = getNode(root, key); if(node == null) throw new IllegalArgumentException(key + " doesn't exist!"); node.value = newValue; } // 返回以node為根的二分搜尋樹的最小值所在的節點 private Node minimum(Node node){ if(node.left == null) return node; return minimum(node.left); } // 刪除掉以node為根的二分搜尋樹中的最小節點 // 返回刪除節點後新的二分搜尋樹的根 private Node removeMin(Node node){ if(node.left == null){ Node rightNode = node.right; node.right = null; size --; return rightNode; } node.left = removeMin(node.left); return node; } // 從二分搜尋樹中刪除鍵為key的節點 public V remove(K key){ Node node = getNode(root, key); if(node != null){ root = remove(root, key); return node.value; } return null; } private Node remove(Node node, K key){ if( node == null ) return null; if( key.compareTo(node.key) < 0 ){ node.left = remove(node.left , key); return node; } else if(key.compareTo(node.key) > 0 ){ node.right = remove(node.right, key); return node; } else{ // key.compareTo(node.key) == 0 // 待刪除節點左子樹為空的情況 if(node.left == null){ Node rightNode = node.right; node.right = null; size --; return rightNode; } // 待刪除節點右子樹為空的情況 if(node.right == null){ Node leftNode = node.left; node.left = null; size --; return leftNode; } // 待刪除節點左右子樹均不為空的情況 // 找到比待刪除節點大的最小節點, 即待刪除節點右子樹的最小節點 // 用這個節點頂替待刪除節點的位置 Node successor = minimum(node.right); successor.right = removeMin(node.right); successor.left = node.left; node.left = node.right = null; return successor; } } }