4.5 位運算(5)


套路

  • 涉及到二進制、不用單目運算符做加法運算,其中某一位的問題(兩數不同,至少有一位不同)
  • 求解運算符選擇受限時可以考慮利用邏輯與&&邏輯或||的短路特性
  • 二進制數1的個數: n = n & n - 1
  • a ^ b 不進位加法,(a & b) << 1 進位加法
  • 僅利用位去存儲少量(小于32)的數字

注意點

  • 暫無

目錄

  • 二進制中1的個數
  • 求1+2+3+...+n
  • 數組中只出現一次的數字
  • 不用加減乘除做加法
  • 撲克牌順子

二進制中1的個數

輸入一個整數,輸出該數二進制表示中1的個數。其中負數用補碼表示。

  • 解法一:將數字轉二進制字符串,再處理每一個字符
public int NumberOf1(int n) {
    int ans = 0;
    char[] arr = Integer.toBinaryString(n).toCharArray();
    for (char ch : arr) {
        if (ch == '1') ans++;
    }
    return ans;
}
  • 解法二: 將數字轉成二進制字符串,再將其中的0用""代替,返回結果字符串的長度即可
public int NumberOf1(int n) {
    return Integer.toBinaryString(n).replaceAll("0", "").length();
}
  • 最優解:位運算,n = n & n - 1 計算一次,1的個數減一
public int NumberOf1(int n) {
    int count = 0;
    while (n != 0) {
        n &= n - 1;
        count++;
    }
    return count;
}

求1+2+3+...+n

求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)。

  • 遞歸調用的出口不能用if語句,可用 n == 0 時 && (邏輯與)的短路特性來終止遞歸調用
public int Sum_Solution(int n) {
    boolean b = (n > 0) && ((n += Sum_Solution(n - 1)) > 0);
    return n;
}

數組中只出現一次的數字

題目描述
一個整型數組里除了兩個數字之外,其他的數字都出現了兩次。請寫程序找出這兩個只出現一次的數字。

  • 數組中只有出現了一次和出現了兩次的數,所以可以用異或解題。出現了兩次的數異或結果為0,整個數組異或完就是兩個只出現一次的數異或的結果。
  • 這兩個數字不同,其中必然有一位一個是1,一個是0,從剛才異或的結果中找到這一位的位置。兩數組按這一位為0和為1分成兩部分,其中相同的數那一位必然都為0或都為1,一定會被分在一組,而兩個只出現一次的數因為那一位不同,必然會被分在不同的組。兩組各自分別再進行一遍異或,即可得到兩個數。
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
    // ans 保存兩個只出現一次的數異或的結果
    // index 保存ans中不同的那一位從右往左方向的索引位置
    int ans = 0, index = 0;
    for (int i = 0; i < array.length; i++) {
        ans ^= array[i];
    }
    // == 比 & 優先級高!
    while ((ans & 1) == 0) {
        ans >>= 1;
        index++;
    }
    for (int i = 0; i < array.length; i++) {
        // == 比 & 優先級高!
       (array[i] >> index & 1) == 0 ? num1[0] ^= array[i] : num2[0] ^= array[i];
    }
}

不用加減乘除做加法

寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。

  • a ^ b 不進位加法,(a & b) << 1 進位加法,兩者之和為不進位加法結果加進位加法相加的結果,然而不能用+號得到。我們不斷的循環調用這一過程,當進位加法為0時,不進位加法的結果即為兩數之和。
public int Add(int num1,int num2) {
    do {
        int temp = num1 ^ num2;
        num2 = (num1 & num2) << 1;
        num1 = temp;
    } while (num2 != 0);
    return num1;
}

撲克牌順子

LL今天心情特別好,因為他去買了一副撲克牌,發現里面居然有2個大王,2個小王(一副牌原本是54張_)...他隨機從中抽出了5張牌,想測測自己的手氣,看看能不能抽到順子,如果抽到的話,他決定去買體育彩票,嘿嘿??!“紅心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是順子.....LL不高興了,他想了想,決定大\小 王可以看成任何數字,并且A看作1,J為11,Q為12,K為13。上面的5張牌就可以變成“1,2,3,4,5”(大小王分別看作2和4),“So Lucky!”。LL決定去買體育彩票啦。 現在,要求你使用這幅牌模擬上面的過程,然后告訴我們LL的運氣如何。為了方便起見,你可以認為大小王是0。

  • 最優解:不使用任何額外空間(一兩個變量不算啊,空間在這里指數據結構),時間復雜度 O(n),只要滿足以下兩條即可: 1. 除0以外沒有重復的數;2. 除0以外其它數的差小于5。因為數字最大值只有13,所以可以用以下這種方式保存數字,數字1左移num位保存在bitFlag里,標記出現過的數字用來判重。
public boolean isContinuous(int [] numbers) {
    if (numbers == null || numbers.length != 5) {
        return false;
    }
    int min = 14, max = -1, bitFlag = 0;
    for (int i = 0; i < 5; i++) {
        int num = numbers[i];
        if (num < 0 || num > 13) return false;
        if (num == 0) continue;
        if ((bitFlag >> num & 1) == 1) return false;
        bitFlag |= 1 << num;
        if (num < min) min = num;
        if (num > max) max = num;
        if (max - min >= 5) return false;
    }
    return true;
}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容