算法二——二分查找
文章出處:極客時間《數據結構和算法之美》-作者:王爭。該系列文章是本人的學習筆記。
思考題目
1 用最省內存的方式查找數據。
2 快速定位ip所在省市
二分查找的速度
二分每次都通過跟區間中的中間元素對比,將待查找的區間縮小為一半,直到區間為0或者找到元素。需要重點關注退出條件、mid取值、low和high的更新。
二分的時間復雜度O(logn)。logn少的驚人之處和指數的大的驚人之處類似。2的32次方是4億多。在4億多有序數組中查找數據,只需要32次。很少吧。
簡單代碼實現
簡單是因為數組中沒有重復數據。
public static int easySearch(int[] a,int value){if(a==null) return -1;int n = a.length;int low = 0;int high = n-1;return easySearch(a, value, low, high);}private static int easySearch(int[] a,int value,int low,int high){if(low>high) return -1;int mid = low + ((high-low)>>1);if(value==a[mid]) return mid;if(value<a[mid]) return easySearch(a,value,low,mid-1);return easySearch(a,value,mid+1,high);}二分的應用場景
1 二分依賴的是順序表,也就是數組。鏈表復雜度變高。因為鏈表隨機查找的時間復雜度是O(n)。
如果使用鏈表,第一次查找a[mid],需要n2\dfrac{n}{2}2n?次查找,第二次查找a[mid],需要n4\dfrac{n}{4}4n?次查找,依次類推所需要的查找有sum=n2+n4+n8+....+1=n?1sum=\dfrac{n}{2}+\dfrac{n}{4}+\dfrac{n}{8}+....+1=n-1sum=2n?+4n?+8n?+....+1=n?1。時間復雜度O(n)。
2 二分針對的是有序數據。在插入、刪除比較少的場景中,可以將排序的時間成本均攤到查詢上面。插入、刪除多,則復雜度升高。
3 數據量小不能體現二分的優勢。例如10個數據。無論順序查找還是二分,時間差不多。
4 數據量不能太大。因為二分依賴數組存儲數據,數組要求連續的內存。
思考題
在100M內存中,查找1000萬整數的某個數。如果用long表示整數,一個整數8個字節,1000萬整數,80M內存。可以先使用空間復雜度低的排序算法排序,之后二分查找。
求一個數的平方根,精確到小數點后6位。
二分的變體
查找第一個等于value的元素的位置
public static int findFirstEqualElement(int[] a ,int value){if(a==null) return -1;int n = a.length;int low = 0;int high = n-1;while(low<=high){int mid = low + ((high-low)>>1);if(value<a[mid]){high = mid - 1;}else if(value>a[mid]){low = mid +1;}else{if(mid==0 || a[mid-1]!=value)return mid;elsehigh = mid-1;}}return -1;}最后一個等于value的元素的位置
public static int findLastEqualElement(int[] a ,int value){if(a==null) return -1;int n = a.length;int low = 0;int high = n-1;while(low<=high){int mid = low + ((high-low)>>1);if(value<a[mid]){high = mid - 1;}else if(value>a[mid]){low = mid +1;}else{if(mid==n-1 || a[mid+1]!=value)return mid;elsehigh = mid-1;}}return -1;}查找第一個大于等于value的元素的位置
public static int findFirstMoreOrEqualElement(int[] a ,int value){if(a==null) return -1;int n = a.length;int low = 0;int high = n-1;while(low<=high){int mid = low + ((high-low)>>1);if(value<=a[mid]){if(mid==0 || a[mid-1]<value) return mid;high = mid - 1;}else if(value>a[mid]){low = mid +1;}}return -1;}查找最后一個小于等于value的元素的位置
public static int findLastLessOrEqualElement(int[] a ,int value){if(a==null) return -1;int n = a.length;int low = 0;int high = n-1;while(low<=high){int mid = low + ((high-low)>>1);if(value<a[mid]){high = mid - 1;}else if(value>=a[mid]){if(mid==n-1 || a[mid+1]>value)return mid;low = mid +1;}}return -1;}思考題
快速定位ip所在省市。可以用一個32位的int表示一個ip地址。查找每一個ip段內,最后一個起始ip小于等于目標ip的ip段,然后查找目標ip是不是在這個范圍內。
循環有序數據的二分查找怎么解決,數組是升序。
需要先找到第一個a[i]<a[i+1]a[i]<a[i+1]a[i]<a[i+1],例如數組nums={4,5,6,7,0,1,2}。i應該等于4。然后我們可以選擇將數組復制一下成為{0,1,2,4,5,6,7},但是這樣時間復雜度就會是O(n)。我們也可以這樣看nums[4]=0,nums[5]=1,nums[6]=2,nums[7]=4,nums[8]=5,nums[9]=6,nums[10]=6。相當于將nums看做是一個循環數組。
總結
- 上一篇: 用Linq转换数据
- 下一篇: openssl工具的使用以及创建私有CA