一、算法
二叉排序樹(Binary Sort Tree)又稱二叉查找樹(Binary Search Tree),亦稱二叉搜索樹。 它或者是一棵空樹;或者是具有下列性質的二叉樹: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別爲二叉排序樹;
不得不說二叉查找樹的插入和刪除非常麻煩,可謂是費了九牛二虎之力才把代碼寫完。支持Insert,Delete,Search,Min,Max,Successor,Predecessor等操作。BST在數據結構中佔有很重要的地位,一些高級樹結構都是其的變種,例如AVL樹、紅黑樹等,因此理解BST對於後續樹結構的學習有很好的作用。
二、java代碼
<span style="font-size:14px;">package com.tangbo;
public class BinarySearchTree<T extends Comparable<T>> {
Node<T> root;
public BinarySearchTree() {
super();
}
/*
* 插入一個元素T t
*/
public void insert(T t) {
Node<T> node = new Node<T>(t);
if(node.getKey()==null)
{
throw new IllegalArgumentException("插入的元素不能爲空!");
}
if(root == null)
{
root = node;
}else
{
Node<T> temp = root;
while(true)
{
if(node.getKey().compareTo(temp.getKey())>0)
{
if(temp.getRightNode()==null)
{
node.setParentNode(temp);
temp.setRightNode(node);
break;
}else
{
temp = temp.getRightNode();
}
}else
{
if(temp.getLeftNode()==null)
{
node.setParentNode(temp);
temp.setLeftNode(node);
break;
}else
{
temp = temp.getLeftNode();
}
}
}
}
}
//計算該節點的有幾個孩子節點
private int childCount(Node<T> node) {
if (node == null) {
throw new IllegalArgumentException("節點不能爲空");
}
int count = 0;
if (node.getLeftNode() != null) {
count++;
}
if (node.getRightNode() != null) {
count++;
}
return count;
}
//刪除一個節點
public void delete(Node<T> node) {
if (node == null) {
throw new IllegalArgumentException("刪除節點不能爲空!");
}
int childCount = childCount(node);
Node<T> parentNode = node.getParentNode();
if (childCount == 0) {
if (parentNode == null) {
root = null;
} else {
if (node == parentNode.getLeftNode()) {
parentNode.setLeftNode(null);
} else {
parentNode.setRightNode(null);
}
}
} else if (childCount == 1) {
if (parentNode == null) {
if (node.getLeftNode() != null) {
root = node.getLeftNode();
node.getLeftNode().setParentNode(null);
} else {
root = node.getRightNode();
node.getRightNode().setParentNode(null);
}
} else {
if (node == parentNode.getLeftNode()) {
if (node.getLeftNode() != null) {
parentNode.setLeftNode(node.getLeftNode());
node.getLeftNode().setParentNode(parentNode);
} else {
parentNode.setLeftNode(node.getRightNode());
node.getRightNode().setParentNode(parentNode);
}
} else {
if (node.getLeftNode() != null) {
parentNode.setRightNode(node.getLeftNode());
node.getLeftNode().setParentNode(parentNode);
} else {
parentNode.setRightNode(node.getRightNode());
node.getRightNode().setParentNode(parentNode);
}
}
}
} else {
//後繼沒有左孩子
Node<T> successor = min(node);
if (successor != node.getRightNode()) {
transplant(successor, successor.getRightNode());
successor.setRightNode(node.getRightNode());
node.getRightNode().setParentNode(successor);
}
transplant(node, successor);
successor.setLeftNode(node.getLeftNode());
node.getLeftNode().setParentNode(successor);
}
}
private void transplant(Node<T> u, Node<T> v) {
if (u == null) {
throw new IllegalArgumentException("節點不能爲空");
}
if (u.getParentNode() == null) {
root = v;
} else if (u == u.getParentNode().getLeftNode()) {
u.getParentNode().setLeftNode(v);
} else {
u.getParentNode().setRightNode(v);
}
if (v != null) {
v.setParentNode(u.getParentNode());
}
}
//查找
public Node<T> search(T key) {
Node<T> temp = root;
int result;
while(root!=null)
{
result = key.compareTo(temp.getKey());
switch (result) {
case 0:
return temp;
case 1:
temp = temp.getRightNode();
break;
case -1:
temp = temp.getLeftNode();
break;
}
}
return null;
}
//求節點rootNode下的最小節點
public Node<T> min(Node<T> rootNode) {
if(rootNode == null)
{
throw new IllegalArgumentException("參考節點不能爲空!");
}else
{
while(rootNode.getLeftNode()!=null)
{
rootNode = rootNode.getLeftNode();
}
}
return rootNode;
}
//返回rootNode下最大的節點
public Node<T> max(Node<T> rootNode) {
if(rootNode == null)
{
throw new IllegalArgumentException("參考節點不能爲空!");
}else
{
while(rootNode.getRightNode()!=null)
{
rootNode = rootNode.getRightNode();
}
}
return rootNode;
}
//返回一個節點的後繼
public Node<T> successor(Node<T> rootNode) {
if(rootNode==null)
{
throw new IllegalArgumentException("參考節點不能爲空!");
}
if(rootNode.getRightNode()!=null)
{
return min(rootNode.getRightNode());
}
Node<T> processNode = rootNode;
Node<T> parent = processNode.getParentNode(); //向上迭代
while (parent != null && processNode == parent.getRightNode()) {
processNode = parent;
parent = processNode.getParentNode();
}
return parent;
}
//返回一個節點的前驅
public Node<T> predecessor(Node<T> rootNode) {
if(rootNode==null)
{
throw new IllegalArgumentException("參考節點不能爲空!");
}
if(rootNode.getLeftNode()!=null)//有左孩子的情況
{
return max(rootNode.getLeftNode());
}
Node<T> processNode = rootNode;
Node<T> parent = processNode.getParentNode();//向上迭代
while (parent != null && processNode == parent.getLeftNode()) {
processNode = parent;
parent = processNode.getParentNode();
}
return parent;
}
//根據數組t構建二叉樹
public Node<T> buidBinarySearchTree(T[] t)
{
for(int i=0;i<t.length;i++)
{
insert(t[i]);
}
return root;
}
}
</span>
Node類:
<span style="font-size:14px;">package com.tangbo;
public class Node<T extends Comparable<T>> {
private Node<T> leftNode;
private T key;
private Node<T> rightNode;
private Node<T> parentNode;
public Node() {
super();
}
public Node(T t) {
key = t;
leftNode=null;
rightNode = null;
parentNode = null;
}
public Node<T> getLeftNode() {
return leftNode;
}
public void setLeftNode(Node<T> leftNode) {
this.leftNode = leftNode;
}
public Node<T> getRightNode() {
return rightNode;
}
public void setRightNode(Node<T> rightNode) {
this.rightNode = rightNode;
}
public Node<T> getParentNode() {
return parentNode;
}
public void setParentNode(Node<T> parentNode) {
this.parentNode = parentNode;
}
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return key+"";
}
}
</span>
如果大家認真的看代碼,你會發現一個問題二叉搜索樹對根節點有一個要求,最好根節點在選擇的時候就能夠處於數組的中間,因爲如果根節點處於數組的兩邊(如果數組排序),就會導致這顆二叉樹不平衡,就是總有一邊的樹枝太長,左右兩邊不太對等,更差的情況,如果這個數組T[] t已經被排序的話,那麼這個二叉樹就完全是一個鏈表,查找的速度是最差的,和平時的數組查找時間一樣。那我們想一想,有沒有什麼方法,能夠保持數時平衡的,就是左右子數的高度差不多呢?答案肯定有,就是“旋轉”,具體有AVL樹,在後續的文章中,我會把AVL樹寫出來!