鴿巢原理
假設我們有 10 只鴿子,但只有 9 個鴿籠可以放入它們。由於我們的鴿子比鴿籠多,因此至少其中一個洞必須至少有 2 只鴿子。 這就是鴿巢原理。 每當我們要放入孔中的物品多於孔時,至少一個孔必須包含不止一件物品。
假設鴿子的數為n,鴿籠的個數為k,那么上述原理轉換下就是: 鴿巢原理
❝假設你有 k 個鴿籠和 n 只鴿子要放在里面。 如果 n > k (鴿子數 > 鴿籠數) 那么至少一個鴿舍包含至少兩只鴿子。
❞
其中,鴿子通常是數字、物體乃至一個對象,而鴿籠則是存儲數組、物體或者對象的一個容器。
證明
我們第一反應是,這不是顯而易見的么?還需要證明?
想象一下,一群鴿子被塞進了許多抽屜。 只要鴿子的數量超過抽屜的數量,至少一個抽屜會包含兩只鴿子。 請注意,即使在最平等的情況下,每個抽屜都有一只鴿子,但最后仍有剩余的鴿子需要放入其中一個已經裝滿的抽屜,從而實現原則。 如果鴿子是按概率分布的,當然有些抽屜里可能會有超過兩只鴿子。
在一般情況下,如果將 n 個對象放入 m 個容器中,則:
- 如果n < m,則有部分容器是空的
- 如果 n > m, 則有部分容器至少有兩個對象 那么轉換成公式就是:
n = (r - 1)m + 1
或者
r = [(n - 1) / m] + 1 = ROUNDUP(n / m)
其中:
❝n 為鴿子或者對象的數量 m 為鴿巢或者容器的數量 r 為容器或者鴿巢中 最多的對象或者鴿子的數量
❞
假設 n < m 並且存在從 Nm = {1, ..., m} 到 Nn = {1, ..., n} 的函數 f。 這意味着,對於每個 k∈Nm,都有一個元素 f(k)∈Nn。 此外,假設 Nn 中沒有任何元素與 Nm 中的一個以上元素相關聯。 換句話說,i,j∈Nm 和 i≠j 意味着 f(i)≠f(j)。 這恰恰意味着 f(Nm) 是 Nn 的子集,使得 m = |f(Nm)| = |Nm| ≤ |Nn| = n。 但這與我們的假設 n
應用
以leetcode第442題為例
分析:
❝n個巢, n+1只鴿子,每個鴿子進一個巢,那種總會剩下一個鴿子無家可歸; 在此問題中我們假設數字的下標為鴿巢,下標對應的值為鴿子編號。 經過一次遍歷讓鴿子(回到鴿子編號-1的巢里)回家,最終發現無家可歸的鴿子,和沒有鴿子的巢。
❞
代碼實現:
vector findDisappearedNumbers(vector& nums) {
int n=nums.size();
for(int i=0;i<n;i++)
{
while(nums[i]!=nums[nums[i]-1])
swap(nums[i],nums[nums[i]-1]);
}
vector res;
for(int i=0;i<n;i++)
{
if(nums[i]!=i+1)
res.push_back(nums[i]);
}
return res;
}
專注於高性能架構探索與開發,關注公眾號【高性能架構探索】,一起進步
