Bit Manipulation 位运算常见套路及相应LeetCode算法题

关于我的 Leetcode 题目解答,代码前往 Github:https://github.com/chenxiangcyr/leetcode-answers


Bit Manipulation(位运算):

  • &
  • |
  • 异或 ^
  • 左移 <<
  • 右移 >>
    • 正数右移,高位用 0 补,负数右移,高位用 1 补
  • 无符号右移 >>>
    • 当负数使用无符号右移时,用 0 进行补位
  • 取非 ~
    • 一元操作符

一些小技巧

  • 将数字 A 的第 k 位设置为1:A = A | (1 << (k - 1))
  • 将数字 A 的第 k 位设置为0:A = A & ~(1 << (k - 1))
  • 检测数字 A 的第 k 位:A & (1 << (k - 1)) != 0
  • Extract the lowest set bit 获取数字 A 的最低位:A & -A 或者 A & ~(A - 1)
    • 例如数字 6(110)的最低位对应 2(10)
  • 得到 111111..111: ~0

异或 ^ 运算的小技巧

Use ^ to remove even exactly same numbers and save the odd, or save the distinct bits and remove the same.
去除出现偶数次的数字,保留出现奇数次的数字。

LeetCode 题目:371. Sum of Two Integers
Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.

// Recursive
public int getSum(int a, int b) {
    return b == 0 ? a : getSum(a^b, (a&b)<<1);
}

// Iterative
public int getSum(int a, int b) {
    if (a == 0) return b;
    if (b == 0) return a;

    while (b != 0) {
        int carry = a & b;
        a = a ^ b;
        b = carry << 1;
    }
    
    return a;
}

LeetCode 题目:122. Missing Number
Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is missing from the array.

public int missingNumber(int[] nums) {
    int n = nums.length;
    
    int missing = 0;
    for(int i = 0; i <= n; i++) {
        missing = missing ^ i;
    }
    
    for(int i = 0; i < n; i++) {
        missing = missing ^ nums[i];
    }
    
    return missing;
}

| 运算的小技巧

Keep as many 1-bits as possible
尽可能多地保留 1

题目:
Find the largest power of 2 (most significant bit in binary form), which is less than or equal to the given number N.

long largest_power(long N) {
    //changing all right side bits to 1.
    N = N | (N>>1);
    N = N | (N>>2);
    N = N | (N>>4);
    N = N | (N>>8);
    N = N | (N>>16);
    return (N+1)>>1;
}

LeetCode 题目:190. Reverse Bits
Reverse bits of a given 32 bits unsigned integer.
For example, given input 43261596 (represented in binary as 00000010100101000001111010011100), return 964176192 (represented in binary as 00111001011110000010100101000000).

public int reverseBits(int n) {
    int result = 0;
    
    for(int i = 0; i < 32; i++) {
        result = result + (n & 1);
        
        n >>>= 1;   // CATCH: must do unsigned shift
        if (i < 31) // CATCH: for last digit, don't shift!
            result <<= 1;
        }
    
    return result;
}

& 运算的小技巧

Just selecting certain bits
选择特定的位

LeetCode 题目:191. Number of 1 Bits
Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also known as the Hamming weight).
For example, the 32-bit integer ’11' has binary representation 00000000000000000000000000001011, so the function should return 3.

public int hammingWeight(int n) {
    int count = 0;
    int mask = 1;
    
    for (int i = 0; i < 32; i++) {
        
        if((n & mask) != 0) {
            count++;
        }
        
        mask = mask << 1;
    }
    
    return count;
}

LeetCode 题目:477. Hamming Distance
The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
Given two integers x and y, calculate the Hamming distance.

public int hammingDistance(int x, int y) {
    
    int xor = x ^ y;
    
    return hammingWeight(xor);
}

// you need to treat n as an unsigned value
public int hammingWeight(int n) {
    int count = 0;
    int mask = 1;
    
    for (int i = 0; i < 32; i++) {
        
        if((n & mask) != 0) {
            count++;
        }
        
        mask = mask << 1;
    }
    
    return count;
}

其他位运算算法题

LeetCode 题目:169. Majority Element
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

// Hashtable 
public int majorityElement2(int[] nums) {
    Map myMap = new HashMap();
    //Hashtable myMap = new Hashtable();
    int ret=0;
    for (int num: nums) {
        if (!myMap.containsKey(num))
            myMap.put(num, 1);
        else
            myMap.put(num, myMap.get(num)+1);
        if (myMap.get(num)>nums.length/2) {
            ret = num;
            break;
        }
    }
    return ret;
}

// Moore voting algorithm
public int majorityElement3(int[] nums) {
    int count=0, ret = 0;
    for (int num: nums) {
        if (count==0)
            ret = num;
        if (num!=ret)
            count--;
        else
            count++;
    }
    return ret;
}

// Bit manipulation 
public int majorityElement(int[] nums) {
    int[] bit = new int[32];
    for (int num: nums)
        for (int i=0; i<32; i++) 
            if ((num>>(31-i) & 1) == 1)
                bit[i]++;
    int ret=0;
    for (int i=0; i<32; i++) {
        bit[i]=bit[i]>nums.length/2?1:0;
        ret += bit[i]*(1<<(31-i));
    }
    return ret;
}

LeetCode 题目:187. Repeated DNA Sequences
All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACGAATTCCG". When studying DNA, it is sometimes useful to identify repeated sequences within the DNA.
Write a function to find all the 10-letter-long sequences (substrings) that occur more than once in a DNA molecule.
For example, Given s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT",
Return: ["AAAAACCCCC", "CCCCCAAAAA"].

public List findRepeatedDnaSequences(String s) {
    Set words = new HashSet<>();
    Set doubleWords = new HashSet<>();
    
    List rv = new ArrayList<>();
    
    char[] map = new char[26];
    //map['A' - 'A'] = 0;
    map['C' - 'A'] = 1;
    map['G' - 'A'] = 2;
    map['T' - 'A'] = 3;

    for(int i = 0; i < s.length() - 9; i++) {
        int v = 0;
        
        for(int j = i; j < i + 10; j++) {
            v <<= 2;
            v |= map[s.charAt(j) - 'A'];
        }
        
        if(!words.add(v) && doubleWords.add(v)) {
            rv.add(s.substring(i, i + 10));
        }
    }
    
    return rv;
}

LeetCode 题目:136. Single Number
Given an array of integers, every element appears twice except for one. Find that single one.

public int singleNumber(int[] nums) {
    // null pointer check
    if(nums == null) {
        return 0;
    }
    
    int result = 0;
    for(int i : nums) {
        result = result ^ i;
    }
    
    return result;
}

LeetCode 题目:137. Single Number II
Given an array of integers, every element appears three times except for one, which appears exactly once. Find that single one.

public int singleNumber(int[] nums) {
    // null pointer check
    if(nums == null) {
        return 0;
    }

    /*
    每一个int都是32位(4 bytes),遍历每一个int的每一位。
    如果某一位上是1,则count++,对于出现了三次的int,则该位上count = 3。
    因此 count = count % 3可以清除出现了三次的int,保留至出现了一次的int。
    */
    
    int result = 0;
    for(int i = 0; i < 32; i++) {
        int count = 0;
        
        for(int j = 0; j < nums.length; j++) {
            if((nums[j] & 1) == 1) {
                count++;
            }
            nums[j] = nums[j]>>>1;
        }
        
        count = count % 3;
        
        if(count != 0) {
            result = (count << i) + result;
        }
    }

    return result;
}

LeetCode 题目:260. Single Number III
Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
For example:
Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].

public int[] singleNumber(int[] nums) {
    // 假设只出现一次的数字为 A,B
    
    /*
    第一步,得到所有数字的异或结果,即 A ^ B,因为其他数字都出现两次
    这个结果不能为0,因为 A != B,其实这个结果表示的是 A 和 B 之间的差别。
    假设这个结果的第 i 位为1,则说明A和B在第 i 位不同,可能一个是 1(假设是 A),另外一个是 0(假设是 B)。
    */
    int diff = 0;
    for(int num : nums) {
        diff = diff ^ num;
    }
    
    // Extract the lowest set bit 获取数字 A 的最低位
    // or diff = diff & ~(diff - 1);
    diff = diff & -diff;
    
    /*
    结合上面的理论,所有的数字可以分为两组:
        第一组包括A和其他一些数字,他们的第 i 位为1
            因此在第一组内部求异或结果,即为 A
            
        第二组包括B和其他一些数字,他们的第 i 位为0
            因此在第二组内部求异或结果,即为 B
    */
    int a = 0;
    int b = 0;
    
    for(int num : nums) {
        // 第一组
        if ((num & diff) == 0) {
            a = a ^ num;
        }
        
        // 第二组
        else {
            b = b ^ num;
        }
    }
    
    return new int[]{a, b};
}

LeetCode 题目:239. Power of Two
Given an integer, write a function to determine if it is a power of two.

public boolean isPowerOfTwo(int n) {
    /*
    2的幂都遵循如下格式:
    1
    10
    100
    1000
    ...
    */
    
    if(n < 0) return false;
    
    // 最简单的 return !(n&(n-1));

    while(n != 0) {
        if(n == 1) {
            return true;
        }
        
        if((n & 1) == 1) {
            return false;
        }
        
        n = n >>> 1;
    }
    
    return false;
}

LeetCode 题目:342. Power of Four
Given an integer (signed 32 bits), write a function to check whether it is a power of 4.

public boolean isPowerOfFour(int num) {
    /*
    4的幂都遵循如下格式:
    1
    4: 100 (2个0)
    16: 10000 (4个0)
    64: 1000000 (6个0)
    256: 100000000 (8个0)
    ...
    */
    
    if(num < 0) return false;
    
    // 0x55555555 is to get rid of those power of 2 but not power of 4
    // 最简单 return (num&(num-1)) == 0 && (num & 0x55555555) != 0;
    
    if(num == 1) return true;
    
    for(int i = 1; i * 2 < 32; i++) {
        int t = (1 << (i * 2));
        if((num ^ t) == 0) {
            return true;
        }
    }
    
    return false;
}

LeetCode 题目:338. Counting Bits
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array.

Example:
For num = 5 you should return [0,1,1,2,1,2].

class Solution {
    public int[] countBits(int num) {
        if(num < 0) return null;
        
        int[] result = new int[num + 1];
        
        result[0] = 0; // 00
        
        for(int i = 1; i <= num; i++) {
            // 对于偶数,例如6 = 2 * 3; 因此 result[6] = result[3],乘以二相当于左移,不增加1的个数
            if(i % 2 == 0) {
                result[i] = result[i / 2];
            }
            // 对于奇数,例如5 = 2 * 2 + 1; 因此 result[5] = result[2] + 1,乘以二相当于左移,不增加1的个数
            else {
                result[i] = result[i / 2] + 1;
            }
        }
        
        return result;
    }
}

更多位运算相关算法题,参见 LeetCode Bit Manipulation


引用:
a-summary-how-to-use-bit-manipulation-to-solve-problems-easily-and-efficiently

你可能感兴趣的:(Bit Manipulation 位运算常见套路及相应LeetCode算法题)