算法26-----位運算

目錄:

  技巧:

應用技巧1:x&(x-1)【消除x(二進制)最後一位1】html

        • 判斷是否爲2的冪

應用技巧2:<<左移【除2】java

        • 判斷是否爲2的冪
        • 判斷是否爲4的冪

應用技巧3:>>右移【×2】結合&1【判斷最後一位是否爲1】ios

應用技巧4:子集【根據二進制來打印子集】git

應用技巧5:異或【兩個相同的數異或結果爲0】算法

題目:

      1. 如何不用額外空間交換兩個變量
      2. 不用任何比較判斷找出兩個數中較大的數
      3. 只用位運算不用算術運算實現整數的加減乘除運算
      4. 整數的二進制表達中有多少個1
      5. 在其餘數都出現偶數次的數組中找到出現奇數次的數
      6. 在其餘數都出現K次的數組中找到只出現一次的數
      7. 請設置一種加密過程,完成對明文text的加密和解密工做。
      8. 數的冪【 舉例:10^1101 = 10^0001*10^0100*10^1000】
      9. 吃糖果
      10. 分硬幣

技巧:

一、應用技巧1:與&【消除x(二進制)最後一位1】

def checkPowerof2(x): return x>0 and x&(x-1)==0

應用技巧二:判斷n是否爲4的冪:

def isFour(n): m=1 while m<n: m=m<<2 return m==n

 

 三、應用技巧3:>>右移【×2】結合&1 

題目:數組

  You are given an array A1,A2...AN. You have to tell how many pairs (i, j) exist such that 1 ≤ i < j ≤ N and Ai XOR Aj is odd.加密

  

思路:判斷列表數據中最後一位是0的個數和1的個數,二者相乘即爲結果。spa

代碼:.net

T = int(input())
for i in range(T):
    N = int(input())
    res = 0
    arr = list(map(int,input().split()))
    odd , even = 0 , 0
    for i in range(N):
        if arr[i]&1:
            odd += 1
        else:
            even += 1
    print(odd * even)

 

四、應用技巧4:子集

// Non Recursion class Solution { /** * @param S: A set of numbers. * @return: A list of lists. All valid subsets. */ public List<ArrayList<Integer>> subsets(int[] nums) { List<ArrayList<Integer>> result = new ArrayList<List<Integer>>(); int n = nums.length; Arrays.sort(nums); // 1 << n is 2^n // each subset equals to an binary integer between 0 .. 2^n - 1 // 0 -> 000 -> [] // 1 -> 001 -> [1] // 2 -> 010 -> [2] // .. // 7 -> 111 -> [1,2,3] for (int i = 0; i < (1 << n); i++) { List<Integer> subset = new ArrayList<Integer>(); for (int j = 0; j < n; j++) { // check whether the jth digit in i's binary representation is 1 if ((i & (1 << j)) != 0) { subset.add(nums[j]); } } result.add(subset); } return result; } }

 

五、應用技巧5:異或

 

java代碼:3d

public class Solution { public int singleNumber(int[] nums) { int ones = 0, twos = 0; for(int i = 0; i < nums.length; i++){ ones = (ones ^ nums[i]) & ~twos; twos = (twos ^ nums[i]) & ~ones; } return ones; } }

public class Solution { public int[] singleNumber(int[] nums) { //用於記錄,區分「兩個」數組 int diff = 0; for(int i = 0; i < nums.length; i ++) { diff ^= nums[i]; } //取最後一位1 //先介紹一下原碼,反碼和補碼 //原碼,就是其二進制表示(注意,有一位符號位) //反碼,正數的反碼就是原碼,負數的反碼是符號位不變,其他位取反 //補碼,正數的補碼就是原碼,負數的補碼是反碼+1 //在機器中都是採用補碼形式存 //diff & (-diff)就是取diff的最後一位1的位置 diff &= -diff; int[] rets = {0, 0}; for(int i = 0; i < nums.length; i ++) { //分屬兩個「不一樣」的數組 if ((nums[i] & diff) == 0) { rets[0] ^= nums[i]; } else { rets[1] ^= nums[i]; } } return rets; } }

 題目:

一、如何不用額外空間交換兩個變量:

二、不用任何比較判斷找出兩個數中較大的數

法一:獲得a-b的值的符號,若a-b是負數,則返回b,不然a。【存在溢出風險,a、b正負不一樣時,相減會溢出】

法二:

 

c = a-b,sign(c),sign(a),sign(b):表示a,b,c的正負

sign(a) == sign(b):則不會溢出,sign(c)>0則返回a,不然返回b

sign(a) != sign(b):返回sign(a)、sign(b)大的一個。

三、只用位運算不用算術運算實現整數的加減乘除運算

給定兩個32位整數a,b,可正,可負,可0,不能使用算術運算符,分別實現a和b的加減乘除運算。

加法思路:

兩個運算【異或】以及【&加左移<<】。

不考慮進位:a^b

僅考慮進位:(a&b)<<1

將上面兩個不斷相加直到沒有進位產生爲最終結果。

代碼:

def add(a,b):
    sum = a
    while b!=0:
        sum = a^b
        b = (a&b)<<1
        a = sum
    return sum
a = 19
b = 12
add(a,b)

 減法思路:a-b 實現a+(-b)便可。b取反加1(補碼)即爲-b

#加法
def add(a,b):
    sum = a
    while b!=0:
        sum = a^b
        b = (a&b)<<1
        a = sum
    return sum

#減法
#取反加1得-b
def negNum(b):
    return add(~b,1)
#a+(-b)
def minus(a,b):
    return add(a,negNum(b))
    
a = 19
b = 12
minus(a,b)

乘法運算:

代碼:

除法思路:

代碼:

四、整數的二進制表達中有多少個1

給定一個32位整數n,可爲0,可爲正,也可爲負,返回該整數二進制表達中1的個數。

簡單思路:右移判斷1的個數(循環32次)

循環次數更少的思路:1的個數次數

 法3:循環次數和法2同樣:

 

法四:平行算法:

 

五、在其餘數都出現偶數次的數組中找到出現奇數次的數

給定一個整型數組arr,其中只有一個數出現了奇數次,其餘的數都出現了偶數次i,打印這個數。

進階問題:有兩個數出現了奇數次,其餘的數都出現了偶數次,打印這兩個數。

進階思路:先將數組全部數字異或,最後結果就是兩個出現一次的數字相互異或的結果,再將這兩個數分別分在兩個數組中進行異或。

 

http://www.javashuo.com/article/p-kltdyjse-do.html

六、在其餘數都出現K次的數組中找到只出現一次的數

 給定一個整型數組arr和一個大於1的整數k。已知arr中只有1個數出現了1次,其餘的數都出現了k次,請返回只出現了1次的數。

思路:時間O(N),空間O(1)

定律:k個相同的k進制無進位相加的結果爲0.
例如: 兩個相同的2進制 100110101
100110101
無進位相加 結果 0000000

解法:把數組中的每一個元素當作k進制 , 全部元素相加後獲得的k進制結果就是 那個只出現一次的數,而後轉成相應的十進制,得解。

詳細解釋:

一個重要結論:

解該題:

代碼:

 

def onceNum(arr,k):
    eO = [0] * 32
    for i in range(len(arr)):
        setExclusiveOr(eO,arr[i],k)
    res = getNumFromKSysNum(eO,k)
    return res
#得到全部的K進制無進位加和 def setExclusiveOr(eO,value,k):
    curKSysNum = getKSysNumFromNum(value,k)
    for i in range(len(eO)):
        eO[i] = (eO[i] + curKSysNum[i]) % k
#得到K進制,res返回的是k進制 def getKSysNumFromNum(value,k):
    res = [0] * 32
    index = 0
    while value != 0:
        res[index] = value % k
        index += 1
        value = value // k
    return res

#將結果的那個K進制數轉成十進制數輸出
def getNumFromKSysNum(eO,k): res = 0 for i in range(len(eO) - 1,-1,-1): res = res * k + eO[i] return res arr = [1,1,1,2,2,2,4] k = 3 onceNum(arr,k)

 

 

七、請設置一種加密過程,完成對明文text的加密和解密工做。

 

八、數的冪

給定一個double類型的浮點數base和int類型的整數exponent。求base的exponent次方。

思路:

寫出指數的二進制表達,例如13表達爲二進制1101。 經過&1和>>1來逐位讀取1101,爲1時將該位表明的乘數累乘到最終結果。

 舉例:10^1101 = 10^0001*10^0100*10^1000

代碼:

    def Power(self, base, exponent):
        # write code here
        #return math.pow(base,exponent)
        
        if base == 0 :
            return 0
        if exponent == 0:
            return 1
        elif exponent < 0:
            n = -exponent
        else:
            n = exponent
        res = 1
        cur = base
        while n:
            if (n&1) == 1:
                res *= cur
            cur *= cur
            n = n >> 1
        return res if exponent > 0 else (1/res)

 

九、吃糖果

思路:因此的數二進制求和sum,而後判斷sum中有幾個1。

  【1 ,1,2,3,3】--->【1,1,10,11,11】---> sum(1,1,10,11,11) --->1010【兩個1,答案爲2】

  代碼:

十、今年騰訊的一道筆試題:拼湊硬幣

題目描述:小Q十分富有,擁有很是多的硬幣,小Q擁有的硬幣是有規律的,對於全部的非負整數K,小Q剛好各有兩個面值爲2^k的硬幣,全部小Q擁有的硬幣就是1,1,2,2,4,4,8,8.....小Q有一天去商店購買東西須要支付n元錢,小Q想知道有多少種方案從他擁有的硬幣中選取一些拼湊起來剛好是n元(若是兩種方案某個面值的硬幣選取的個數不同就考慮爲不同的方案)

輸入:

輸入包括一個整數n(1<=n<=10^18),表示小Q須要支付多少錢,注意n的範圍

輸出:

輸出一個整數,表示小Q能夠拼湊出n元錢的方案數

樣例輸入:6

樣例輸出:3 (例如4+2, 4+1+1, 2+2+1+1)

一個很秒的思路:https://blog.csdn.net/xiaoquantouer/article/details/78076764

將硬幣分爲兩份:1,2,4,8,16,... 和1,2,4,8,16,...

組成兩個數值爲a, b的兩個數,他們的和是a+b=n;

a在第一份中只有可能有一種組合方式(採用二進制的思想,任何一個數均可以由若干個2^i的數相加的和組成),而b = n-a也只會有一種組合形式。

將a和b使用二進制表示,舉個例子n=11,有a=101, b=110這種組合,即a = 1+0+4=5,b=0+2+4=6。可是注意到會有重複的狀況,好比111+100和101+110本質上是同一種組合方法,因此須要將二個數作二進制異或而後去重。

  代碼:

#include <iostream>
#include<set>
using namespace std;
 
int main()
{
    int n;
    while(cin>>n){
        set<int> countset;
        for(int i=1;i<=n/2;i++){
            int result =i^(n-i);
            countset.insert(result);
        }
        cout<<countset.size()<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索