使用“初中知識”實現查找重複最優算法 + 最終極限算法

 

這是園子裏討論了好長時間的題目了:
1-1000放在含有1001個元素的數組中,只有唯一的一個元素值重複,其它均只出現一次。每個數組元素只能訪問一次,設計一個算法,將它找出來;不用輔助存儲空間,能否設計一個算法實現?
發起文章:算法題,求高手. 作者: 莫貝特(MBetter) 
算法改進:利用異或的特性解決,找出重複數的問題,應該是目前最優算法。  作者:Ivony...

莫貝特給出的算法是:將所有數加起來,減去1+2+...+1000的和
Ivony給出的算法是: 
將所有的數全部異或,得到的結果與1^2^3^...^1000的結果進行異或,得到的結果就是重複數
兩位算法的時間複雜度都是: 2n (莫貝特的算法可以用高斯算法簡化)

我把題目擴展了一下,將1000換成n,讓些題更通用一些:
1-n放在含有n+1個元素的數組中,只有唯一的一個元素值重複,其它均只出現一次。每個數組元素只能訪問一次,設計一個算法,將它找出來;不用輔助存儲空間,能否設計一個算法實現?(n可能爲奇數)(注意:數組是無序的,有序數組很好解決不討論)

下面是我的算法,先給出代碼,c#語言

        /// <summary>
        
/// 查找重複的數
        
/// </summary>
        
/// <param name="ints">1..n無序序列加上1..n之間的一個數</param>
        
/// <returns>重複的數</returns>

        public static int GetRepeated(int[] array)
        
{
            
int temp = 0;
            
foreach (int i in array)
                
if (i % 2 == 1) temp += i + 1;
                
else temp -= i;
            
if (array.Length % 2 == 0) temp -= array.Length;//n爲奇數要處理一下
            return temp < 0 ? -temp : temp - 1;
        }
原理如下(n爲偶數時):
(1-2)+(3-4)+(5-6)+(7-8)+....+(n-1 - n)  等於多少?先不管,再改進一下
((1+1)-2)+((3+1)-4)+((4+1)-5)+((5+1)-6)+...+((n-1+1) - n) 等於多少?零!這個公式證明初中生都會!

這是有序序列,對無序的情況我們可以這樣理解:從數組中依次讀數,如果讀到是奇數則加上再加1,如果偶數則減去。
和代碼中的foreach對照一下吧,應該比較好理解!
函數中倒數第二行對n是奇數的情況作了些處理。

最後返回處理:
      如果重複的數是偶數,則肯定被減了,則返回它的負值-temp;
      如果重複的數是奇數,則肯定被加了,而且還多加了1, 返回 temp-1。

這個算法時間複雜度是n,空間複雜度是1,代碼行數是6,原理也簡單。

利用初中(小學奧賽估計也會)的知識解決一個算法問題,感覺算不上高手。只能感嘆我們以前的知識忘記了太多了!

----------------------------------------------------------------------------------------------------------
剛纔又看前面兩篇帖子的回覆,發現了loogn 提供的一個算法,本人修正了一下:
        public static int GetRepeated2(int[] array)
        
{
            
int temp = 0;
            
for (int i = 0; i < array.Length; i++)
                temp 
+= array[i] - i ;
            
return temp;
        }
算法原理:改進莫貝特的算法,加減加減加減...,而不是(加加加...)減(加加加...)。出現溢出的可能性也小了很多!
更是精簡,強人!!

不甘落後,我用lambda再改進一下:
        public static int GetRepeated3(int[] array)
        {
            
return array.Select((i, j) => i - j).Sum();
        }
簡單說明一下:
      方法三中的“i”相當與方法二中的“array[i]”;
      方法三中的“j”相當於方法二中的“i”

其它大可不必寫成一個函數,像下面這樣直接調用就好了,也算是最後的極限算法了。
    int repeatedNum = array.Select((i, j) => i - j).Sum();
array就是包含n+1個數的數組,repeatedNum就是其中唯一重複的數。這應該是極限了吧!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章