LeetCode--Bit Manipulation

關於位運算的總結,可參考這篇博客http://blog.csdn.net/black_ox/article/details/46411997

191. Number of 1 Bits(數字的二進制表示中1的數量)

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.

解析:

(1)方法一:n=n & (n-1)會使得n的二進制表示中最右邊的1變成0。循環判斷n!=0。有幾個1就有幾次循環。
(2)方法二:n & flag,flag初始爲1,flag & n爲1則代表n中某位爲1,flag=flag<<1(右移),循環判斷flag!=0。

C++代碼實現:

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int count = 0;
        /*
        while(n) {
            count++;
            n = n & (n-1);      //去掉1個1
        }*/
        unsigned int flag = 1;
        while(flag) {
            if(n & flag)
                count++;
            flag <<= 1;
        }
        return count;
    }
};

136. Single Number

Given an array of integers, every element appears twice except for one. Find that single one.

Note:

Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

解析:

(1)方法一:哈希表。用unordered_map記錄數字的出現次數,然後遍歷哈希表找到次數爲1的數字。空間複雜度O(n)
(2)方法二:異或。數組中每個數字都出現兩次除了一個數。我們知道,兩個相同的數進行異或操作,結果爲0。一個數和0異或,結果爲其本省。因此,對數組中的數字進行異或操作,既可以得到結果。

C++代碼實現:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        /*
        unordered_map<int,int> table;
        for(auto a : nums) 
            table[a]++;
        auto it = table.begin();
        while(it!=table.end()) {
            if(it->second==1)
                return it->first;
            it++;
        }*/
        int result = 0;
        for(auto a : nums)
            result = a ^ result;
        return result;
    }
};

461. 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.
Note:
0 ≤ x, y < 2^31.

Example:

Input: x = 1, y = 4
Output: 2
Explanation:
1 (0 0 0 1)
4 (0 1 0 0)
The above arrows point to positions where the corresponding bits are different.

解析:

海明距離指的是兩個數字的32位二進制表示中,對應位置 位不同的數量。解法是,先對兩個數字進行異或操作,num=x^y,然後判斷num二進制表示中1的數量。

C++代碼實現:

class Solution {
public:
    int hammingDistance(int x, int y) {
        int num = x ^ y;
        int count = 0;
        unsigned int flag = 1;
        while(flag) {
            if(num&flag)
                count++;
            flag<<=1;
        }
        return count;
    }
};

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.
You may assume that the array is non-empty and the majority element always exist in the array.

解析:

方法比較多。比如排序、哈希表、位操作等。《編程之美》上也提到了兩種解法。下面介紹其中一種O(n)的算法。用times記錄相同數字出現次數,用prev記錄遍歷到的數字。當遍歷到下一個數字時,如果下一個數字和prev相同,則次數加1;如果不同,則減1。如果次數爲0,需要設置prev爲該數並且重新設置times爲1。由於某個數字出現次數超過一半,那麼該數字必是最後一次把times設爲1時對應的數字prev。

C++代碼實現:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int prev = nums[0];
        int times = 1;      //記錄數字出現次數
        for(int i=1; i<nums.size(); ++i) {
            if(times==0) {              //此時前面兩個數字不同,times重新計數
                prev = nums[i];
                times = 1;
            }else if(prev == nums[i])   //如果本次數字和上次數字相同,則times+1
                times++;
            else                //如果本次數字和上次數字不同,則times-1
                times--;
        }
        return prev;
    }
};

405. Convert a Number to Hexadecimal

Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used.

Note:

All letters in hexadecimal (a-f) must be in lowercase.
The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character ‘0’; otherwise, the first character in the hexadecimal string will not be the zero character.
The given number is guaranteed to fit within the range of a 32-bit signed integer.
You must not use any method provided by the library which converts/formats the number to hex directly.

解析:

求一個數的16進製表示,基本的做法是 num%16,num/=16,循環判斷num!=0。其中num%16可以替換成num&(16-1),num/=16替換成num>>4。對於正數而言,無需預處理。對於負數,負數採用2的補數表示,但是其二進制表示是不變的。因此,無論是正負數都可以直接進行操作,處理的過程就是每次獲取最低4位,將其對應的16進製表示添加到結果字符串的開頭。關鍵的一點是,進行循環移位時,需要把數轉爲無符號數進行移位,因爲右移操作符添加的值依賴於運算對象的類型,如果是無符號數,則添加0,否則添加符號位的副本或是0,根據具體環境而定。這裏我們希望的是添加0,以便用於判斷循環結束,所以進行了類型轉換。

C++代碼實現:

class Solution {
public:
    string toHex(int num) {
        if(num==0)
            return "0";
        char dict[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        string result;
        while(num) {
            result = dict[num & 0xf] + result; //num%16等價於 num & (16-1)
            num = (unsigned)num>>4;          
        }
        return result;
    }

};

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).
Follow up:
If this function is called many times, how would you optimize it?

解析:

將一個數n的二進制進行反轉,然後求反轉後的數字。只需要取出n的第k位 a,將a左移31-k位,即將第k位反轉到了第31-k位。

C++代碼實現:

class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t result = 0;
        for(int i=0; i<32; i++) {
            result += (n&1)<<(31-i);//取第i位
            n>>=1;
        }
        return result;
    }
};

476. Number Complement

Given a positive integer, output its complement number. The complement strategy is to flip the bits of its binary representation.

Note:

The given integer is guaranteed to fit within the range of a 32-bit signed integer.
You could assume no leading zero bit in the integer’s binary representation.

C++代碼實現:

class Solution {
public:
    int findComplement(int num) {
        int result = 0;
        int k = 0;
        while(num) {
            result += (1-(num&1))<<k;
            num>>=1;
            k++;
        }
        return result;
    }
};

231. Power of Two(判斷一個數是否是2的冪)

Given an integer, write a function to determine if it is a power of two.

解析:

如果一個數是2的冪,即n=2^k,則其具有這樣的特性:n & (n-1)==0

C++代碼實現:

class Solution {
public:
    bool isPowerOfTwo(int n) {
        if(n<=0)
            return false;
        return ((n&(n-1))==0);
    }
};

342. Power of Four

Given an integer (signed 32 bits), write a function to check whether it is a power of 4.

Example:

Given num = 16, return true. Given num = 5, return false.
Follow up: Could you solve it without loops/recursion?

解析:

一個數是4的冪,則其必然是2的冪。因此,先判斷n是否是2的冪,即n&(n-1)==0表示2的冪。然後,再判斷n是否爲4的冪。當一個數爲4的冪時,其特點是,1只出現在偶數位(假設從0開始),比如1=0000 0001;4=0000 0100;16=0001 0000;64=0100 0000;由此,構造一個奇數位置全爲0、偶數位全爲1的數0x55555555,判斷n & 0x55555555 ==n,也表示其爲4的冪。(判斷是2的冪的前提下)構造一個奇數位置全爲1、偶數位全爲0的數0xaaaaaaaa,如果n&0xaaaaaaaa==0,則表示其爲4的冪。

C++代碼實現:

class Solution {
public:
    bool isPowerOfFour(int num) {
        if(num<=0)
            return false;
        if((num & (num-1))==0 && (num & 0x55555555)==num)  
        //if((num & (num-1))==0 && (num & 0xaaaaaaaa)==0)
            return true;
        return false;
    }
};

371. Sum of Two Integers

Calculate the sum of two integers a and b, but you are not allowed to use the operator + and -.

Example:

Given a = 1 and b = 2, return 3.

解析:

a+b=(a|b)+(a&b);
a-b=(a|~b)-(~x&y)

C++代碼實現:

class Solution {
public:
    int getSum(int a, int b) {
        return (a|b)+(a&b);
    }
};

268. Missing Number

Given an array containing n distinct numbers taken from 0, 1, 2, …, n, find the one that is missing from the array.

For example,

Given nums = [0, 1, 3] return 2.

Note:

Your algorithm should run in linear runtime complexity. Could you implement it using only constant extra space complexity?

解析:

(1)方法一:等差數組。total=n*(n+1)/2,然後將數組中所有數字相加成sum,數組中缺失的數爲total-sum。
(2)方法二:二分搜索
(3)方法三:異或法。原本數組中的數應爲[0,1,2,3,4,5,…],其對應的下標也爲[0,1,2,3,4,5,..],則 i^num[i]==0。現在數組中缺少某個數k,則從數k+1開始,其下標往前移了一個位置,於是可以使用異或操作,找出缺失的數,即 k=n ^ (i^num[i])^…。

C++代碼實現:
方法一

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n = nums.size();
        int total = (n*(n+1))/2;
        for(auto a:nums)
            total -= a;
        return total;
    }
};

方法三:

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n = nums.size();
        for(int i=0; i<nums.size(); i++){
            n ^= i;
            n ^= nums[i];
        }
        return n;
    }
};

389. Find the Difference

Given two strings s and t which consist of only lowercase letters.
String t is generated by random shuffling string s and then add one more letter at a random position.
Find the letter that was added in t.

Example:

Input:
s = “abcd”
t = “abcde”
Output:
e
Explanation:
‘e’ is the letter that was added.

解析:

(1)方法一:哈希表。
(2)方法二:異或操作。將s和t中所有字母進行異或操作,最後的結果即爲t中多出來的字符。

C++代碼實現:
方法二

class Solution {
public:
    char findTheDifference(string s, string t) {
        int result = 0;
        for(auto c : s)
            result ^= c;
        for(auto c : t)
            result ^= c;
        return result;
    }
};

方法一

class Solution {
public:
    char findTheDifference(string s, string t) {
        unordered_map<char,int> dict;
        for(auto c : s)
            dict[c]++;
        for(auto c : t) {
            if(dict.count(c)==0)
                return c;
            else {
                dict[c]--;
                if(dict[c]==0)
                    dict.erase(c);
            }
        }
        return 0;
    }
};

78. Subsets(數組的子集)

Given a set of distinct integers, nums, return all possible subsets.
Note: The solution set must not contain duplicate subsets.

For example,

If nums = [1,2,3], a solution is:
[ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

解析:

用0和1代表某個數字是否放入集合中,比如101代表把1和3放入集合。集合的總數爲2^n,可以用0~2^n-1表示。

C++代碼實現:

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int subset_count = pow(2,nums.size());
        vector<vector<int>> subset(subset_count,vector<int>());

        for(int i=0; i<nums.size(); ++i) {
            for(int j=0; j<subset_count; ++j) {
                if( (j>>i) & 1 ) //判斷j的第i爲是否爲1,爲1則放入j集合中。
                   subset[j].push_back(nums[i]); 
            }
        }
        return subset;
    }
};

318. Maximum Product of Word Lengths

Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.

Example 1:

Given [“abcw”, “baz”, “foo”, “bar”, “xtfn”, “abcdef”]
Return 16
The two words can be “abcw”, “xtfn”.

Example 2:

Given [“a”, “ab”, “abc”, “d”, “cd”, “bcd”, “abcd”]
Return 4
The two words can be “ab”, “cd”.

Example 3:

Given [“a”, “aa”, “aaa”, “aaaa”]
Return 0
No such pair of words.

解析:

(1)方法一:對於每個Word[i],用unordered_set記錄其中不同的字母,然後逐個對i後的每個單詞進行比對,如果兩個單詞沒有重複的字母,則記錄word[i].len*word[j].len。該方法的時間複雜度爲O(n^2)。
(2)方法二:上一方法中使用unordered_set記錄單詞不同的字母,本方法中用vector < int >dict,使用按位或的方式記錄。即dict[i] |= 1<<(c-‘a’),比如abdef的結果爲00000000000000000000000000111011。如果兩個單詞有重複的字母,則dict[i]&dict[j]結果不爲0。如果兩個單詞的dict[i]&dict[j]結果爲0,意味着兩者不存在重複單詞。

C++代碼實現:
方法二

class Solution {
public:
    int maxProduct(vector<string>& words) {
        int result = 0;
        vector<int> dict(words.size(),0);
        for(int i=0; i<words.size(); i++) {
            for(char c : words[i])
                dict[i] |= 1<<(c-'a');      //用 按位或的方式 記錄每個單詞中的不同字母
        }
        for(int i=0; i<words.size(); i++) {
            for(int j=i+1; j<words.size(); j++) {
                if( (dict[i] & dict[j])==0 && result < words[i].size()*words[j].size())
                    result = words[i].size()*words[j].size();
            }
        }
        return result;
    }
};

方法一

class Solution {
public:
    int maxProduct(vector<string>& words) {
        int result = 0;
        bool flag = true;
        for(int i=0; i<words.size(); i++) {
            unordered_set<char> dict(words[i].begin(),words[i].end());
            for(int j=i+1; j<words.size(); j++) {
                for(char c : words[j]) {
                    if(dict.count(c)){
                        flag = false;
                        break;
                    }
                }
                if(flag)
                    result = result >= words[i].size()*words[j].size() ? result : words[i].size()*words[j].size();
                flag = true;
            }
        }
        return result;
    }
};

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].

Note:

The order of the result is not important. So in the above example, [5, 3] is also correct.
Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?

解析:

(1)方法一:哈希表。用unordered_map < int, int >記錄數組中每個數字的出現次數,然後遍歷哈希表,出現次數爲1的數即爲答案。
(2)方法二:異或法。不同於數組中只有1個數字出現1次的解法。該題中需要進行兩次異或操作。第一次異或操作,先求得要找的兩個數A和B的異或結果,即mask=A^B。然後從mask中取最右邊的1,可以使用mask &= -mask。用mask對數每個數字進行 &操作,將數組分成兩部分,A和B必然會出現在不同部分,然後對這兩部分進行第二次的異或操作,結果即爲答案。

C++代碼實現:
方法二

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        int mask = 0;
        //phase 1
        for(int n : nums)
            mask ^= n;      //得到異或結果
        mask &= -mask;      //得到mask的二進制表示中最右邊的1

        int first = 0;
        int second = 0;
        //phase 2
        for(int n : nums) {
            if(n & mask)        //用mask將數組分成兩部分,然後分別從兩邊找出現一次的數字
                first ^= n;
            else
                second ^= n;
        }
        vector<int> result = {first,second};
        return result;
    }
};

393. UTF-8 Validation

A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:
For 1-byte character, the first bit is a 0, followed by its unicode code.
For n-bytes character, the first n-bits are all one’s, the n+1 bit is 0, followed by n-1 bytes with most significant 2 bits being 10.

This is how the UTF-8 encoding would work:
Char. number range | UTF-8 octet sequence
(hexadecimal) | (binary)
——————–+———————————————
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Given an array of integers representing the data, return whether it is a valid utf-8 encoding.

Note:

The input is an array of integers. Only the least significant 8 bits of each integer is used to store the data. This means each integer represents only 1 byte of data.

Example 1:

data = [197, 130, 1], which represents the octet sequence: 11000101 10000010 00000001.
Return true.
It is a valid utf-8 encoding for a 2-bytes character followed by a 1-byte character.

Example 2:

data = [235, 140, 4], which represented the octet sequence: 11101011 10001100 00000100.
Return false.
The first 3 bits are all one’s and the 4th bit is 0 means it is a 3-bytes character.
The next byte is a continuation byte which starts with 10 and that’s correct.
But the second continuation byte does not start with 10, so it is invalid.

解析:

一個UTF8字符其字節長度爲1-4bytes,並且遵循以下規則:
(1)對於1-bytes的字符,第1位爲0,後面7位緊跟Unicode代碼,比如0xxxxxxx;
(2)對於n-bytes的字符,前n位爲1,n+1位爲0,然後緊跟的n-1 個字符,這些字符開頭爲10,比如案例2。
由上面可知,
(1)字符不可能大於255;
(2)大於240,即11110xxx,代表4bytes;大於224,即1110xxxx,代表3bytes;大於192,即110xxxxx,代表2bytes;小於128,即0xxxxxxx,代表1bytes。
詳情見代碼註釋。

C++代碼實現:
第二次寫的代碼:

class Solution {
public:
    bool validUtf8(vector<int>& data) {
        int count = 0;
        for(int i=0; i<data.size(); i++) {
            count = 0;
            if(data[i] > 255)
                return false;
            else if( (data[i] & 248) == 240 )  //11110xxx -> [240,247],代表4bytes
                count = 4;
            else if( (data[i] & 240) == 224)   //1110xxxx -> [224,239],代表3bytes
                count = 3;
            else if( (data[i] & 224) == 192)   //110xxxxx -> [192,223],代表2bytes
                count = 2;
            else if( (data[i] & 128) == 0)     //0xxxxxxx -> [0,127],代表1bytes
                count = 1;
            else
                return false;

            for(int j=1; j<count; j++){  //判斷後面count-1個字符
                if(i+j>=data.size())
                    return false;
                if( (data[i+j] & 192) != 128 )    //判斷是否以10開頭,[128,191]
                    return false;
            }
            i += count-1;
        }
        return true;
    }
};

第一次寫的代碼:

class Solution {
public:
    bool validUtf8(vector<int>& data) {
        int count = 0;
        int i = 0;
        while(i<data.size()) {
            count = getCount(data[i]);
            //cout<<data[i]<<" : "<<count<<endl;
            if(count==1 || count>4)
                return false;
            if(!validFollow(data,i+1,count-1))
                return false;
            if(count==0)
                i++;
            else
                i += count;

        }
        return true;
    }
private:
    int getCount(int num) {  //判斷n-bytes character
        int count = 0;
        int k = 7;
        while( k>=0 && (num>>k)&1 ) {
            count++;
            k--;
        }
        return count;
    }
    bool validFollow(vector<int>& data,int start,int count) {   //驗證followed n-1 bytes是否是10開頭
        if(count<=0)
            return true;
        if(start >= data.size() || start+count > data.size())
            return false;
        for(int i=start; i<(start+count); ++i) {
            if(!startWithOneZero(data[i]))
                return false;
        }
        return true;
    }
    bool startWithOneZero(int num) {
        //cout<<num<<" : "<<((num>>7) & 1) << ((num>>6) & 1) <<endl;
        if( ((num>>7) & 1) && !((num>>6) & 1))
            return true;
        return false;
    }
};
發佈了31 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章