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<Integer, Integer> myMap = new HashMap<Integer, Integer>();
    //Hashtable<Integer, Integer> myMap = new Hashtable<Integer, Integer>();
    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<String> findRepeatedDnaSequences(String s) {
    Set<Integer> words = new HashSet<>();
    Set<Integer> doubleWords = new HashSet<>();
    
    List<String> 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,287评论 0 10
  • **2014真题Directions:Read the following text. Choose the be...
    又是夜半惊坐起阅读 9,369评论 0 23
  • (昨天群上的作业,并不善于这样题材限制的游戏规则,才思敏捷的布布第一个交了卷,又热心的跑来提醒我,看来偷懒是不成了...
    妖妖z阅读 360评论 40 33
  • 口口声声说自己喜欢文字,喜欢写作,可却并没有为此做出过多大的努力。没有付出过,就想得到回报,我是如此,很多人亦是如...
    筱苗阅读 508评论 0 0
  • 过年放假这几天懈怠了很多,简书没有更新,朋友圈也不怎么发了。除了每天和儿子呆在一起的时间久了些,想想放假这几天的收...
    18岁的童姥阅读 313评论 7 2