1. 程式人生 > >【Java】 大話數據結構(11) 查找算法(2)(二叉排序樹/二叉搜索樹)

【Java】 大話數據結構(11) 查找算法(2)(二叉排序樹/二叉搜索樹)

PE bsp clas 代碼 根節點 替代 找到 extend true

本文根據《大話數據結構》一書,實現了Java版的二叉排序樹/二叉搜索樹

二叉排序樹介紹

在上篇博客中,順序表的插入和刪除效率還可以,但查找效率很低;而有序線性表中,可以使用折半、插值、斐波那契等查找方法來實現,但因為要保持有序,其插入和刪除操作很耗費時間。

二叉排序樹(Binary Sort Tree),又稱為二叉搜索樹,則可以在高效率的查找下,同時保持插入和刪除操作也又較高的效率。下圖為典型的二叉排序樹。

技術分享圖片

二叉查找樹具有以下性質:

  (1) 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
  (2) 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

  (3) 任意節點的左、右子樹也分別為二叉查找樹。

查找操作

思路:查找值與結點數據對比,根據大小確定往左子樹還是右子樹進行下一步比較。

采用遞歸的查找算法

	/*
	 * 查找
	 */
	public boolean SearchBST(int key) {
		return SearchBST(key, root);
	}

	private boolean SearchBST(int key, Node node) {
		if (node == null)
			return false;
		if (node.data == key) {
			return true;
		} else if (node.data < key) {
			return SearchBST(key, node.rChild);
		} else {
			return SearchBST(key, node.lChild);
		}
	}

 

采用非遞歸的查找算法

	/*
	 * 查找,非遞歸
	 */
	public boolean SearchBST2(int key) {
		Node p = root;
		while (p != null) {
			if (p.data > key) {
				p = p.lChild;
			} else if (p.data < key) {
				p = p.rChild;
			} else {
				return true;
			}
		}
		return false;
	}

  

插入操作

思路:與查找類似,但需要一個父節點來進行賦值。

采用非遞歸的插入算法:

	/*
	 * 插入,自己想的,非遞歸
	 */
	public boolean InsertBST(int key) {
		Node newNode = new Node(key);
		if (root == null) {
			root = newNode;
			return true;
		}
		Node f = null; // 指向父結點
		Node p = root; // 當前結點的指針
		while (p != null) {
			if (p.data > key) {
				f = p;
				p = p.lChild;
			} else if (p.data < key) {
				f = p;
				p = p.rChild;
			} else {
				System.out.println("樹中已有相同數據,不再插入!");
				return false;
			}
		}
		if (f.data > key) {
			f.lChild = newNode;
		} else if (f.data < key) {
			f.rChild = newNode;
		}
		return true;
	}

  

采用遞歸的插入算法:

	/*
	 * 插入,參考別人博客,遞歸
	 * 思路:把null情況排除後用遞歸,否則無法賦值
	 */
	public boolean InsertBST2(int key) {
		if (root == null) {
			root = new Node(key);
			return true;
		}
		return InsertBST2(key, root);
	}

	private boolean InsertBST2(int key, Node node) {
		if (node.data > key) {
			if (node.lChild == null) {
				node.lChild = new Node(key);
				return true;
			} else {
				return InsertBST2(key, node.lChild);
			}
		} else if (node.data < key) {
			if (node.rChild == null) {
				node.rChild = new Node(key);
				return true;
			} else {
				return InsertBST2(key, node.rChild);
			}
		} else {
			System.out.println("樹中已有相同數據,不再插入!");
			return false;
		}
	}

刪除操作

思路:

(1)刪除葉子結點

  直接刪除;

(2)刪除僅有左或右子樹的結點

  子樹移動到刪除結點的位置即可;

(3)刪除左右子樹都有的結點

  找到刪除結點p的直接前驅(或直接後驅)s,用s來替換結點p,然後刪除結點s,如下圖所示。

技術分享圖片

首先找到刪除結點位置及其父結點

	/*
	 * 刪除操作,先找到刪除結點位置及其父結點
	 * 因為需要有父結點,所以暫時沒想到遞歸的方法(除了令Node對象帶個parent屬性)
	 */
	public boolean deleteBST(int key) {
		if (root == null) {
			System.out.println("空表,刪除失敗");
			return false;
		}
		Node f = null; // 指向父結點
		Node p = root; // 指向當前結點
		while (p != null) {
			if (p.data > key) {
				f = p;
				p = p.lChild;
			} else if (p.data < key) {
				f = p;
				p = p.rChild;
			} else {
				delete(p, f);
				return true;
			}
		}
		System.out.println("該數據不存在");
		return false;
	}

  

再根據上述思路進行結點p的刪除:(需註意刪除結點為根節點的情況)

	/*
	 * 刪除結點P的操作
	 * 必須要有父結點,因為Java無法直接取得變量p的地址(無法使用*p=(*p)->lChild)
	 */
	private boolean delete(Node p, Node f) {// p為刪除結點,f為其父結點
		if (p.lChild == null) { // 左子樹為空,重接右子樹
			if (p == root) { // 被刪除結點為根結點,該情況不能忽略
				root = root.rChild;
			} else {
				if (f.data > p.data) { // 被刪結點為父結點的左結點,下同
					f.lChild = p.rChild;
					p = null;
				} else {// 被刪結點為父結點的右結點,下同
					f.rChild = p.rChild;
					p = null;
				}
			}
		} else if (p.rChild == null) { // 右子樹為空,重接左子樹
			if (p == root) { // 被刪除結點為根結點
				root = root.lChild;
			} else {
				if (f.data > p.data) {
					f.lChild = p.lChild;
					p = null;
				} else {
					f.rChild = p.lChild;
					p = null;
				}
			}
		} else { // 左右子樹都不為空,刪除位置用前驅結點替代
			Node q, s;
			q = p;
			s = p.lChild;
			while (s.rChild != null) { // 找到待刪結點的最大前驅s
				q = s;
				s = s.rChild;
			}
			p.data = s.data; // 改變p的data就OK
			if (q != p) {
				q.rChild = s.lChild;
			} else {  //這種情況也別忽略了
				q.lChild = s.lChild;
			}
			s = null;
		}
		System.out.println("刪除成功!");
		return true;
	}

  

完整代碼(含測試代碼)

package BST;

/**
 * 二叉排序樹(二叉查找樹)
 * 若是泛型,則要求滿足T extends Comparable<T> static問題
 * @author Yongh
 *
 */
class Node {
	int data;
	Node lChild, rChild;

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

public class BSTree {
	private Node root;

	public BSTree() {
		root = null;
	}

	/*
	 * 查找
	 */
	public boolean SearchBST(int key) {
		return SearchBST(key, root);
	}

	private boolean SearchBST(int key, Node node) {
		if (node == null)
			return false;
		if (node.data == key) {
			return true;
		} else if (node.data < key) {
			return SearchBST(key, node.rChild);
		} else {
			return SearchBST(key, node.lChild);
		}
	}

	/*
	 * 查找,非遞歸
	 */
	public boolean SearchBST2(int key) {
		Node p = root;
		while (p != null) {
			if (p.data > key) {
				p = p.lChild;
			} else if (p.data < key) {
				p = p.rChild;
			} else {
				return true;
			}
		}
		return false;
	}

	/*
	 * 插入,自己想的,非遞歸
	 */
	public boolean InsertBST(int key) {
		Node newNode = new Node(key);
		if (root == null) {
			root = newNode;
			return true;
		}
		Node f = null; // 指向父結點
		Node p = root; // 當前結點的指針
		while (p != null) {
			if (p.data > key) {
				f = p;
				p = p.lChild;
			} else if (p.data < key) {
				f = p;
				p = p.rChild;
			} else {
				System.out.println("樹中已有相同數據,不再插入!");
				return false;
			}
		}
		if (f.data > key) {
			f.lChild = newNode;
		} else if (f.data < key) {
			f.rChild = newNode;
		}
		return true;
	}

	/*
	 * 插入,參考別人博客,遞歸
	 * 思路:把null情況排除後用遞歸,否則無法賦值
	 */
	public boolean InsertBST2(int key) {
		if (root == null) {
			root = new Node(key);
			return true;
		}
		return InsertBST2(key, root);
	}

	private boolean InsertBST2(int key, Node node) {
		if (node.data > key) {
			if (node.lChild == null) {
				node.lChild = new Node(key);
				return true;
			} else {
				return InsertBST2(key, node.lChild);
			}
		} else if (node.data < key) {
			if (node.rChild == null) {
				node.rChild = new Node(key);
				return true;
			} else {
				return InsertBST2(key, node.rChild);
			}
		} else {
			System.out.println("樹中已有相同數據,不再插入!");
			return false;
		}
	}

	/*
	 * 刪除操作,先找到刪除結點位置及其父結點
	 * 因為需要有父結點,所以暫時沒想到遞歸的方法(除了令Node對象帶個parent屬性)
	 */
	public boolean deleteBST(int key) {
		if (root == null) {
			System.out.println("空表,刪除失敗");
			return false;
		}
		Node f = null; // 指向父結點
		Node p = root; // 指向當前結點
		while (p != null) {
			if (p.data > key) {
				f = p;
				p = p.lChild;
			} else if (p.data < key) {
				f = p;
				p = p.rChild;
			} else {
				delete(p, f);
				return true;
			}
		}
		System.out.println("該數據不存在");
		return false;
	}

	/*
	 * 刪除結點P的操作
	 * 必須要有父結點,因為Java無法直接取得變量p的地址(無法使用*p=(*p)->lChild)
	 */
	private boolean delete(Node p, Node f) {// p為刪除結點,f為其父結點
		if (p.lChild == null) { // 左子樹為空,重接右子樹
			if (p == root) { // 被刪除結點為根結點,該情況不能忽略
				root = root.rChild;
			} else {
				if (f.data > p.data) { // 被刪結點為父結點的左結點,下同
					f.lChild = p.rChild;
					p = null;
				} else {// 被刪結點為父結點的右結點,下同
					f.rChild = p.rChild;
					p = null;
				}
			}
		} else if (p.rChild == null) { // 右子樹為空,重接左子樹
			if (p == root) { // 被刪除結點為根結點
				root = root.lChild;
			} else {
				if (f.data > p.data) {
					f.lChild = p.lChild;
					p = null;
				} else {
					f.rChild = p.lChild;
					p = null;
				}
			}
		} else { // 左右子樹都不為空,刪除位置用前驅結點替代
			Node q, s;
			q = p;
			s = p.lChild;
			while (s.rChild != null) { // 找到待刪結點的最大前驅s
				q = s;
				s = s.rChild;
			}
			p.data = s.data; // 改變p的data就OK
			if (q != p) {
				q.rChild = s.lChild;
			} else {
				q.lChild = s.lChild;
			}
			s = null;
		}
		System.out.println("刪除成功!");
		return true;
	}

	/*
	 * 中序遍歷
	 */
	public void inOrder() {
		inOrder(root);
		System.out.println();
	}

	public void inOrder(Node node) {
		if (node == null)
			return;
		inOrder(node.lChild);
		System.out.print(node.data + " ");
		inOrder(node.rChild);
	}

	/*
	 * 測試代碼
	 */
	public static void main(String[] args) {
		BSTree aTree = new BSTree();
		BSTree bTree = new BSTree();
		int[] arr = { 62, 88, 58, 47, 35, 73, 51, 99, 37, 93 };
		for (int a : arr) {
			aTree.InsertBST(a);
			bTree.InsertBST2(a);
		}
		aTree.inOrder();
		bTree.inOrder();
		System.out.println(aTree.SearchBST(35));
		System.out.println(bTree.SearchBST2(99));
		aTree.deleteBST(47);
		aTree.inOrder();
	}
}

  

技術分享圖片
35 37 47 51 58 62 73 88 93 99 
35 37 47 51 58 62 73 88 93 99 
true
true
刪除成功!
35 37 51 58 62 73 88 93 99 
BSTree

【Java】 大話數據結構(11) 查找算法(2)(二叉排序樹/二叉搜索樹)