定義
二叉查找樹,也稱二叉搜索樹、有序二叉樹(英語(yǔ):ordered binary tree),排序二叉樹(英語(yǔ):sorted binary tree),是指一棵空樹或者具有下列性質(zhì)的二叉樹:
- 若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;
- 若任意節(jié)點(diǎn)的右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值;
- 任意節(jié)點(diǎn)的左、右子樹也分別為二叉查找樹;
- 沒(méi)有鍵值相等的節(jié)點(diǎn)。
刪除一個(gè)有左、右子樹的節(jié)點(diǎn)
先弄出一個(gè)二叉樹的葉子
private static class BinaryNode<K,V> {
K key; // 鍵
V value; // 值
BinaryNode<K,V> left; // 左子樹
BinaryNode<K,V> right; // 右子樹
BinaryNode(K key,V value) {
this(key,value, null, null);
}
BinaryNode(K key,V value, BinaryNode<K,V> left, BinaryNode<K,V> right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
插入算法
向一個(gè)二元搜尋樹b中插入一個(gè)節(jié)點(diǎn)s的算法,過(guò)程為:
- 若b是空樹,則將s所指結(jié)點(diǎn)作為根節(jié)點(diǎn)插入,否則:
- 若s->data等于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則返回,否則:
- 若s->data小于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則把s所指節(jié)點(diǎn)插入到左子樹中,否則:
- 把s所指節(jié)點(diǎn)插入到右子樹中。(新插入節(jié)點(diǎn)總是葉子節(jié)點(diǎn))
private BinaryNode insert(K key,V value,BinaryNode<K,V> node){
// 如果根為空,則直接把傳進(jìn)來(lái)的建值作為樹的根
if(node == null){
return new BinaryNode(key,value);
}
// 比較樹根和將要成為葉子的值
int result = compare(key,node.key);
// 節(jié)點(diǎn)值比根小,將節(jié)點(diǎn)插入左子樹,否則插入右子樹
if(result<0){
node.left = insert(key,value,node.left);
}else if(result > 0){
node.right = insert(key,value,node.right);
}
return node;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
查找算法
在二元搜尋樹b中查找x的過(guò)程為:
- 若b是空樹,則搜索失敗,否則:
- 若x等于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則查找成功;否則:
- 若x小于b的根節(jié)點(diǎn)的數(shù)據(jù)域之值,則搜索左子樹;否則:
- 查找右子樹。
private BinaryNode<K, V> getEntry(K key) {
if(key == null){
throw new NullPointerException();
}
BinarySearchTree.BinaryNode<K,V> p = rootTree;
while (p != null){
// 比較樹根和和傳入的key
int cmp = compare(key,p.key);
// 節(jié)點(diǎn)值比根小,則查找左子樹,否則查找右子樹
if(cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
刪除算法
- 若*p結(jié)點(diǎn)為葉子結(jié)點(diǎn),即PL(左子樹)和PR(右子樹)均為空樹。由于刪去葉子結(jié)點(diǎn)不破壞整棵樹的結(jié)構(gòu),則只需修改其雙親結(jié)點(diǎn)的指針即可。
- 若p結(jié)點(diǎn)只有左子樹PL或右子樹PR,此時(shí)只要令PL或PR直接成為其雙親結(jié)點(diǎn)f的左子樹(當(dāng)p是左子樹)或右子樹(當(dāng)p是右子樹)即可,作此修改也不破壞二叉查找樹的特性。
- 若p結(jié)點(diǎn)的左子樹和右子樹均不空。在刪去p之后,為保持其它元素之間的相對(duì)位置不變,可按中序遍歷保持有序進(jìn)行調(diào)整,可以有兩種做法:其一是令p的左子樹為f的左/右(依p是f的左子樹還是右子樹而定)子樹,s為p左子樹的最右下的結(jié)點(diǎn),而p的右子樹為s的右子樹;其二是令p的直接前驅(qū)(in-order predecessor)或直接后繼(in-order successor)替代p,然后再?gòu)亩娌檎覙渲袆h去它的直接前驅(qū)(或直接后繼)。
private BinaryNode<K, V> deleteEntry(K key, BinaryNode<K, V> node) {
if (node == null) {
return node;
}
int result = compare(key, node.key);
if (result < 0) {
// 存在左子樹
node.left = deleteEntry(key, node.left);
} else if (result > 0) {
// 存在右子樹
node.right = deleteEntry(key, node.right);
} else if (node.left != null && node.right != null) {
/**
* 這邊刪除可以有兩種方式,一種是找到右子樹的最左節(jié)點(diǎn),還有一種是找到左子樹的最右節(jié)點(diǎn)
* 然后把要?jiǎng)h除的節(jié)點(diǎn)替換掉
* node.key = findMax(node.left).key;
* node.value = findMax(node.left).value;
* node.left = deleteEntry(node.key, node.left);
*/
// 找到右子樹的左邊最小節(jié)點(diǎn)把要?jiǎng)h除的節(jié)點(diǎn)替換掉
node.key = findMin(node.right).key;
node.value = findMin(node.right).value;
// 替換掉之后將節(jié)點(diǎn)刪除
node.right = deleteEntry(node.key, node.right);
} else
node = (node.left != null) ? node.left : node.right;
return node;
}
二叉查找樹的弊端
最壞情況下,當(dāng)先后插入的關(guān)鍵字有序時(shí),構(gòu)成的二叉查找樹蛻變?yōu)閱沃洌@個(gè)時(shí)候復(fù)雜度會(huì)退化成O(n)
完整代碼
/**
* Created by tianzeng on 2017/5/13.
* 二叉查找樹
* 1. 若任意節(jié)點(diǎn)的左子樹不空,則左子樹上所有結(jié)點(diǎn)的值均小于它的根結(jié)點(diǎn)的值;
* 2. 若任意節(jié)點(diǎn)的右子樹不空,則右子樹上所有結(jié)點(diǎn)的值均大于它的根結(jié)點(diǎn)的值;
* 3. 任意節(jié)點(diǎn)的左、右子樹也分別為二叉查找樹;
* 4. 沒(méi)有鍵值相等的節(jié)點(diǎn)
*/
public class BinarySearchTree<K, V> {
/**
* 節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)
*/
private static class BinaryNode<K, V> {
K key;
V value;
BinaryNode<K, V> left;
BinaryNode<K, V> right;
BinaryNode(K key, V value) {
this(key, value, null, null);
}
BinaryNode(K key, V value, BinaryNode<K, V> left, BinaryNode<K, V> right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
private BinaryNode rootTree; //
private final Comparator<? super K> comparator;
/**
* 構(gòu)造一個(gè)空的二叉查找樹
*/
public BinarySearchTree() {
this.comparator = null;
rootTree = null;
}
public BinarySearchTree(Comparator<? super K> comparator) {
this.comparator = comparator;
}
/**
* 清空二叉查找樹
*/
public void clear() {
rootTree = null;
}
/**
* 判斷二叉樹是否為空
*/
public boolean isEmpty() {
return rootTree == null;
}
final int compare(Object k1, Object k2) {
return comparator == null ? ((Comparable<? super K>) k1).compareTo((K) k2)
: comparator.compare((K) k1, (K) k2);
}
/**
* 插入元素
*/
public void put(K key, V value) {
rootTree = insert(key, value, rootTree);
}
/**
* 插入元素
*/
private BinaryNode insert(K key, V value, BinaryNode<K, V> node) {
// 如果根為空
if (node == null) {
return new BinaryNode(key, value);
}
int result = compare(key, node.key);
// 節(jié)點(diǎn)值比根小,左子樹
if (result < 0) {
node.left = insert(key, value, node.left);
} else if (result > 0) {
node.right = insert(key, value, node.right);
}
return node;
}
/**
* 查找元素
*/
public V get(K key) {
BinarySearchTree.BinaryNode<K, V> p = getEntry(key);
return (p == null ? null : p.value);
}
private BinaryNode<K, V> getEntry(K key) {
if (key == null) {
throw new NullPointerException();
}
BinarySearchTree.BinaryNode<K, V> p = rootTree;
while (p != null) {
int cmp = compare(key, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}
public void remove(K key) {
rootTree = deleteEntry(key, rootTree);
}
/**
* 尋找該節(jié)點(diǎn)的最小節(jié)點(diǎn)
*/
BinaryNode<K,V> findMin(BinaryNode<K, V> node){
if(node == null){
return null;
}else if(node.left == null){
return node;
}
return findMin(node.left);
}
BinaryNode<K,V> findMax(BinaryNode<K, V> node){
if(node == null){
return null;
}else if(node.right == null){
return node;
}
return findMax(node.right);
}
private BinaryNode<K, V> deleteEntry(K key, BinaryNode<K, V> node) {
if (node == null) {
return node;
}
int result = compare(key, node.key);
if (result < 0) {
// 存在左子樹
node.left = deleteEntry(key, node.left);
} else if (result > 0) {
// 存在右子樹
node.right = deleteEntry(key, node.right);
} else if (node.left != null && node.right != null) {
/**
* node.key = findMax(node.left).key;
* node.value = findMax(node.left).value;
* node.left = deleteEntry(node.key, node.left);
*/
// 找到右子樹的左邊最小節(jié)點(diǎn)把要?jiǎng)h除的節(jié)點(diǎn)替換掉
node.key = findMin(node.right).key;
node.value = findMin(node.right).value;
// 替換掉之后將節(jié)點(diǎn)刪除
node.right = deleteEntry(node.key, node.right);
} else
node = (node.left != null) ? node.left : node.right;
return node;
}
public void print(){
print(rootTree);
}
public void print(BinaryNode root) {
if (root == null) {
return;
}
List<BinaryNode> list = new LinkedList<>();
BinaryNode node;
// 當(dāng)前層的結(jié)點(diǎn)個(gè)數(shù)
int current = 1;
// 記錄下一層的結(jié)點(diǎn)個(gè)數(shù)
int next = 0;
list.add(root);
while (list.size() > 0) {
node = list.remove(0);
current--;
System.out.printf("%-3s", node.value);
if (node.left != null) {
list.add(node.left);
next++;
}
if (node.right != null) {
list.add(node.right);
next++;
}
if (current ==0) {
System.out.println();
current = next;
next = 0;
}
}
}
}
測(cè)試:
參考資料
維基百科——二叉搜索樹
二叉樹(BST樹)內(nèi)結(jié)點(diǎn)的刪除
6天通吃樹結(jié)構(gòu)—— 第一天 二叉查找樹