1. 程式人生 > >資料結構的Java實現(十)—— 二叉樹

資料結構的Java實現(十)—— 二叉樹

目錄

二叉樹

樹(tree)是一種抽象資料型別(ADT),用來模擬具有樹狀結構性質的資料集合。它是由n(n>=0)個有限節點組成一個具有層次關係的集合。節點一般代表一些實體,在java中節點一般代表物件。連線節點的線稱為邊,一般從一個節點到另一個節點的唯一方法就是沿著一條順著有邊的道路前進,在Java中邊通常表示引用。

特點:每個節點有零個或多個子節點;沒有父節點的節點稱為根節點;每一個非根節點有且只有一個父節點;除了根節點外,每個子節點可以分為多個不相交的子樹。

相關術語:

  1. 路徑:順著節點的邊從一個節點走到另一個節點,所經過的節點的順序排列就稱為“路徑”。
  2. :樹頂端的節點稱為根。一棵樹只有一個根,如果要把一個節點和邊的集合稱為樹,那麼從根到其他任何一個節點都必須有且只有一條路徑。A是根節點。
  3. 父節點:若一個節點含有子節點,則這個節點稱為其子節點的父節點;
  4. 子節點:一個節點含有的子樹的根節點稱為該節點的子節點;
  5. 兄弟節點:具有相同父節點的節點互稱為兄弟節點;
  6. 葉子節點:度為0的節點稱為葉節點;
  7. 樹的度:一棵樹中,最大的節點的度稱為樹的度;
  8. 節點的度:一個節點含有的子樹的個數稱為該節點的度;
  9. 節點的層次:從根開始定義起,根為第1層,根的子節點為第2層,以此類推;
  10. 樹的高度或深度:樹中節點的最大層次;

二叉樹

二叉樹:每個節點最多含有兩個子樹的樹稱為二叉樹。

二叉排序樹(Binary Sort Tree),又稱二叉查詢樹(Binary Search Tree),亦稱二叉搜尋樹,是指具有下列性質的二叉樹:

  1. 若左子樹不空,則左子樹上所有節點的值均小於它的根節點的值;
  2. 若右子樹不空,則右子樹上所有節點的值均大於它的根節點的值;
  3. 左、右子樹也分別為二叉排序樹;
  4. 沒有鍵值相等的節點。

Node類:

package yrwan09;

/**
 * 二叉樹結點
 * 
 * @author Wyran
 *
 */
public class Node {
	public int data;// 節點資料
	public Node leftChild;// 左子節點
	public Node rightChild;// 右子節點

	public Node(int data) {
		this.data = data;
	}
}

二叉樹的方法有:插入節點、查詢節點、刪除節點、前序遍歷、中序遍歷、後序遍歷等。

查詢節點,要從根節點開始遍歷,待查詢的值比當前節點值大,則搜尋右子樹;待查詢的值小於當前節點值,則搜尋左子樹;待查詢的值等於當前節點值,則停止搜尋。

插入節點,要先找到插入的位置,待插入的節點要從根節點開始進行比較,小於根節點則與根節點左子樹比較,反之則與右子樹比較,直到左子樹為空或右子樹為空,則插入到相應為空的位置,在比較的過程中要注意儲存父節點的資訊以及待插入的位置是父節點的左子樹還是右子樹,才能插入到正確的位置。

刪除節點,是二叉搜尋樹中最複雜的操作,刪除的節點有三種情況:1、該節點是葉節點 2、該節點有一個子節點 3、該節點有兩個子節點。前兩種比較簡單,第三種卻很複雜。

遍歷樹,是根據一種特定的順序訪問樹的每一個節點。比較常用的有前序遍歷,中序遍歷和後序遍歷。而二叉搜尋樹最常用的是中序遍歷。

  • 前序遍歷:根節點——》左子樹——》右子樹
  • 中序遍歷:左子樹——》根節點——》右子樹
  • 後序遍歷:左子樹——》右子樹——》根節點
package yrwan09;

/**
 * 二叉樹:插入、查詢、刪除、遍歷
 * 
 * @author Wyran
 *
 */
public class Tree {
	public Node root;// 根節點

	/**
	 * 插入節點:從根節點開始查詢相應節點,這個節點作為新插入節點的父節點。
	 * 
	 * @param value
	 */
	public void insert(int value) {
		Node newNode = new Node(value);
		if (root == null) {
			root = newNode;
			return;
		} else {
			Node current = root;
			Node parentNode = null;
			while (true) {
				parentNode = current;
				if (value < current.data) {
					current = current.leftChild;
					if (current == null) {
						parentNode.leftChild = newNode;
						return;
					}
				} else {
					current = current.rightChild;
					if (current == null) {
						parentNode.rightChild = newNode;
						return;
					}
				}
			}
		}
	}

	/**
	 * 查詢結點:從根節點開始遍歷
	 * ①、查詢值小於當前節點值,則搜尋左子樹;
	 * ②、查詢值大於當前節點值,則搜尋右子樹;
	 * ③、查詢值等於當前節點值,停止搜尋(終止條件);
	 * 
	 * @param value
	 * @return
	 */
	public Node find(int value) {
		Node current = root;
		while (current != null) {
			if (current.data > value) {// 當前值比查詢值大,搜尋左子樹
				current = current.leftChild;
			} else if (current.data < value) {// 當前值比查詢值小,搜尋右子樹
				current = current.rightChild;
			} else {
				return current;
			}
		}
		return null;// 沒找到,則返回null
	}

	/**
	 * 前序遍歷:根節點——>左子樹——>右子樹
	 * 
	 * @param node
	 */
	public void preOrder(Node current) {
		if (current != null) {
			System.out.print(current.data + " ");
			preOrder(current.leftChild);
			preOrder(current.rightChild);
		}
	}

	/**
	 * 中序遍歷:左子樹——>根節點——>右子樹
	 * 
	 * @param node
	 */
	public void inOrder(Node current) {
		if (current != null) {
			inOrder(current.leftChild);
			System.out.print(current.data + " ");
			inOrder(current.rightChild);
		}
	}

	/**
	 * 後序遍歷:左子樹——>右子樹——>根節點
	 * 
	 * @param node
	 */
	public void postOrder(Node current) {
		if (current != null) {
			postOrder(current.leftChild);
			postOrder(current.rightChild);
			System.out.print(current.data + " ");
		}
	}

	/**
	 * 刪除結點
	 * 
	 * @param value
	 * @return 
	 */
	public boolean delete(int value) {
		Node current = root;// 待刪除節點
		Node parentNode = root;
		boolean isLeftChild = false;// 判斷待刪除節點是左孩子還是右孩子

		while (current.data != value) {
			parentNode = current;
			if (current.data > value) {// 當前值比查詢值大,搜尋左子樹
				current = current.leftChild;
				isLeftChild = true;
			} else {// 當前值比查詢值小,搜尋右子樹
				current = current.rightChild;
				isLeftChild = false;
			}
			if (current == null) {// 待刪除值不存在
				return false;
			}
		}

		// ①該節點沒有子節點,是葉子結點
		if (current.leftChild == null && current.rightChild == null) {
			if (current == root) {
				root = null;
			} else if (isLeftChild) {
				parentNode.leftChild = null;
			} else {
				parentNode.rightChild = null;
			}
		} else if (current.rightChild == null && current.leftChild != null) {// ②該節點只有一個右子節點
			if (current == root) {
				root = current.leftChild;
			} else if (isLeftChild) {
				parentNode.leftChild = current.leftChild;
			} else {
				parentNode.rightChild = current.leftChild;
			}
		} else if (current.leftChild == null && current.rightChild != null) {// ②該節點只有一個左子節點
			if (current == root) {
				root = current.rightChild;
			} else if (isLeftChild) {
				parentNode.leftChild = current.rightChild;
			} else {
				parentNode.rightChild = current.rightChild;
			}
		} else {// ③該節點有兩個子節點
			Node successor = getSuccessor(current);
			if (current == root) {
				root = successor;
			} else if (isLeftChild) {
				parentNode.leftChild = successor;
			} else {
				parentNode.rightChild = successor;
			}
			successor.leftChild = current.leftChild;
		}

		return true;
	}

	/**
	 * 查詢待刪除節點的中序後繼節點
	 * 
	 * @param delNode 待刪除節點
	 * @return 中序後繼節點
	 */
	public Node getSuccessor(Node delNode) {
		Node successor = delNode;// 中序後繼節點
		Node successorParent = delNode;// 中序後繼節點的父節點
		Node current = delNode.rightChild;// 待刪除節點的右子節點開始

		while (current != null) {// 向左子樹遍歷到一個沒有左孩子的節點
			successorParent = successor;
			successor = current;
			current = current.leftChild;
		}

		// 如果中序後繼節點不是待刪除節點的右孩子 把待移動的子樹全部移動完
		if (successor != delNode.rightChild) {
			successorParent.leftChild = successor.rightChild;
			successor.rightChild = delNode.rightChild;
		}
		return successor;
	}
}