算法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;
}
相关文章
相关标签/搜索