資料結構與演算法---樹
阿新 • • 發佈:2019-01-06
對於大量的輸入資料,連結串列的線性訪問時間太慢,不宜使用。而樹是一種簡單的資料結構,其大部分操作的執行時間平均為O(log N)
二叉樹
二叉樹是一棵樹,其中每個節點都不能有多於兩個的兒子。
實現
class BinaryNode(){
Object element;
BinaryNode left;
BinaryNode right;
}
查詢樹ADT — 二叉查詢樹
使二叉樹成為二叉查詢樹的性質是,對於樹中的每個節點X,他的左子樹中所有項的值小於X中的項,而它的右子樹中所有項的值小於X中的項
public class BinarySearchTree <E extends Comparable<? super E>>{ private static class BinaryNode<E>{ BinaryNode(E theElement) { this(theElement,null,null); } BinaryNode(E element, BinaryNode<E> left, BinaryNode<E> right) { this.element = element; this.left = left; this.right = right; } E element; BinaryNode<E> left; BinaryNode<E> right; } private BinaryNode<E> root; public BinarySearchTree(){root = null;} public void makeEmpty(){root = null;} public boolean isEmpty(){return root == null;} public boolean contains(E x){ return contains(x, root); } public E findMin(){ if(isEmpty()) throw new BufferUnderflowException(); return findMin(root).element; } public E findMax(){ if(isEmpty()) throw new BufferUnderflowException(); return findMax(root).element; } public void insert(E x){root = insert(x, root);} public void remove(E x){root = remove(x, root);} public void printTree(){}; private BinaryNode<E> remove(E x, BinaryNode<E> root) { if (root == null) return root; int compareResult = x.compareTo(root.element); if (compareResult < 0) root.left = remove(x, root.left); else if (compareResult > 0) root.right = remove(x, root.right); else if (root.left != null && root.right != null) { root.element = findMin(root.right).element; root.right = remove(root.element,root.right); } else root = (root.left != null)?root.left:root.right; return root; } private BinaryNode<E> insert(E x, BinaryNode<E> root) { if (root == null) return new BinaryNode<E>(x, null, null); int compareResult = x.compareTo(root.element); if (compareResult < 0) root.left = insert(x, root.left); else if (compareResult > 0) root.right = insert(x, root.right); else ; return root; } private BinaryNode<E> findMax(BinaryNode<E> root) { if(root != null) while (root.right != null) root = root.right; return root; } private BinaryNode<E> findMin(BinaryNode<E> root) { if (root ==null) return null; else if (root.left ==null) return root; return findMin(root.left); } private boolean contains(E x, BinaryNode<E> root) { if(root == null) return false; int compareResult = x.compareTo(root.element); if(compareResult < 0) return contains(x, root.left); else if(compareResult > 0) return contains(x, root.right); else return true; }
AVL樹
AVL樹是帶有平衡條件的二叉查詢樹。這個平衡條件必須要容易保持。空樹的高度定義為 -1 。每一個幾點保留高度資訊。
在插入資料時會破壞的平衡結構,所以每次插入刪除操作都需要對樹進行修正。修正的方法很簡單,我們這個操作為旋轉。
分別為單旋轉和雙旋轉,具體實現可以上網查閱資料。
public class AvlTree<E extends Comparable<? super E> >{ private static class AvlNode<E>{ AvlNode(E element) { this(element, null, null); } AvlNode(E element, AvlNode<E> left, AvlNode<E> right) { this.element = element; this.left = left; this.right = right; } E element; AvlNode<E> left; AvlNode<E> right; int height; } private AvlNode<E> root; public void insert(E x){root = insert(x,root);} private int height(AvlNode<E> root) { return root == null ? -1 : root.height; } private AvlNode<E> insert(E x, AvlNode<E> t){ System.out.println("插入"+ x); if(t == null) { System.out.println("插入根節點"+x); return new AvlNode<E>(x, null, null); } int compareResult = x.compareTo(t.element); if(compareResult < 0){ System.out.println("插入比根節點小的值"); t.left = insert(x,t.left); if(height(t.left) - height(t.right) == 2) if (x.compareTo(t.left.element) < 0) { System.out.println("插入比根節點小的值,單旋"); t = rotateWithLeftChild(t); } else{ System.out.println("插入比根節點小的值,雙旋"); t = doubleWithLeftChild(t);} } else if (compareResult > 0){ System.out.println("插入比根節點大的值"); t.right = insert(x, t.right); if(height(t.right) - height(t.left) == 2) if(x.compareTo(t.right.element) > 0){ System.out.println("插入比根節點大的值,單旋"); t = rotateWithRightChild(t);} else{ System.out.println("插入比根節點大的值,雙旋"); t = doubleWithRightChild(t);} } else ; t.height = Math.max(height(t.left),height(t.right)) + 1; return t; } private AvlNode<E> rotateWithRightChild(AvlNode<E> k2) { AvlNode<E> k1 = k2.right; k2.right = k1.left; k1.left = k2; k2.height = Math.max(height(k2.right),height(k2.right))+1; k1.height = Math.max(height(k1.right),k2.height)+1; return k1; } private AvlNode<E> doubleWithRightChild(AvlNode<E> k3) { k3.right = rotateWithLeftChild(k3.right); return rotateWithRightChild(k3); } private AvlNode<E> rotateWithLeftChild(AvlNode<E> k2) { AvlNode<E> k1 = k2.left; k2.left = k1.right; k1.right = k2; k2.height = Math.max(height(k2.right),height(k2.right))+1;; k1.height = Math.max(height(k1.left),k2.height)+1; return k1; } private AvlNode<E> doubleWithLeftChild(AvlNode<E> k3) { k3.left = rotateWithRightChild(k3.left); return rotateWithLeftChild(k3); } }
伸展樹
伸展樹的基本想法是,當一個節點被訪問後,它就要經過一系列AVL樹的旋轉被推到根上。如果一個節點很深,那麼其路徑上就存在許多也相對較深的節點,通過重新構造可以減少對所有這些節點很深,那麼在其路徑上就存在許多也相對較深的節點,通過重新構造可以減少對所有這些節點的進一步訪問所花費的時間。因此,那我們要求重新構造應具有平衡這棵樹的作用。
樹的遍歷
樹的遍歷方式有三種
- 先序遍歷
當前節點在其兒子節點之前處理
- 中序遍歷
先處理左子樹,然後是當前節點,再處理右子樹,其總的執行時間是O(N)
- 後序遍歷
當前節點在其兒子節點之後處理,比如計算樹的高度時
- 層序遍歷
所有深度為d的節點要在深度d+1之前進行處理,與上述三種遍歷方式不同的是它不使用遞迴方式進行,他用到佇列