前面介紹了基本的排序算法,排序通常是查找的前奏操作。這篇介紹基本的查找算法。
目錄:
- 1、符號(hào)表
- 2、順序查找
- 3、二分查找
- 4、插值查找
- 5、二叉查找樹(shù)
- 6、平衡查找樹(shù)
- 6.1、平衡二叉樹(shù)(AVL樹(shù))
- 6.2、2-3查找樹(shù)
- 6.3、紅黑樹(shù)
- 7、性能比較
1、符號(hào)表
符號(hào)表(Symbol Table)是一種存儲(chǔ)鍵值對(duì)的數(shù)據(jù)結(jié)構(gòu),它可以將鍵和值關(guān)聯(lián)起來(lái)。支持兩種操作:插入,將一組新的鍵值對(duì)插入到表中;查找,根據(jù)給定的鍵得到響應(yīng)的值。
符號(hào)表,有時(shí)又稱(chēng)索引,是為了加快查找速度而設(shè)計(jì)。它將關(guān)鍵字Key和記錄Value相關(guān)聯(lián),通過(guò)關(guān)鍵字Key來(lái)查找記錄Value。在現(xiàn)實(shí)生活中,我們經(jīng)常會(huì)遇到各種需要根據(jù)key來(lái)查找value的情況,比如DNS根據(jù)域名查找IP地址,圖書(shū)館根據(jù)索引號(hào)查找圖書(shū)等等:
符號(hào)表的特征:
- 表中不能有重復(fù)的鍵
- 鍵和值不能為空
符號(hào)表的抽象數(shù)據(jù)類(lèi)型:
public interface ST<K, V> {
//將鍵值對(duì)存入表中
void put(K key, V value);
//獲取key對(duì)應(yīng)的值
V get(K key);
}
2、順序查找
順序查找(Sequential Search)又稱(chēng)線性查找,是最基本的查找技術(shù)。從表中第一個(gè)記錄開(kāi)始,逐個(gè)進(jìn)行查找,若記錄的關(guān)鍵字和給定值相等,則查找成功。若直到最后,沒(méi)有關(guān)鍵字和給定值相等,則查找失敗。
代碼:
public class SequentialST<K, V> implements ST<K, V>{
private Node head;
private class Node {
K key;
V value;
Node next;
public Node(K key, V value, Node next) {
super();
this.key = key;
this.value = value;
this.next = next;
}
}
@Override
public void put(K key, V value) {
Node temp = sequentialSearch(key);
if(temp != null) {
temp.value = value;
}else {
head = new Node(key, value, head);
}
}
//順序查找,【關(guān)鍵】
private Node sequentialSearch(K key) {
for(Node cur= head; cur != null; cur=cur.next) {
if(key.equals(cur.key)) {
return cur;
}
}
return null;
}
@Override
public V get(K key) {
Node temp = sequentialSearch(key);
if(temp != null) {
return temp.value;
}
return null;
}
public static void main(String[] args) {
SequentialST<String, Integer> st = new SequentialST<>();
st.put("AA", 2);
st.put("BB", 2);
System.out.println(st.get("BB"));
}
}
很顯然順序查找的時(shí)間復(fù)雜度為O(N),效率非常低。
3、二分查找
二分查找(Binary Search),又稱(chēng)折半查找。二分查找的前提是符號(hào)表中的記錄必須有序。在符號(hào)表中取中間記錄作為比較對(duì)象,若中間值和給定值相等,則查找成功;若給定值小于中間值,則在左半?yún)^(qū)繼續(xù)查找,否則在右半?yún)^(qū)進(jìn)行查找;不斷重復(fù)直到成功或失敗。
代碼:
/**
*基于二分查找的符號(hào)表
*/
public class BinarySearchST<K extends Comparable<K>, V>
implements ST<K, V> {
private K[] keys;
private V[] values;
private int size;
public BinarySearchST(int capacity) {
keys = (K[]) new Comparable[capacity];
values = (V[]) new Object[capacity];
}
@Override
public void put(K key, V value) {
int i = binarySearch(key, 0, size-1);
//查找到給定的鍵,則更新對(duì)應(yīng)的值, size=0時(shí),i=0
if(i < size && keys[i].compareTo(key) == 0) {
values[i] = value;
return;
}
for(int j=size; j>i; j--) {
keys[j] = keys[j-1];
values[j] = values[j-1];
}
keys[i] = key;
values[i] = value;
size++;
}
@Override
public V get(K key) {
int i = binarySearch(key, 0, size-1);
if(keys[i].compareTo(key) == 0) {
return values[i];
}
return null;
}
//二分查找,【關(guān)鍵】
private int binarySearch(K key, int down, int up) {
while(down <= up) {
int mid = down + (up-down)/2;
int temp = keys[mid].compareTo(key);
if(temp > 0) {
up = mid-1;
}else if(temp < 0) {
down = mid + 1;
} else {
return mid;
}
}
return down;
}
public static void main(String[] args) {
BinarySearchST<String, Integer> st = new BinarySearchST<>(10);
st.put("AA", 2);
st.put("BB", 2);
System.out.println(st.get("BB"));
}
}
二分查找的時(shí)間復(fù)雜度為O(logN)
4、插值查找
插值查找(Interpolation Search)是根據(jù)要查找的關(guān)鍵字key與查找表中最大最小記錄的關(guān)鍵字比較后的查找方法。其前提條件是符號(hào)表有序。
插值查找的關(guān)鍵是將二分查找中
代碼:
private int binarySearch(K key, int down, int up) {
while(down <= up) {
int mid = down + (key-keys[down])/(keys[up]-keys[down])*(up-down);
int temp = keys[mid].compareTo(key);
if(temp > 0) {
up = mid-1;
}else if(temp < 0) {
down = mid + 1;
} else {
return mid;
}
}
return down;
}
對(duì)于表長(zhǎng)較大,且關(guān)鍵字分布比較均勻的符號(hào)表,插值查找的性能比二分查找要好的多。
5、二叉查找樹(shù)
二叉查找樹(shù)(Binary Search Tree),又稱(chēng)二叉排序樹(shù)。它是一棵二叉樹(shù),其中每個(gè)結(jié)點(diǎn)的鍵都大于其左子樹(shù)中任意結(jié)點(diǎn)的鍵而小于其右子樹(shù)中任意結(jié)點(diǎn)的鍵。
代碼:
//二叉查找樹(shù)
public class BST <K extends Comparable<K>, V>
implements ST<K, V> {
private Node root; //二叉樹(shù)的根結(jié)點(diǎn)
private class Node {
K key; //鍵
V value; //值
Node left, right; //左右子樹(shù)
int N; //以該結(jié)點(diǎn)為根的結(jié)點(diǎn)總數(shù)
public Node(K key, V value, int n) {
this.key = key;
this.value = value;
N = n;
}
}
@Override
public void put(K key, V value) {
root = put(root, key, value);
}
//插入操作
private Node put(Node node, K key, V value) {
if(node == null)
return new Node(key,value,1);
int cmp = key.compareTo(node.key);
if(cmp < 0) {
node.left = put(node.left, key, value);
}else if(cmp > 0) {
node.right = put(node.right, key, value);
} else {
node.value = value;
}
node.N = node.left.N + node.right.N + 1; //遞歸返回時(shí)更新N
return node;
}
@Override
public V get(K key) {
return get(root, key);
}
//查找操作
private V get(Node node, K key) {
if(node == null)
return null;
int cmp = key.compareTo(node.key);
if(cmp < 0) {
return get(node.left, key);
}else if(cmp > 0) {
return get(node.right, key);
} else {
return node.value;
}
}
}
插入過(guò)程:
在插入操作中,若樹(shù)為空,就返回一個(gè)含有該鍵值對(duì)的新結(jié)點(diǎn),若查找的鍵小于根結(jié)點(diǎn),則在左子樹(shù)中插入該鍵,否則在右子樹(shù)中插入該鍵。這樣通過(guò)遞歸的方法就能構(gòu)造出一個(gè)二叉查找樹(shù)。
查找過(guò)程:
對(duì)于查找操作可以使用非遞歸的方法來(lái)提高性能。其代碼為:
@Override
public V get(K key) {
Node node = root;
while(node != null) {
int cmp = key.compareTo(node.key);
if(cmp == 0) {
return node.value;
}else if(cmp > 0) {
node = node.right;
}else {
node = node.left;
}
}
return null;
}
刪除操作
若要?jiǎng)h除的結(jié)點(diǎn)是二叉樹(shù)中的葉子結(jié)點(diǎn),刪除它們對(duì)整棵樹(shù)無(wú)影響,直接刪除即可。若刪除的結(jié)點(diǎn)是只有左子樹(shù)或右子樹(shù),將它的左子樹(shù)或右子樹(shù)整個(gè)移動(dòng)到刪除結(jié)點(diǎn)的位置即可。如刪除二叉樹(shù)中最小結(jié)點(diǎn)的過(guò)程:
若要?jiǎng)h除的的結(jié)點(diǎn) 既有左子樹(shù)又有右子樹(shù),只需找到要?jiǎng)h除結(jié)點(diǎn)的直接前驅(qū)或直接后繼(即左上樹(shù)的最大結(jié)點(diǎn)或右子樹(shù)的最小結(jié)點(diǎn))S,用S來(lái)替換它 ,然后刪除結(jié)點(diǎn)S即可。如刪除帶左右子樹(shù)的結(jié)點(diǎn)2的過(guò)程:
代碼如下:
//刪除鍵key及其對(duì)應(yīng)的值
public void delete(K key) {
root = delete(root, key);
}
private Node delete(Node node, K key) {
if(node == null)
return null;
int cmp = key.compareTo(node.key);
if(cmp < 0) {
node.left = delete(node.left, key);
}else if(cmp > 0) {
node.right = delete(node.right, key);
} else {
if(node.left == null) {
return node.right;
}
if(node.right == null) {
return node.left;
}
Node temp = node;
node = min(temp.right);
node.right = deleteMin(temp.right);
node.left = temp.left;
}
node.N = node.left.N + node.right.N + 1; //從棧返回時(shí)更新N
return node;
}
//刪除一個(gè)子樹(shù)的最小結(jié)點(diǎn)
private Node deleteMin(Node node) {
if(node.left == null) { //刪除結(jié)點(diǎn)node
return node.right;
}
node.left = deleteMin(node.left);
node.N = node.left.N + node.right.N + 1; //更新子樹(shù)的計(jì)數(shù)N
return node;
}
//查找一個(gè)子樹(shù)的最小結(jié)點(diǎn)
private Node min(Node node) {
if(node.left == null) {
return node;
}
return min(node.left);
}
二叉查找樹(shù)的性能:
二叉查找樹(shù)的性能取決于樹(shù)的形狀,而樹(shù)的形狀取決于鍵被插入的順序。最好情況下,二叉樹(shù)是完全平衡的,此時(shí)查找和插入的時(shí)間復(fù)雜度都為O(logN)。最壞情況下,二叉樹(shù)呈線型,此時(shí)查找和插入的時(shí)間復(fù)雜度都為O(N)。平均情況下,時(shí)間復(fù)雜度為O(logN)。
6、平衡查找樹(shù)
雖然二叉查找樹(shù)能夠很好的用于許多應(yīng)用中,但它在最壞情況下的性能很糟糕。而平衡查找樹(shù)的所有操作都能夠在對(duì)數(shù)時(shí)間完成。
1、平衡二叉樹(shù)(AVL樹(shù))
平衡二叉樹(shù)(Self-Balancing Binary Search Tree),是一種二叉排序樹(shù),其中每一個(gè)節(jié)點(diǎn)的左子樹(shù)和右子樹(shù)的高度差至多等于1。將二叉樹(shù)上結(jié)點(diǎn)的左子樹(shù)深度減去右子樹(shù)深度的值稱(chēng)為平衡因子BF(Balance Factor)。平衡二叉樹(shù)的BF值只能為-1,0,1。
第一幅圖是平衡二叉樹(shù)。在第二幅圖中結(jié)點(diǎn)2的左結(jié)點(diǎn)比結(jié)點(diǎn)2大,所以它不是二叉排序樹(shù),而平衡二叉樹(shù)的前提是一個(gè)二叉排序樹(shù)。在第三幅圖中結(jié)點(diǎn)5的左子樹(shù)的高度為2,右子樹(shù)高度為0,BF值為2,不符合平衡二叉樹(shù)的定義。
平衡二叉樹(shù)的構(gòu)建思想:在構(gòu)建二叉查找樹(shù)的過(guò)程中,每當(dāng)插入一個(gè)結(jié)點(diǎn)時(shí),先檢查是否因插入而破壞了樹(shù)的平衡性,若是,則找出最小不平衡子樹(shù),調(diào)整最小不平衡子樹(shù)中各結(jié)點(diǎn)之間的鏈接關(guān)系,進(jìn)行相應(yīng)的旋轉(zhuǎn),使之成為新的平衡子樹(shù)。
對(duì)不平衡子樹(shù)的操作有左旋和右旋。
左旋:
代碼:
private Node rotateLeft(Node h) {
Node x = h.right;
h.right = x.left;
x.left = h;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
右旋:
代碼:
private Node rotateRight(Node h) {
Node x = h.left;
h.left = x.right;
x.right = h;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
向一棵AVL樹(shù)中插入一個(gè)結(jié)點(diǎn)的四種情況:
- 第一種:當(dāng)一個(gè)結(jié)點(diǎn)的BF值大于等于2,并且它的子結(jié)點(diǎn)的BF值為正,則右旋
- 第二種:當(dāng)一個(gè)結(jié)點(diǎn)的BF值大于等于2,但它的子結(jié)點(diǎn)的BF值為負(fù),對(duì)子結(jié)點(diǎn)進(jìn)行左旋操作,變?yōu)榈谝环N情況,然后再進(jìn)行處理。
- 第三種:當(dāng)一個(gè)結(jié)點(diǎn)的BF值小于等于-2,并且它的子結(jié)點(diǎn)的BF值為負(fù),則左旋
- 第四種:當(dāng)一個(gè)結(jié)點(diǎn)的BF值小于等于-2,但它的子結(jié)點(diǎn)的BF值為正,對(duì)子結(jié)點(diǎn)進(jìn)行右旋操作,變?yōu)榈谌N情況,然后再進(jìn)行處理。
AVL的實(shí)現(xiàn):
//平衡二叉樹(shù)(AVL樹(shù))的實(shí)現(xiàn)
public class AVL<K extends Comparable<K>, V> implements ST<K, V> {
private Node root; // 二叉樹(shù)的根結(jié)點(diǎn)
private class Node {
K key; // 鍵
V value; // 值
Node left, right; // 左右子樹(shù)
int N; // 以該結(jié)點(diǎn)為根的結(jié)點(diǎn)總數(shù)
public Node(K key, V value, int n) {
this.key = key;
this.value = value;
N = n;
}
}
// 左旋
private Node rotateLeft(Node h) {
Node x = h.right;
h.right = x.left;
x.left = h;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
// 右旋
private Node rotateRight(Node h) {
Node x = h.left;
h.left = x.right;
x.right = h;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
@Override
public void put(K key, V value) {
root = put(root, key, value);
}
// 插入操作
private Node put(Node node, K key, V value) {
if (node == null)
return new Node(key, value, 1);
int cmp = key.compareTo(node.key);
if (cmp < 0) {
node.left = put(node.left, key, value);
} else if (cmp > 0) {
node.right = put(node.right, key, value);
} else {
node.value = value;
}
if (BF(node) <= -2) {
if (BF(node.right) > 0) {
rotateRight(node.right);
}
rotateLeft(node);
}
if (BF(node) >= 2) {
if (BF(node.left) < 0) {
rotateLeft(node);
}
rotateRight(node);
}
node.N = node.left.N + node.right.N + 1; // 從棧返回時(shí)更新N
return node;
}
// 平衡因子BF的值
private int BF(Node node) {
return depth(node.left) - depth(node.right);
}
// 求子樹(shù)的深度
private int depth(Node node) {
if (node == null)
return 0;
return Math.max(depth(node.right), depth(node.left)) + 1;
}
// 查找操作,和二叉查找樹(shù)相同
@Override
public V get(K key) {
Node node = root;
while (node != null) {
int cmp = key.compareTo(node.key);
if (cmp == 0) {
return node.value;
} else if (cmp > 0) {
node = node.right;
} else {
node = node.left;
}
}
return null;
}
}
2、2-3查找樹(shù)
一棵2-3查找樹(shù),或?yàn)橐豢每諛?shù),或由以下結(jié)點(diǎn)組成:
- 2-結(jié)點(diǎn):含有一個(gè)鍵和兩個(gè)鏈接,左鏈接指向鍵小于該結(jié)點(diǎn)的子樹(shù),右鏈接指向鍵大于該結(jié)點(diǎn)的子樹(shù)。
- 3-結(jié)點(diǎn):含有兩個(gè)鍵和三個(gè)鏈接,左鏈接指向鍵都小于該結(jié)點(diǎn)的子樹(shù),中鏈接指向鍵位于該結(jié)點(diǎn)兩個(gè)鍵之間的子樹(shù),右鏈接指向鍵大于該結(jié)點(diǎn)的子樹(shù)。
查找:
2-3樹(shù)的查找操作和平衡二叉樹(shù)一樣,從根結(jié)點(diǎn)開(kāi)始,根據(jù)比較結(jié)果,到相應(yīng)的子樹(shù)中去繼續(xù)查找,直到命中或查找失敗。
插入:
向一棵2-3樹(shù)中插入一個(gè)結(jié)點(diǎn)可能的情況
- 第一種:向2-結(jié)點(diǎn)中插入新鍵,只需用一個(gè)3-結(jié)點(diǎn)替換2-結(jié)點(diǎn)即可。
- 第二種:向3-結(jié)點(diǎn)中插入新鍵,這個(gè)3-結(jié)點(diǎn)沒(méi)有父結(jié)點(diǎn),此時(shí)可將其分解為一個(gè)含有3個(gè)結(jié)點(diǎn)的二叉查找樹(shù)。
- 第三種:向一個(gè)父結(jié)點(diǎn)為2-結(jié)點(diǎn)的3-結(jié)點(diǎn)中插入新鍵,先將其分解為一個(gè)含有3個(gè)結(jié)點(diǎn)的二叉查找樹(shù),然后將中鍵移到父結(jié)點(diǎn),父結(jié)點(diǎn)由2-結(jié)點(diǎn)變?yōu)?-結(jié)點(diǎn)。
- 第四種:向一個(gè)父結(jié)點(diǎn)為3-結(jié)點(diǎn)的3-結(jié)點(diǎn)中插入新鍵,和第三種情況一樣,一直向上分解臨時(shí)的4-結(jié)點(diǎn),直到遇到一個(gè)2-結(jié)點(diǎn)將它替換為3-結(jié)點(diǎn),或到達(dá)3-結(jié)點(diǎn)的根,然后直接分解成一個(gè)含有3個(gè)結(jié)點(diǎn)的二叉查找樹(shù)。如圖,此時(shí)2-3樹(shù)依然平衡。
性能:
2-3查找樹(shù)的插入和查找操作的時(shí)間復(fù)雜度不超過(guò) O(logN)。
2-3查找樹(shù)需要維護(hù)兩種不同的結(jié)點(diǎn),實(shí)現(xiàn)起來(lái)比較復(fù)雜,并且在結(jié)點(diǎn)的轉(zhuǎn)換過(guò)程中需要大量的復(fù)制操作,這些都將產(chǎn)生額外的開(kāi)銷(xiāo),使的算法的性能可能比二叉查找樹(shù)要慢。
3、紅黑樹(shù)
紅黑樹(shù)是對(duì)2-3查找樹(shù)的一種改進(jìn),它使用標(biāo)準(zhǔn)的二叉查找樹(shù)和一些額外的信息來(lái)表示2-3樹(shù)。使用兩個(gè)2-結(jié)點(diǎn)和『紅鏈接』來(lái)表示一個(gè)3-結(jié)點(diǎn)。由于每個(gè)結(jié)點(diǎn)都只有一個(gè)指向自己的鏈接,所以可以在結(jié)點(diǎn)中使用boolean值來(lái)表示紅鏈接。
紅黑樹(shù),是滿(mǎn)足下列條件的二叉樹(shù):
- 紅鏈接均為左鏈接,即紅色結(jié)點(diǎn)必須為左結(jié)點(diǎn)。
- 沒(méi)有任何結(jié)點(diǎn)同時(shí)和兩個(gè)紅鏈接相連,即不存在左右子結(jié)點(diǎn)都為紅的結(jié)點(diǎn)。
- 任何空鏈接到根節(jié)點(diǎn)的路徑上的黑鏈接(黑結(jié)點(diǎn))數(shù)量相同。
- 根結(jié)點(diǎn)總是黑色的。
2-3查找樹(shù)與紅黑樹(shù)的對(duì)應(yīng)關(guān)系
紅黑樹(shù)的左旋和右旋操作,與平衡二叉樹(shù)(AVL樹(shù))的基本相同,只需注意旋轉(zhuǎn)后的結(jié)點(diǎn)顏色的變化即可。其代碼等下會(huì)給出。
向一個(gè)2-結(jié)點(diǎn)中插入結(jié)點(diǎn)的情況:
向一個(gè)2-結(jié)點(diǎn)中插入結(jié)點(diǎn)比較簡(jiǎn)單,和平衡二叉樹(shù)基本相同,這里需要注意,第二種情況,由于紅黑樹(shù)的紅色結(jié)點(diǎn)必須為左結(jié)點(diǎn),所以這里需要一個(gè)左旋操作,將右結(jié)點(diǎn)變?yōu)樽蠼Y(jié)點(diǎn)。
向一個(gè)3-結(jié)點(diǎn)中插入結(jié)點(diǎn)的情況:
向一個(gè)3-結(jié)點(diǎn)中插入結(jié)點(diǎn),由紅黑樹(shù)的定義,結(jié)點(diǎn)不能和兩個(gè)紅色結(jié)點(diǎn)相連并且紅色結(jié)點(diǎn)必須為左結(jié)點(diǎn),所以需要做相應(yīng)的旋轉(zhuǎn)操作。這里需要注意第一種情況中,對(duì)左右結(jié)點(diǎn)均為紅色時(shí)的顏色轉(zhuǎn)換,處理后紅色向上傳遞,這可能會(huì)使上層結(jié)點(diǎn)的左右結(jié)點(diǎn)均為紅色,這時(shí)需要對(duì)上層繼續(xù)進(jìn)行顏色轉(zhuǎn)換,直到根結(jié)點(diǎn)或不出現(xiàn)左右結(jié)點(diǎn)均為紅的情況。
代碼實(shí)現(xiàn):
//紅黑樹(shù)的實(shí)現(xiàn)
public class RedBlackBST <K extends Comparable<K>, V> implements ST<K, V> {
private static final boolean RED = true;
private static final boolean BLACK = false;
private Node root; // 二叉樹(shù)的根結(jié)點(diǎn)
private class Node {
K key; // 鍵
V value; // 值
Node left, right; // 左右子樹(shù)
int N; // 以該結(jié)點(diǎn)為根的結(jié)點(diǎn)總數(shù)
//由于每個(gè)結(jié)點(diǎn)都只有一個(gè)指向自己的鏈接,所以可以在結(jié)點(diǎn)中使用boolean值來(lái)表示紅鏈接。
boolean color;
public Node(K key, V value, int n, boolean color) {
this.key = key;
this.value = value;
N = n;
this.color = color;
}
}
private boolean isRed(Node node) {
if(node == null)
return false;
return node.color == RED;
}
// 左旋
private Node rotateLeft(Node h) {
Node x = h.right;
h.right = x.left;
x.left = h;
x.color = h.color;
h.color = RED;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
// 右旋
private Node rotateRight(Node h) {
Node x = h.left;
h.left = x.right;
x.right = h;
x.color = h.color;
h.color = RED;
x.N = h.N;
h.N = h.left.N + h.right.N + 1;
return x;
}
@Override
public void put(K key, V value) {
root = put(root, key, value);
root.color = BLACK; //根節(jié)點(diǎn)總是黑色的,因?yàn)樗鼪](méi)有父鏈接
}
// 插入操作
private Node put(Node node, K key, V value) {
if (node == null)
return new Node(key, value, 1,RED);
int cmp = key.compareTo(node.key);
if (cmp < 0) {
node.left = put(node.left, key, value);
} else if (cmp > 0) {
node.right = put(node.right, key, value);
} else {
node.value = value;
}
if(isRed(node.right) && !isRed(node.left)) {
node = rotateLeft(node);
}
if(isRed(node.left) && isRed(node.left.left)) {
node = rotateRight(node);
}
if(isRed(node.left) && isRed(node.right)) {
flipColors(node);
}
node.N = node.left.N + node.right.N + 1; // 從棧返回時(shí)更新N
return node;
}
//顏色轉(zhuǎn)換
private void flipColors(Node node) {
node.color = RED;
node.left.color = BLACK;
node.right.color = BLACK;
}
// 查找操作,和二叉查找樹(shù)一樣
@Override
public V get(K key) {
Node node = root;
while (node != null) {
int cmp = key.compareTo(node.key);
if (cmp == 0) {
return node.value;
} else if (cmp > 0) {
node = node.right;
} else {
node = node.left;
}
}
return null;
}
}
7、性能比較
性能如下圖:
關(guān)于紅黑樹(shù)的刪除,今天看了好久,也沒(méi)完全弄明白,這里就不寫(xiě)出來(lái)誤人子弟了,感興趣的可以看下這篇博客。