复盘二进制的习题(1)
本文是對近期二進制專題的leetcde習題的復盤。文中的解決思路來源于leetcode的討論,以及一些網頁。
342 判斷一個整數(32bits)是否是4的次冪。
寫出4i,i=0,1,2,3,4...的二進制表示,查找規律。會發現這些數的特征是 a 都>0;b 只有一位是1,代碼:n&(n-1)==0;c 1都在奇數位置,如果從1開始數,代碼:n&(0x55555555)!=0。
191 數數一個無符號整數n的二進制表示中有幾個1。
方法一:每次判斷最末位置是否為1:n&1==1=>最末位是1;接著n=n>>1,繼續判斷。
方法二:每次判斷n的某一個位置,從最末位開始。n&1!=0=>該位是1;接著計算(n&(1<<1)),再繼續n&(1<<2),如此下午,判斷32個位置。方法一和二思路是相同的,都是通過位移來判斷不同的位置,都需要32次循環,只是一個移動數字,一個移動掩碼mask。
方法三:如果數字n的二進制位只有1位是1,循環32次太浪費了。n&(n-1) 實際上是把一個數的最低位的有效1,給去掉了;而且一次還只能去掉一個位置上的1。這樣只要n=(n&(n-1)),n不等于0,就知道1的位數是加1的。
思考:如果是有符號的呢?
136 Single Number。給定的數組中,每個數都出現了兩次,只有一個數出現了一次。找到只出現了一次的數。
方法:兩個相同的數異或之后會是0。所有數字異或之后加和,留下的數就是要找的數。
461 Hamming Distance。海明距離是計算兩個int,都多少個位是不同的。
方法:任意兩個數異或之后不同的位會變為1。步驟是:n=(i^j);數n有幾個1,等同于191。
169 Majority Element. 主元素是指在數組中出現次數超過n/2的元素。前提假設:數組不為空;主元素總數存在。(這題做過,怎么一點印象都沒有)
方法1:循環計算nums[i]出現次數。時間復雜度O(n2)。
方法2:用hashmap,保存每個數字出現次數。時間復雜度O(n),空間復雜度O(n)。
方法3:先排序,再找到最長的連續串。時間復雜度O(nlogn)。
方法4:分治法,數組一分為2,查找第一部分的主元素A,第二部分的主元素B。如果A的次數=B的次數,A和B都是候選主元素。如果不同,則選出主元素。時間復雜度O(nlogn)。
方法5:隨機選元素(不甚理解)。平均時間復雜度O(n),最壞是無窮。隨機啊。。。。
方法6:Moore Voting algorithm。定義候選元素element和次數count。不斷遍歷nums,如果count=0,element=當前元素,count=1;如果count>0,當前元素=element則count++;否則count–。留下的element一定是主元素。時間復雜度O(n)。這個想法太奇妙了。有點像雙方交戰,主元素是一方,其他元素是一方。不斷增加或者減少count。
方法7:位運算。直接上代碼吧。有點不會描述了。一個int,32位。哪個位置上1的個數>n/2,那這一位一定是主元素貢獻的。所以只要找到1出現次數超過n/2的位,其他位置為0,就可以找到主元素?!?/p>
public int majorityElement(int[] nums) {int[] bit = new int[32];for (int i = 0; i < nums.length; i++) {for (int j = 0; j < 32; j++) {bit[j] += (nums[i] >> j) & 1;}}int majority = 0;for (int j = 0; j < 32; j++) {bit[j] = bit[j] > (nums.length / 2)? 1 : 0;majority += bit[j] << j;}return majority;}
405 將一個整數用十六進制表示
方法:做十進制與十六進制基礎元素的映射。做映射有兩種方式,一種是map,一種是數組。當key是數字的時候用數組還是比較方便的。nums[]={0,1,2,….a,b,…f}。數組的下標正好是十六進制對應的十進制。
接著十六進制可以用4位二進制表示。每次將num與0x0f做與操作,這樣就能只留下最后四位了。
190 給定一個無符號的整數num,輸出一個整數out,這個整數是輸入數字的二進制的逆轉。如果方法要多次調用,可以怎么優化。
方法一:變量r為最后結果,從num低位開始,每次處理1位,r<<1+最低位處理的值。循環32次。
多次調用優化的方法應該是存儲字節與結果的映射關系。
方法二:可以先看代碼。來自網頁。每4位當做一個整體處理,int32位分為8個部分:abcdefgh。處理順序是:abcdefgh -> efghabcd -> ghefcdab -> hgfedcba。代碼最后兩行是處理:4位內部的倒序。
這思路看到后真是震驚。原來分治法還能這么玩。膜拜啊。
476 Number Complement。一個int的補是指二進制取反。但是高位的0不變。例如5的補是2。因為 5=101 ,取反,010=2。但是一個int有32位,對于5,從第4位開始的0都不算。所以需要知道最高位的1,在哪個位置即可。
下面是兩種解法。
401 Binary Watch。二進制手表。好酷的手表。這是一個窮舉搜索的問題。
時:8 4 2 1 。每一個選與不選分別用1和0 表示。這樣就形成了一個一個的二進制數。如果說時鐘只有一個燈亮,那么選擇可能有:
時:8 4 2 1
f1: 0 0 0 1
f2: 0 0 1 0
f3: 0 1 0 0
f4: 1 0 0 0
231 判斷一個數是否是2的平方。2的平方的特征是:a 大于0;2 只有一個1。
public boolean isPowerOfTwo(int n) {return n>0 && (n&(n-1))==0;}389 Find the Difference。輸入兩個字符串s、t。t是s中所有字母隨機排列后組成的字符串,但是t中有一個字符在s中沒有出現。找出在t中沒有出現的那個字符。
思路一:用map。遍歷s中每個字符的出現次數,保存在map中。接著遍歷t中的字符串,將map[key]value值減1。如果在map的key值不存在或者說map[key]的value <=0 ,那這個字符一定不在s中出現。
思路2:位操作。用異或可以將相同的字符抵消。s和t兩個字符串做異或,留下的就是沒在s中出現的字符。
268 Missing Number。給一個包含n個不同數值的數組nums,數組本應該是0,1,2,…n這樣的數字,但是丟了一個數字。返回丟失的數字。例如輸入 nums = [0, 1, 3] 返回2。
思路:這個題目與上一提很相似。389是查找多出的字符,268是查找丟失的數字。都是從兩個可比較的對象中,查找出多的或者少的部分。
371 Sum of Two Integers。不用+ - 操作求兩個數的和。這個解法就是比較神奇了。處理兩個數相同的部分;處理兩個數相異的部分。兩個數相同的部分加和的時候還要向前進一,和我們手動計算和相似。
public int getSum(int a, int b) {return b == 0 ? a : getSum(a ^ b, (a & b) << 1); }參考資料
1 bit-manipulation
2 網址
3 網址
可能會有落下的網址,未列出。謝謝作者們。
總結
以上是生活随笔為你收集整理的复盘二进制的习题(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android通用流行框架大全
- 下一篇: MOSSE跟踪算法源码解析