Leetcode: 數組中兩個數的最大異或值(java代碼)


題目:給定一個非空數組,數組中元素爲 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。
找到 ai 和aj 最大的異或 (XOR) 運算結果,其中0 ≤ i, j < n 。
你能在O(n)的時間解決這個問題嗎?
示例:
輸入: [3, 10, 5, 25, 2, 8]
輸出: 28
解釋: 最大的結果是 5 ^ 25 = 28.
思路:
如果暴力破解的話,時間複雜度爲O(n*n)。剛開始以爲最大的異或值是數組中最大的數與數組中某個數異或。最後發現如果數組爲[8, 10, 2]時,結果就不對了。
首先,異或的計算方法是:將兩個數轉換爲二進制數,然後按位比較,0^0=0, 1^0=1, 0^1=1, 1^1=1.既然要求最大的異或值,我們不妨從左最高位(從第32位)開始試探,判斷最大值(我們記最大的異或值爲max)的當前位是否爲1。這樣我們每一位判斷之後,時間複雜度爲O(32*n)=O(n)。那麼如何判斷最大值的當前位是否爲1.
其實試探當前位,只需要知道數組的前綴(即二進制只保留左高位到當前位,其餘位置爲0)。當判斷最大異或值的第i位是否1,我們只要求出左高位到第i位的前綴,max的前綴=數組中某兩個數的前綴進行異或運算。例如,數組爲[8,10,2],剛開始在第31位,由於[8,10,2] 的第31到第5位都是0,當i大於4小於32時,這三個數的前綴均爲0,當判斷到第4位時,它們的前綴爲[1000, 1000, 0000], 假設max的第四位爲1時, 只要1000=數組中其中兩個數的前綴的異或即可。基於這個思路,我們定義一個mask值來用來輔助我們求出每個數的前綴。
回想我們IP數據包進行路由選擇時,通過子網掩碼和IP地址進行與運算來得到網絡號。這裏的mask值與子網掩碼有些類似:mask值爲當前位i到左高位的所有位置上的數字爲1,其它位置爲0,即11…10…0。將數組中的每個元素分別與mask進行與運算,將與運算的結果存進集合set(用來存數組中所有的數的前綴)中。 用temp爲當前試探的異或最大值(即我們先假設當前位爲1),temp=max|(1左移i),假設a,b屬於set集合,只要滿足temp=a^b,則說明temp的第i位可以爲1,將max=temp,否則,temp的第i位不可以爲1.然後進入下一個循環, 直到判斷max的最後一位停止。

代碼如下:

public static int findMaximumXOR(int[] nums) {
        int max = 0;
        int mask = 0;
        for (int i = 31; i >= 0; i--) {
            // 從最高位試着找nums的前綴
            mask = mask | (1 << i);
            HashSet<Integer> set = new HashSet<Integer>();
            for (int num : nums) {
                set.add(mask & num);
            }
            //判斷最大異或結果的當前位是否爲1
            int temp=max|(1<<i);
            for (int prefix: set){
                if (set.contains(prefix^temp)) {
                    max=temp;
                }
            }
        }
        return max;
    }

參考資料:https://leetcode.com/problems/maximum-xor-of-two-numbers-in-an-array/discuss/91049/java-on-solution-using-bit-manipulation-and-hashmap

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章