二分查找也稱折半查找, 是一種效率較高的查找方法,可是使用的條件比較苛刻。下面以一個猜數字遊戲爲例學習二分查找的過程。java
遊戲的規則:1到10的數字中, A選一個數字,B去猜,B猜數字的時候,A會回答小啦、大啦或者猜對啦, B怎麼作才能在最少次數裏猜對呢。數組
用二分查找法,B最多須要4次便可猜對啦,下面用個圖來展現一下過程:數據結構
B每次猜的時候是取中間的數字,總數爲偶數個時,中間數會有兩個; 總數爲奇數個時,中間數就只有1個。代碼實現以下:性能
//下面代碼是用遞歸方式來實現, 也能夠用非遞歸方式來實現
private static int binarySearch(int[] arr, int start, int end, int key){
if(arr == null){
throw new IllegalArgumentException("傳入的數組不能爲null!");
}
if(start < 0 || end < 0 || start > end){
return -1;
}
int middle = (start + end) / 2;
int temp = key - arr[middle];
if(temp == 0){
return middle;
}else if(temp > 0){
return binarySearch(arr, middle + 1, end, key);
}else {
return binarySearch(arr, start, middle - 1, key);
}
}
public static void main(String[] args){
int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int index = binarySearch(data, 0, data.length - 1, 1);
System.out.println("index: " + index);
}
複製代碼
也許數量少,才10個數,可能有種錯覺,查找也不是很快啊,每次折半,數量級是2冪次方,2的32次方已是億級別了,查找一個數也就須要32次。 二分查找使用時,必須是一個有序數組, 依靠數組index折半才能實現高效, 並且實現開發中,一般使用是對象,也會有相等的元素出現,因此實現開發中仍是要看狀況使用二分查找法。學習
跳錶的原理相似二分查找,可是數據結構用得鏈表,跳錶在原有的有序鏈表上面增長了多級索引,經過索引來實現快速查找。理論有點抽象,下面也用個圖來講明一下:spa
上圖例子中是使用了一個單鏈表實現的簡單的跳錶, Node類裏next指向下一個節點, value是當前的值,圖上面數字是節點中的值,索引的Node是一個指針, 指向原始的節點。 上圖的鏈表數值依次是 2, 5 , 10, 12, 18 , 22, 25, 26。假如在鏈表中要找是否有12, 須要從單鏈表頭開始遍歷,到第4個就找到18。當創建瞭如上圖的2層索引後,首先從第一層索引開始, 2 -> 18 , 18大於12不對,因此第二層索引 2 -> 10 找到10的位置,而後10 ->12 就找到了12。當數據量很大,查找的時間複雜度爲O(logn)。圖中索引是每隔一個節點建的索引(每隔1個至關於每次減半),你也能夠設計每隔3個節點建一個索引。索引層級也要根據數據量增長,單鏈表也能夠改爲雙鏈表。設計
上面分析了跳錶搜索的過程,接着聊聊數據插入過程,假如接下來要插入 六、七、 八、 9這幾個元素,須要保持跳錶節點順序,能夠利用索引找到第二層索引的中2 (第二層索引的10 大於 6, 7, 8 , 9)。插入元素後狀況以下圖所示:指針
看上圖所示,若是須要找9,你會發現查找速度降低,緣由是因爲插入數據致使原有索引性能退化啦,須要從新生成索引才能保證查找的速度。一樣的,刪除節點時經過索引快速找到須要刪除的節點,刪除後也一樣須要維持新索引。建立索引會佔據必定性能,在插入或者刪除數據時,若是沒插入或刪除一個元素就從新建立索引又有點浪費,因此須要平衡一下,好比記錄最底層的索引間的元素個數間隔,超過多少個再從新建立索引。跳錶的代碼實現本身回去擼吧, 本文先到此爲止(哈哈有點複雜,我擼不出來)。code