二叉搜索樹

二叉搜索樹是以一棵二叉樹來組織的,如下圖所示。這樣一棵樹可以使用一個鏈表數據結構來表示,其中每個結點就是一個對象。除了key和衛星數據之外,每個結點還飲食屬性left, right 和 p, 它們分別指向結點的左孩子,右孩子和雙新。如果某個孩子結點和父結點不存在,則相應屬性的值爲NIL。根結點是樹中唯一父指針爲NIL的結點 。
二叉搜索樹中的關鍵字總是以滿足二叉搜索樹性質的方式來存儲:
設x是二叉搜索樹中的一個結點。如果y是x左子樹中的一個結點,那麼y.key<=x.key。如果y是x右子樹中的一個結點,那麼y.key >= x.key。
所以創建一個關於它節點的類Node:

結構圖可參看:

參考代碼:
public class Node{
		//依次保存左右節點以及父節點
		Node left;
		Node right;
		Node parent;
		//存放數據值
		private Object data;
		public Node(){
		}
		public Node(Object data){
			this.data = data;
		}
		public Node(Object data, Node parent, Node left, Node right){
			this.data = data;
			this.parent = parent;
			this.left = left;
			this.right = right;
		}
		public String toString(){//只返回data的值
			return    data + "";
		}
		public boolean equals(Object obj){//判斷兩個對象是否相等
			if(this == obj){
				return true;
			}
			if(obj.getClass() == Node.class){ //getClass()進行類型判斷  instanceof: 判斷左邊對象是否是右邊類的實例 --boolean類型的值
				Node target = (Node)obj;
				return data.equals(target.data)
						&& left == target.left
						&& right == target.right
						&& parent == target.parent;
			}
			return false;
		}
	}

遍歷:

這裏採用的是中序遍歷的方法。可參考:點擊打開鏈接
參考代碼:
//中序
	public List<Node> in_order(){
		return in_order(root);
	}

	public List<Node> in_order(Node node){
		List<Node> list = new ArrayList<Node>();
		if(node.left != null){
			list.addAll(in_order(node.left));
		}
		list.add(node);
		if(node.right != null){
			list.addAll(in_order(node.right));
		}
		return list;
	}

查找:
就是根據它的存儲特點去不斷搜尋,因爲他遵循左節點小於根節點,右節點大於根結點的特點。所以沿一條簡單路徑向下,然後找到相等爲止。
//查詢
	public Node tree_search(T data){
		return tree_search(root, data);
	}
	private Node tree_search(Node node, T data){// 根據元素去訪問這個值
		if(node == null){
			return null;
		}
		//x.compareTo(y) x=y -- 0, x > y - 1, x < y - -1
		int cmp = data.compareTo(node.data);
		if(cmp < 0){
			return tree_search(node.left, data);
		} 
		else if(cmp > 0){
			return tree_search(node.right,data);
		}
		else{
			return node;
		}	
	}

最大/最小關鍵值元素:
以最小值爲例:
如果結點x沒有左子樹,那麼由於x右子樹中的每個關鍵字都至少大於或等於x.key,則以x爲根的子樹中的最小關鍵字是x.key。如果結點x有左子樹,那麼由於其右子樹中沒有關鍵字小於x.key,且在左子樹中的每個關鍵字不大於x.key,則以x爲根的子樹中的最小關鍵字一定在經x.left爲根的子樹中。
//返回最大 和最小關鍵字元素
	public Node mininum(){
		return mininum(root);
	}
	public Node mininum(Node node){
		while(node.left != null){
			node = node.left;
		}
		return node;
	}

	public Node maxinum(){
		return maxinum(root);
	}
	public Node maxinum(Node node){
		while(node.right != null){
			node = node.right;
		}
		return node;
	}

前驅和後繼:
這裏是根據它排序後的順序,根據指定的值找到它的前驅和後繼
//尋找前驅和後繼 簡單的遵循一條路徑沿樹向下 或者 沿樹向上。
	public Node successor(Node node){
		if(node.right != null){
			return mininum(node.right);
		}
		Node newNode = node.parent;
		while(newNode != null && node == newNode.right){
			node = newNode;
			newNode = newNode.parent;
		}
		return newNode;
	}

	public Node presuccessor(Node node){
		if(node.left != null){
			return mininum(node.left);
		}
		Node newNode = node.parent;
		while(newNode != null && node == newNode.left){
			node = newNode;
			newNode = newNode.parent;
		}
		return newNode;
	}

插入:
因爲要保證二叉搜索樹的特點,所以先根據根結點的位置, 和輸入的值進行比較,如果大於必然在右子樹,如果小於必然在左子樹裏,不斷更新這個根節點,直到找到能插入這個新的元素的位置,在根據它的特點判斷是它的右節點還是左節點即可。
//插入數據
	public Node insert(T data) {
		// 如果根節點爲空
		if(root == null){
			root = new Node(data, null, null, null); 
		}
		else{
			Node current = root;
			Node parent = null;
			int cmp = 0;
			//搜索到合適的葉子節點,以該節點爲父節點添加新的節點
			while(current != null){//找到合適的位置 假定的"null"會被我指定的值替換
				parent = current;
				cmp = data.compareTo(current.data);
				if(cmp < 0){
					current = current.left;
				}
				else{
					current = current.right;
				}
			}
			//創建新節點, 通過直接的比較知道了它的父節點的值, 接下來就是通過比較 判斷是他父節點的   左還是右節點
			Node newNode = new Node(data, parent, null, null);
			if(cmp > 0){
				parent.right = newNode;
			}
			else{
				parent.left = newNode;
			}
			return newNode;
		}
		return null;
	}

刪除:
  • 如果z沒有左孩子(圖12-4(a)),那麼用其右孩子來替換z,這個右孩子可以是NIL,也可以不是。當z的右孩子是NIL時,此時這種情況歸爲z沒有孩子結點的情形。當z的右孩子非NIL時,這種情況就是z僅有一個孩子結點的情形,該孩子是其右孩子。
  • 如果z僅有一個孩子且爲其左孩子(圖12-4(b)),那麼用其左孩子來替換z。
  • 否則,z既有一個左孩子又有一個右孩子。我們要查找z的後繼y,這個後繼位於z的右子樹中並且沒有左孩子(見練習12.2-5)。現在需要將y移出原來的位置進行拼接,並替換樹中的z。
  • 如果y是z的右孩子(圖12-4(c)),那麼用y替換z,並僅留下y的右孩子。
  • 否則,y位於z的右子樹中但並不是z的右孩子(圖12-4(d))。在這種情況下,先用y的右孩子替換y,然後再用y替換z。
我在這裏使用一個transplate()方法,是用一棵"子樹"替換另一棵"子樹"併成爲其雙親孩子節點的方法。討論了輸入的其中一個子樹爲根(第一個if)、左節點(else if())中、右節點(else)中不斷的對其更新。
//刪除指定節點
	public void delete(T element){

		//獲取要刪除的節點   注意這裏是通過查找而不是new
		Node target = tree_search(element);
		if(target == null){
			return;
		}
		else{
			Node newNode = null;

			//沒有左孩子, 刪除改節點之後把自己的右節點補上來即可
			if(target.left == null){
				transplant(target, target.right);
			}
			//由一個左孩子, 沒有右孩子
			else if(target.right == null){
				transplant(target, target.left);
			}
			else{
				//如果它有右子樹(或者叫右子樹非空) 那麼它的後繼一定是他右子樹的最小值
				newNode = mininum(target.right);
				if(newNode.parent != target){
					transplant(newNode, newNode.right);
					newNode.right = target.right;
					newNode.right.parent = newNode;
				}
				transplant(target, newNode);
				newNode.left = target.left;
				newNode.left.parent = newNode;
			}	
		}
	}
	private void transplant(Node node1, Node node2) {
		if(node1.parent == null){
			Node current = root;
			current = node2;
		}
		else if(node1 == node1.parent.left){
			node1.parent.left = node2;
		}
		else{
			node1.parent.right = node2;
		}
		if(node2 != null){
			node1.parent = node2.parent;
		}
	}

測試部分和截圖:

構建二叉搜索樹:
//構建二叉排序樹
	public SortBinTree(){
		root = null;
	}
	public SortBinTree(T data){
		root = new Node(data, null, null, null);
	}
測試:
public static void main(String[] args) {
		SortBinTree<Integer> sbt = new SortBinTree<Integer>(15);
		SortBinTree.Node node = sbt.insert(5);
		sbt.insert(20);
		sbt.insert(10);
		sbt.insert(3);
		sbt.insert(8);
		sbt.insert(30);
		System.out.println(sbt.in_order());
		System.out.println("最小關鍵元素值" + sbt.mininum());
		System.out.println("最大關鍵元素值" +sbt.maxinum());
		sbt.insert(17);
		System.out.println(sbt.in_order());
		System.out.println("節點5的前驅是:" + sbt.presuccessor(node));
		System.out.println("節點5的後繼是:" + sbt.successor(node));
		sbt.delete(8);
		System.out.println(sbt.in_order());
	}


參考:《算法導論》


以上就是這篇的內容了,如果您有什麼覺得改進的地方或者發現了錯誤的部分,請指教。謝謝!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章