二叉搜索樹——《算法導論》學習心得(十二)


一、算法 

     二叉排序樹(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樹寫出來!


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com