二叉查找樹
二叉查找樹出現的目的是使查詢的速率整體能夠維持在O(logn)上,而又不像鏈表那樣查詢一定需要O(logN)的時間復雜度,和數組那樣在增刪上會引起整個數組進行重構導致的效率問題.
二叉查找樹的特點
- 二叉查找樹的的左子節點一定比當前節點小,右子節點一定比當前節點大.
- 二叉查找樹不允許存在相同的值.
這樣在理想的情況下,二叉查找樹的查詢效率就在O(logN)上了.
二叉查找樹的實現
package Tree;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/**
* Created by Hammy on 2018/3/3.
*/
public class BST<T extends Comparable> implements BinarySearchTree<T>
{
public TreeNode<T> root;
public BST(){
this.root = null;
}
public BST(T data){
root = new TreeNode<T>(data,null,null);
}
@Override
public boolean isEmpty() {
return size()==0;
}
@Override
public int size() {
if(root==null)
return 0;
return size(root);
}
private int size(TreeNode<T> node){
if(node==null)
return 0;
return size(node.left) + 1 + size(node.right);
}
@Override
public int height() {
if(root==null){
return 0;
}
return height(root);
}
private int height(TreeNode<T> node){
if(node==null)
return 0;
int left = height(node.left);
int right = height(node.right);
return (left>right)?(left+1):(right+1);
}
@Override
public String preOrder() {
StringBuffer stringBuffer = new StringBuffer();
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> tempNode = root;
while(tempNode!=null||!stack.isEmpty()){
if(tempNode!=null){
stringBuffer.append(tempNode.data+",");
stack.push(tempNode);
tempNode=tempNode.left;
}else{
tempNode=stack.pop();
tempNode=tempNode.right;
}
}
return stringBuffer.toString();
}
@Override
public String inOrder() {
StringBuffer stringBuffer = new StringBuffer();
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> tempNode = root;
while(tempNode!=null||!stack.isEmpty()){
while(tempNode!=null){
stack.push(tempNode);
tempNode=tempNode.left;
}
if(!stack.isEmpty()){
tempNode=stack.pop();
stringBuffer.append(tempNode.data+",");
tempNode=tempNode.right;
}
}
return stringBuffer.toString();
}
@Override
public String postOrder() {
StringBuffer stringBuffer = new StringBuffer();
Stack<TreeNode<T>> stack = new Stack<TreeNode<T>>();
TreeNode<T> curNode = root;
TreeNode<T> preNode = root;
while(curNode!=null||!stack.isEmpty()){
while(curNode!=null){
stack.push(curNode);
curNode=curNode.left;
}
if(!stack.isEmpty()){
TreeNode<T> tempNode = stack.peek().right;
if(tempNode==null||tempNode==preNode){
curNode=stack.pop();
stringBuffer.append(curNode.data+",");
preNode=curNode;
curNode=null;
}else{
curNode=tempNode;
}
}
}
return stringBuffer.toString();
}
@Override
public String levelOrder() {
StringBuffer stringBuffer = new StringBuffer();
Queue<TreeNode<T>> queue = new LinkedList<TreeNode<T>>();
TreeNode<T> tempNode = root;
while(tempNode!=null){
stringBuffer.append(tempNode.data+",");
if(tempNode.left!=null)
queue.add(tempNode.left);
if(tempNode.right!=null)
queue.add(tempNode.right);
tempNode=queue.poll();
}
return stringBuffer.toString();
}
@Override
public void insert(T data) throws Exception {
if(data==null)
throw new Exception("data can't be null");
root=insert(root,data);
}
private TreeNode<T> insert(TreeNode<T> node,T data){
if(node==null){
node=new TreeNode<T>(data,null,null);
}else{
int compareResult=data.compareTo(node.data);
if(compareResult<0)
node.left=insert(node.left,data);
if(compareResult>0)
node.right=insert(node.right,data);
}
return node;
}
@Override
public T remove(T data) throws Exception {
if(data==null)
throw new Exception("data can't be null");
return remove(root,data).data;
}
private TreeNode<T> remove(TreeNode<T> node,T data) throws Exception{
if(node==null)
return null;
int compareResult=data.compareTo(node.data);
if(compareResult>0){
node.right=remove(node.right,data);
return node;
}
if(compareResult<0){
node.left=remove(node.left,data);
return node;
}
//如果要刪除節點的左子樹為空
if(node.left==null){
TreeNode<T> rightNode = node.right;
node.right=null;
return rightNode;
}
if(node.right==null){
TreeNode<T> leftNode = node.left;
node.left=null;
return leftNode;
}
//左右子樹都不為空
TreeNode<T> successorNode = new TreeNode<T>(findMin(node.right).data,null,null);
successorNode.left=node.left;
successorNode.right=remove(node.right,data);
//將原節點set null
node.left=null;
node.right=null;
if(root.data==data)
root=successorNode;
return successorNode;
}
@Override
public T findMax() throws Exception {
if(root==null)
throw new Exception("data can't be null");
return findMax(root).data;
}
private TreeNode<T> findMax(TreeNode<T> node){
TreeNode<T> tempNode = node;
while(tempNode.right!=null){
tempNode=tempNode.right;
}
return tempNode;
}
@Override
public T findMin() throws Exception {
if(root==null)
throw new Exception("data can't be null");
return findMin(root).data;
}
private TreeNode<T> findMin(TreeNode<T> node) throws Exception{
TreeNode<T> tempNode = node;
while(tempNode.left!=null)
tempNode=tempNode.left;
return tempNode;
}
@Override
public boolean contain(T data) throws Exception {
if(data==null)
throw new Exception("data can't be null");
return contain(root,data);
}
private boolean contain(TreeNode<T> node,T data){
if(node==null)
return false;
int compareResult=data.compareTo(node.data);
if(compareResult>0)
return contain(node.right,data);
if(compareResult<0)
return contain(node.left,data);
return true;
}
}
二叉查找樹會引發的問題,如果當前數據幾乎是一個順序或者逆序的前提下,二叉查找樹就把一個偏向一邊甚至退化為鏈表.這樣就完全喪失了它存在的意義.
AVL樹
AVL樹是理想的平衡二叉樹,它保證左子節點和右子節點的深度最多相差1,但每次插入和刪除二叉樹整體都會進行旋轉.但它整體的查詢的效率維持在了O(logN)
B樹
B樹這種數據結構的特點就是深度低,這樣做的目的是減少訪問外存的次數.
B樹的節點設計為一個【k,v】對,這樣就可以根據k的規則就尋找想找的value值.
特點:
- 一顆m階的b-tree最多有m個孩子
- 除了根節點和葉子節點外,每個節點至少有Ceil(m/2)個孩子
-所有葉子節點都在同一層.
-關鍵字的個數n滿足:ceil(m/2)-1<=n<=m-1 - ki(i=1,…n)為關鍵字,且關鍵字升序排序。
- Pi(i=1,…n)為指向子樹根節點的指針。P(i-1)指向的子樹的所有節點關鍵字均小于ki,但都大于k(i-1)
B+樹
B+樹存在的目的是為了降低b樹的深度.特點如下:
- 非葉子節點只存儲鍵值信息。
- 所有葉子節點之間都有一個鏈指針。
- 數據記錄都存放在葉子節點中。