前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >与移位算法相关的几道题

与移位算法相关的几道题

作者头像
山行AI
发布2019-06-28 16:31:50
4730
发布2019-06-28 16:31:50
举报
文章被收录于专栏:山行AI山行AI

1. 回顾一下位运算

1. 异或(^)

异或逻辑的关系是:当AB不同时,输出P=1;当AB相同时,输出P=0。

特性:

  • 任何一个数字异或自己都为0。
  • 任何数 与 0 进行异或操作,结果都为其本身。

2. 与操作(&)

运算规则:0&0=0;0&1=0;1&0=0;1&1=1;即:两位同时为“1”,结果才为“1”,否则为0。另,负数按补码形式参加按位与运算。

特性:

  • 如果想将一个单元清零:即使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。
  • 取一个数的指定位:取10101110的低四位,用10101110 & 00001111=00001110。该数要取的位的对应位为1,其余位为零。

3. 或操作(|)

运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1,参与运算的位,只要有一个为1,其值为1。负数按补码形式按位运算。

特性:

  • 比如将10100000 的低4位 置为1用10100000|0000 1111 = 1010 1111, 也就是将需要置为1的位的值都为1,其他位的值为0。

4. 取反操作(~)

运算规则:~1=0; ~0=1;对一个二进制数按位取反,将0变1,1变成0

特性:

  • 使一个数的最低位为0可以用 n &~1。 ~1的值为 1111111111111110,再按&运算,最低位一定为0(~运算符优先级较高)。

5. 左移运算符(<<)

二进制位全部左移若干位,左边的二进制位丢弃,右边补0。 特性:

  • 如果左移时舍弃的高位不包含1,则将一个数字n的二进制位左移2位的效果相当于,将n变成n * 2 * 2,每向左移一位,相当于该数乘以2。

6. 右移运算符(>>)

二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 注意,这里与>>>有些不同,>>>表示无符号位右移,运算符所作的是无符号的位移处理,它不会将所处理的值的最高位视为正负符号。>>>右移,左边空出的位以0填充。

特性:

  • 一个数字每右移一位,相当于该数除以2。

2. 实战题目

1. 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。你可以不使用额外空间来实现吗?

示例1: 输入:[2,2,1] 输出:1

示例2: 输入:[4,1,2,1,2] 输出:4

代码语言:javascript
复制
/**     * 异或的性质是,任何一个数字异或它自己都等于0,所以用start=0来处理     * @param nums     * @return     */    private static int getUniqueNum(int[] nums){        int start = 0;        for (int num : nums){            start ^= num;        }        return start;    }

2. 只出现一次的数字 II

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

说明:你的算法应该具有线性时间复杂度。你可以不使用额外空间来实现吗? 示例1: 输入: [ 2,2,3,2] 输出: 3

示例2: 输入: [0,1,0,1,0,1,99] 输出:99

代码语言:javascript
复制
/**     * 用两个数one=0、two=0分别记录bits位上1出现的次数,     * 把相同的数放在前面一起排列时:     * 如果一个数出现一次,则one等于这个数,two=0;     * 如果一个数出现两次,则two等于这个数, one等于0;     * 如果一个数出现第三次,则one = 0, two = 0 ,three等于这个数。     *     * @param nums     * @return     */    private static int getUniqueNum(int[] nums) {        int ones = 0;//记录二进制1只出现过1次的bits        int twos = 0;//记录二进制1只出现过两次的bits        int threes;//出现三次的        for (int num : nums) {            twos ^= ones & num;//要在更新ones前面更新twos            ones ^= num;//更新ones            threes = (ones & twos);//ones和twos中都为1即出现了3次            System.out.println("ones:" + ones + "   twos:" + twos + "  threes:" + threes);            ones &= ~threes;//抹去出现了3次的bits            twos &= ~threes;            System.out.println("清零后 ones:" + ones + "   twos:" + twos + "  threes:" + threes);        }        return ones;    }
在main方法中运行如下代码:int[] nums = {15, 15, 15, 5};System.out.println(getUniqueNum(nums));
输出结果为:ones:15   twos:0  threes:0清零后 ones:15   twos:0  threes:0ones:0(15与15取异或为0)   twos:15(先是15与15取并集为15,然后0与15取异或为15)  threes:0(0与15取并集) 清零后 ones:0   twos:15  threes:0ones:15(0与15异或为15)   twos:15(先是0与15取并集为0,然后15与0取异或为15)  threes:15(15与15取并集为15)清零后 ones:0   twos:0  threes:15ones:5(0与5取异或为5)   twos:0(先是0与5取并集为0,然后0与0取异或为0)  threes:0(0与5取并集为0)清零后 ones:5   twos:0  threes:05

运行:int[] nums = {15, 5, 15, 15};System.out.println(getUniqueNum(nums));
输出:ones:15   twos:0  threes:0清零后 ones:15   twos:0  threes:0ones:10(15与5取异或为10)   twos:5(15与5取并集为5,0与5取异或为5)  threes:0(10与5取并集为0)清零后 ones:10   twos:5  threes:0ones:5   twos:15  threes:5清零后 ones:0   twos:10  threes:5ones:15   twos:10  threes:10清零后 ones:5   twos:0  threes:105

这道题只能自己慢慢琢磨。找了几遍博客,对这点说的都不是十分清楚。

3. 只出现一次的数字 III

给定一个整数数组nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。找出只出现一次的那两个元素。

示例: 输入:[1,2,1,3,2,5]

输出: [3,5]

注意: 1.结果输出的顺序并不重要,对于上面的例子,[5,3]也是正确答案。 2.你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

代码语言:javascript
复制
    /**     * 在这里把所有元素都异或,那么得到的结果就是那两个只出现一次的元素异或的结果。     * 然后,因为这两个只出现一次的元素一定是不相同的,所以这两个元素的二进制形式肯定至少有某一位是不同的,即一个为 0 ,另一个为 1 ,现在需要找到这一位。     * 根据异或的性质 任何一个数字异或它自己都等于 0 ,得到这个数字二进制形式中任意一个为 1 的位都是我们要找的那一位。     * 再然后,以这一位是 1 还是 0 为标准,将数组的 n 个元素分成两部分。     *   1. 将这一位为 0 的所有元素做异或,得出的数就是只出现一次的数中的一个     *   2. 将这一位为 1 的所有元素做异或,得出的数就是只出现一次的数中的另一个。     *   这样就解出题目。忽略寻找不同位的过程,总共遍历数组两次,时间复杂度为O(n)。     *     * @param nums     * @return     */    private static int[] getUniqueNum(int[] nums) {        int diff = 0;        for (int num: nums){            diff ^= num;        }        // 得到最低的有效位,即两个数不同的那一位        diff &= -diff;        int[] result = new int[2];        for (int num:nums){            if ((num & diff) == 0){                result[0] ^= num;            }else {                result[1] ^= num;            }        }        return result;    }

4. 判断一个数是否为2的幂,比如1=2^0,2=2^1,4=2^2

利用与运算中,2&1=0,4&3=0,利用2的幂-1与2的幂的与运算结果为0,2的幂的二进制形式一定只有一位为1,其他为0,而他的减一形式一定是这一位为0,其他为1.

代码语言:javascript
复制
boolean istwores(int n){     return n&(n-1)==0?True:False;}

5. 小技巧

  • 因为位运算比乘法运算快,所以可将x = x * 2 优化为x = x << 1
  • 利用左移运算符计算 a 的第 b 个二进制位是什么:a & (1 << b)
  • 不适用临时变量交换a、b两个数:
代码语言:javascript
复制
a = a ^ b;b = a ^ b;a = a ^ b;
  • 判断一个32位整数是不是4的幂,首先判断是不是power of two,如果不是,那么这个数肯定不是4的幂,否则的话和0x55555555做按位与运算,如果得到的结果和原来的数相同,这个数就是一个4的幂。这样做的原理是,0x55555555是一个32位的整形数字,并且在它的二进制表示中,奇数位全部都是1,而powers of 4的二进制中只有一个1存在,并且这个1仅仅存在于奇数位上。所以如果number是power of 2,并且(number & 0x55555555) == number,那么number一定是一个power of 4。
代码语言:javascript
复制
 if(number <= 0)return false;    if(number & (number - 1))return false;    //这里一定要注意,== 的优先级大于 &,所以如果不加括号的话,结果会变成return number & (0x55555555 == number);    return (number & 0x55555555) == number;

参考:

  • https://mp.weixin.qq.com/s?_biz=MzIzMTI3MDAzOQ==&mid=2247483702&idx=1&sn=c5e6986a0c933cb44a4556302c0b54a4&chksm=e8a7f2cedfd07bd84a4770898f9c7a670efd6cb17c475c3216fa08505c8fcb198c9e21448b9c&mpshare=1&scene=1&srcid=0610uJyHqAx04myXmSDhLzAT&key=59b86e7114b344c351c4b191b8d2f09a3ac72d025f6350b288754d08fbe8d6bad8a1ea56839fd8a89ea476fbcd7d59080a4e64707c8f75ccd82b6c7bd9ec5a33d64867cadf4fcf82a174f535febdc0b3&ascene=1&uin=MjY1ODQ5Mzk2Mw%3D%3D&devicetype=Windows+10&version=62060739&lang=zhCN&pass_ticket=aNVAIZpVP8%2FFGutx88v3gIv5rJw8x%2F0n8wZ%2Fyzqqmt9PbSwH0T9a4%2Bmdc4iUP0ZZ
  • https://blog.csdn.net/qq_34229391/article/details/82430837
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 回顾一下位运算
    • 1. 异或(^)
      • 2. 与操作(&)
        • 3. 或操作(|)
          • 4. 取反操作(~)
            • 5. 左移运算符(<<)
              • 6. 右移运算符(>>)
              • 2. 实战题目
                • 1. 只出现一次的数字
                  • 2. 只出现一次的数字 II
                    • 3. 只出现一次的数字 III
                      • 4. 判断一个数是否为2的幂,比如1=2^0,2=2^1,4=2^2
                        • 5. 小技巧
                        相关产品与服务
                        批量计算
                        批量计算(BatchCompute,Batch)是为有大数据计算业务的企业、科研单位等提供高性价比且易用的计算服务。批量计算 Batch 可以根据用户提供的批处理规模,智能地管理作业和调动其所需的最佳资源。有了 Batch 的帮助,您可以将精力集中在如何分析和处理数据结果上。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档