二分查詢針對乙個有序的資料集合,每次通過和區間的中間元素對比,將待查區間縮小為之前的一半,知道查到要查詢的元素,或者區間縮小為0。
查詢的時間複雜度:o(logn)
待查區間是:n,n/2,n/4,n/8,...,n/2^k。其中當n/2^k=1時,查詢了k = log2n次,並且每次查詢只涉及兩個資料的大小比較操作,所以時間複雜度是o(logn)
如何寫乙個正確無誤的二分查詢:
二分法的寫法有很多種,以為你low和high的初始化會影響後面整個二分停止條件,以及mid的賦值,low和high的更新,所以我的寫法以下標為準
int low = 0,high = n-1;
要注意三個容易出錯的地方:
注意是 low<=high,而不是 low
mid = low + (high - low ) /2 防止當low和high都很大的時候,直接相加除以2的方法時的溢位。另外如果想繼續優化效能,mid = low+((high-low)>>1),注意後面要加個括號,因為優先順序的緣故。
low = mid+1;
high = mid-1;
這裡很好理解,因為二分的核心是拿中間的數取和low,high下標對應的數取比較,如果比較完成了之後,肯定要拋去中間這個數了。(對於二分的變形問題不適用,最好的方法就是解決不同變形問題時候畫圖想想)
二分法應用場景的侷限性
如果你想利用鍊錶來實現,鍊錶的隨機訪問時間複雜度很高,這樣會導致二分查詢的時間複雜度變高。
如果是一組無序的靜態資料,沒有頻繁的插入,刪除,我們進行一次排序多次查詢,這樣排序的時間複雜度被均攤,二分查詢效率就會很高。相反,如果資料集涉及到頻繁的插入和刪除,要想用二分查詢,同時還要呼叫多次排序來維護有序性,這樣的時間複雜度就很高。
不過也有例外,當資料之間的比較非常耗時時,不管資料量大小,都推薦使用二分查詢從而來降低比較的次數。
前面提到,二分法依賴順序表結構,就是要開闢一塊連續的記憶體。如果申請不到一塊很大的記憶體空間就沒法來儲存這些資料。
思考題:
如何在 1000 萬個整數中快速查詢某個整數?記憶體限制是100mb。
每個資料大小是8b,直接將資料存在陣列中,消耗記憶體80mb。對1000萬個資料從小到大排序,然後利用二分查詢,可以快速查詢到想要的資料。
至於為什麼要排序,因為如果要多次查詢呢?如果不排序,每次都要o(n)的時間複雜度,但是排序了之後利用二分查詢大大降低了需要的時間。並且,大部分情況下二分查詢可以解決的問題,用雜湊表和二叉樹都可以解決,但是會需要比較多的額外空間。所以100mb儲存不下。陣列除了儲存資料本身之外,不需要額外儲存其他資訊,是最省空間的儲存方式。
課後思考:
假設鍊錶長度為n,第一次查詢的區域為[0,n/2),指標需要移動n/2次;第二次需要移動n/4次;直到第k次需要移動1次。總共指標移動次數(查詢次數) = n/2 + n/4 + n/8 + ...+ 1。sum = n -1。時間複雜度為o(n)。
二分查詢的四個變形問題:
int search(int *a, int n, int value)
else if (a[mid]< value)
else
} return -1;
}
int search(int *a, int n, int value)
else if (a[mid]< value)
else
} return -1;
}
int search(int *a, int n, int value)
else
} return -1;
}
int search(int *a, int n, int value)
else
} return -1;
}
[202.102.133.0, 202.102.133.255] 山東東營市
[202.102.135.0, 202.102.136.255] 山東煙台
[202.102.156.34, 202.102.157.255] 山東青島
[202.102.48.0, 202.102.48.255] 江蘇宿遷
[202.102.49.15, 202.102.51.251] 江蘇泰州
[202.102.56.0, 202.102.56.255] 江蘇連雲港
將所有的ip區間的起始位址進行排序,然後根據二分查詢,找到最後乙個小於等於目標ip的起始ip,然後在這個區間內查詢,如果在,我們就取出對應的歸屬地顯示;如果不在,就返回未查找到。
總結:
凡是用二分查詢能解決的,絕大部分我們更傾向於用雜湊表或者二叉查詢樹。即便是二分查詢在記憶體使用上更節省,但是畢竟記憶體如此緊缺的情況並不多。那二分查詢真的沒什麼用處了嗎?實際上,上一節講的求「值等於給定值」的二分查詢確實不怎麼會被用到,二分查詢更適合用在「近似」查詢問題,在這類問題上,二分查詢的優勢更加明顯。比如今天講的這幾種變體問題,用其他資料結構,比如雜湊表、二叉樹,就比較難實現了。
課後思考:如果有序陣列是乙個迴圈有序陣列,比如 4,5,6,1,2,3。針對這種情況,如何實現乙個求「值等於給定值」的二分查詢演算法呢?
先找到分界下標,然後在兩個區間做二分查詢。
迭代二分查詢二分查詢
在寫這篇文章之前,已經寫過了幾篇關於改迭代二分查詢主題的文章,想要了解的朋友可以去翻一下之前的文章 bentley在他的著作 writing correct programs 中寫道,90 的計算機專家不能在2小時內寫出完整確正的二分搜尋演算法。難怪有人說,二分查詢道理單簡,甚至小學生都能明確。不過...
1128 二分 二分查詢
時間限制 10000ms 單點時限 1000ms 記憶體限制 256mb 描述nettle最近在玩 艦 因此nettle收集了很多很多的船 這裡我們假設nettle氪了很多金,開了無數個船位 去除掉重複的船之後,還剩下n 1 n 1,000,000 種不同的船。每一艘船有乙個稀有值,任意兩艘船的稀有...
二分查詢及變種二分查詢
二分查詢也稱折半查詢 binary search 它的查詢效率很好。二分查詢有乙個要求是必須採用順序儲存結構,而且表種的元素是有序的。只有滿足這個條件我們才能使用二分查詢。查詢條件 查詢區域的左邊界,小於等於查詢區域的右邊界 查詢過程 1.迴圈條件 查詢條件 2.計算序列中間下標位置 3.如果待查詢...