Java 查找算法

這個問題有幾個點要先確認

  • 必須是有序,如果無序的話就只能全遍歷了
  • 查找算法跟數據結構相關,不同的數據結構適用於不同的查找算法
  • 查找算法與磁盤I/O有一定的關係,比如數據庫在索引排序的時候,如果每次都從磁盤讀取一個節點然後進行判斷

數組

如果知道下標的話就方便了,查找的複雜度爲1.
如果是針對值的查找,那麼順序遍歷是O(n),

二分查找

使用二分查找的話可以減少時間複雜度爲:O(logn)

/**
 * 二分查找又稱折半查找,它是一種效率較高的查找方法。 
  【二分查找要求】:1.必須採用順序存儲結構 2.必須按關鍵字大小有序排列。
 * @author wzj
 *
 */
public class BinarySearch { 
    public static void main(String[] args) {
        int[] src = new int[] {1, 3, 5, 7, 8, 9}; 
        System.out.println(binarySearch(src, 3));
        System.out.println(binarySearch(src,3,0,src.length-1));
    }

    /**
     * * 二分查找算法 * *
     * 
     * @param srcArray
     *            有序數組 *
     * @param des
     *            查找元素 *
     * @return des的數組下標,沒找到返回-1
     */ 
   public static int binarySearch(int[] srcArray, int des){ 

        int low = 0; 
        int high = srcArray.length-1; 
        while(low <= high) { 
            int middle = (low + high)/2; 
            if(des == srcArray[middle]) { 
                return middle; 
            }else if(des <srcArray[middle]) { 
                high = middle - 1; 
            }else { 
                low = middle + 1; 
            }
        }
        return -1;
   }

      /**  
     *二分查找特定整數在整型數組中的位置(遞歸)  
     *@paramdataset  
     *@paramdata  
     *@parambeginIndex  
     *@paramendIndex  
     *@returnindex  
     */
    public static int binarySearch(int[] dataset,int data,int beginIndex,int endIndex){  
       int midIndex = (beginIndex+endIndex)/2;  
       if(data <dataset[beginIndex]||data>dataset[endIndex]||beginIndex>endIndex){
           return -1;  
       }
       if(data <dataset[midIndex]){  
           return binarySearch(dataset,data,beginIndex,midIndex-1);  
       }else if(data>dataset[midIndex]){  
           return binarySearch(dataset,data,midIndex+1,endIndex);  
       }else {  
           return midIndex;  
       }  
   } 
}

但是插入因爲會涉及當前節點後的所有值得移動,一次,其時間複雜度爲O(n) + O(log n)

鏈表

只能從頭節點遍歷, 查找的複雜度是O(n)
插入或者是刪除,因爲只需要移動指針,時間複雜度爲O(1) + O(n)

樹的查找,主要是先序遍歷,中序等遍歷方式。
插入和刪除,還是比較快
常用的會有如下的衍生方式:

二叉樹

二叉樹的構建:

class BinaryNode{
        int value;
        BinaryNode left;
        BinaryNode right;
        public BinaryNode(int value){
            this.value = value;
            this.left = null;
            this.right = null;
        }

        public void add(int value){
            if(value > this.value){
                if(this.right != null){
                    this.right.add(value);
                }else{
                    this.right = new BinaryNode(value);
                }
            }else{
                if(this.left != null){
                    this.left.add(value);
                }else{
                    this.left = new BinaryNode(value);
                }
            }
        }

        // 中序查找
        public BinaryNode get(int value){
            if(this.value == value){
                return this;
            }
            if(this.value > value){
                return this.left.get(value);
            }

            if(this.value < value){
                return this.right.get(value);
            }
            return null;
        }
    }

插入的複雜度本身並不高,只是簡單的節點添加。但是因爲尋找插入位置的查找操作的複雜度跟樹的高度相關爲logn,極差的情況下可能接近於線性查找。

平衡二叉樹

平衡二叉樹是儘量減少數高的二叉樹,其算法中增加了左旋和右旋的操作。插入複雜度會高一些,但是會得到不錯的查找性能。

B+Tree

學習自這裏
這個就要說一下上面說的跟磁盤I/O相關的,因此爲了減少磁盤I/O。可以利用磁盤的預讀特性,一次提取大概相當於一頁大小的節點到內存中。
先要說一下B-Tree.
一個平衡的m-way查找數,其要滿足如下的條件:

  • 每節點中的數據量 < m
  • 每層節點數 <= m
  • 子數節點要完全大於、小於、或者在其之間。 也就是不能越過父節點的兩個值
  • 葉子節點中的值的個數>=m/2
  • 非葉子節點中的值的個數=子節點個數-1
    如下圖:
    這裏寫圖片描述
    可以看出,三個子節點的有兩個值,三個子節點中的數據分別對應了小於、之間、大於這個範圍

B+Tree
與上面的差別是:

  • 所有關鍵字都在葉子節點
  • 父節點存儲的都是到子節點的指針
  • 會有兩個入口,一個是根節點,另外一個是從最小葉子節點開始的指針
    這裏寫圖片描述

查找跟二叉樹比較像,因爲插入的時候已經是相當於二分算法了,所以只需要,遞歸找到就可以了。

Hash表

爲了解決一些不容易排序,或者查找的對象。 比如圖像,視頻等等。
在Java的HashMap中有使用。
是一個鏈表的數組
- 對key進行進行散列函數,求Hash值,找到其對應的鏈表。
- 剩下的解決hash衝突的問題
- 解決hash衝突,可以在命中鏈表之後順序比較
- 這裏順便再說一下一致性hash. 預置很多節點,選擇最近的節點存入,可以解決增加節點數據轉移的問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章