關於幾個java位運算的算法分析
問題一:
給定一個正整數N,求其二進制形式的第一個比特位1(從低位到高位的順序)。
例如,給定正整數12,其低8位二進制表示為:00001100
從低位到高位的順序,第一個1出現在第三位。
版本一:
最低位開始,針對每一位進行與(&)操作判斷是否為1,直到遇到第一個1為止。
算法實現(版本一):
public static int getFirstBit1(int N){
int mask = 1; int pos = 1; while(mask <= N){ if((N&mask) == mask){break; }else { mask <<= 1; pos++; } } return pos; }
版本二:
假定第一個1出現在第pos位,註意到tmp=(N^(N-1))+1實際上代表pos位及其右邊全部置1並且pos的左邊全部置0的那個數,求pos的值問題轉化為求tmp有多少個1的問題。
public static int getFirstBit1(int N){ int tmp = (N^(N-1))+1;//代表pos位及其右邊全部置1並且pos的左邊全部置0的那個數int pos = 0; while(tmp > 1){ tmp >>= 1; pos++; } return pos; }
問題二:
給定一正整數N,求其二進制形式中比特位為1的個數。
版本一:
依次從低位到高位校驗每一位是否為1並計數。
/*常規的算法*/ public static int getNumberOfBit1(int N){ int count = 0; int mask = 1; while(mask <= N){ System.out.println("ok"); if((N&mask) == mask){ count++; } mask <<= 1; } return count; }
版本二:
註意到(N-1)&N的結果必然會將N的最右邊的1消掉,以N=12的低8位舉例:
12:00001100
12-1:00001011
12&11:00001000
其結果就是將12的最右邊的1消掉。
按照如此規律依次循環直到N為0為至,經歷了多少次循環,代表原數N中有多少個1。
/*較為高效的算法*/ public static int getNumberOfBit1(int N){ int count = 0; while(N != 0){ count++; N = (N-1)&N; System.out.println("ok"); } return count; }
問題三:
一個無序數組裏有若幹個正整數,其中有一個整數出現了奇數次,其余整數都出現了偶數次,找出這個出現奇數次的整數。
例如,給定無序數組:
int[] ary = {2,3,6,3,4,9,4,2,6};
出現奇數次的整數為:9
分析:
異或運算滿足交換性質,比如2^3^2^3的結果和2^2^3^3的結果是一樣的。
註意到偶數次的現象,因此,可以考慮異或運算:當兩位相同時返回0,不同是返回1。
這一規律表現在:對於一個整數,重復偶數次,對其進行異或運算的結果必然為0;
更進一步,對以上現象的疊加,表現在:對多個整數,每個整數都出現偶數次,對其進行異或運算的結果也必然為0,而與其出現的順序性無關;
算法實現:
public static int find(int[] ary){ int tmp = 0; for (int i = 0; i < ary.length; i++) { tmp = tmp^ary[i];//依次進行異或運算 } return tmp; }
問題四(問題三的擴展):
一個無序數組裏有若幹個正整數,若其中有兩個整數出現了奇數次,其余整數都出現了偶數次,找出這個出現奇數次的兩個整數並返回其和。
例如,給定無序數組:
int[] ary = {2,3,6,3,4,9,4,2,1,6};
出現了奇數次的兩個整數為:9和1,返回其和10。
分析:
假設出現了奇數次的兩個整數為A和B;
一個中心思想就是將A和B分開到不同的兩個組,此時的情況回歸到問題三的情形。分別對這兩個組中的元素依次做異或運算,將會分別得到A和B。
現在的問題是怎麽來分組?
按照問題三的思路:隊原數組ary依次進行異或操作後得到的結果必然等價於A^B的結果,同時註意到A與B不相等,因此A^B的結果必不為0。
既然A^B不為零,則其二進制表示形式中必然存在1,就是由於這個1的存在,可以區分A和B,因為只有A和B在這個位的值不相同時,其異或結果的對應位才能為1。
選取A^B結果的二進制表示形式中任何一個1(比如選擇最右邊的1)作為分組條件,在遍歷ary數組時,首先進行分組判斷,分組後在各自做異或運算即可。
算法實現:
public static int find(int[] ary){ int tmp = 0,mask; int targetOne = 0,targetTwo = 0; for(int ele:ary){ tmp = tmp^ele; } int pos = getFirstBit1(tmp);//以tmp的最右邊變的1作為分組條件 mask = 1<<(pos-1);//這裏的mask實際上就是一個比特掩碼,只有pos位為1 ,其余位均為0 for (int i = 0; i < ary.length; i++) { if ((ary[i]&mask) == mask) {//說明ary[i]的pos位為1 targetOne = targetOne^ary[i]; }else {//說明ary[i]的pos位為0 targetTwo = targetTwo^ary[i]; } } return targetOne+targetTwo; }
轉載註明原文地址:https://i.cnblogs.com/EditPosts.aspx?postid=7689800
關於幾個java位運算的算法分析