標籤:演算法
這個問題有幾個點要先確認
- 必須是有序,如果無序的話就只能全遍曆了
- 尋找演算法跟資料結構相關,不同的資料結構適用於不同的尋找演算法
- 尋找演算法與磁碟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. 預置很多節點,選擇最近的節點存入,可以解決增加節點資料轉移的問題。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Java 尋找演算法