复盘二进制的习题(2)
来源:互联网 发布:c语言读入txt文本文件 编辑:程序博客网 时间:2024/06/06 03:47
338 Counting Bits。输入一个整数n,对于 0 ≤ i ≤ num,计算每个数的二进制1的个数。例如:num = 5 返回 [0,1,1,2,1,2]。希望能在O(n)的时间内完成。这里有动态规划的思想。
思路一:
观察可以发现,值为
public int[] countBits3(int num) { int[] r = new int[num+1]; int p = 1,i=0; while(p<=num){ if((p&(p-1))==0) i=0; r[p++] = 1+r[i++]; } return r;}
思路二:发现一个规律:,7=111 ,由011->111添加了1个1;6=110,由011->110,1的个数不变,但是1的位置变了。从0daonum是一个不断变换1的位置或者不断加1的过程,这样就将任意一个数i,与i/2联系起来了。
public int[] countBits(int num) { int[] r = new int[num+1]; for(int i =1;i<=num;i++){ r[i] = r[i/2]+(i&1); } return r;}
i&1,当数字为偶数就加0,如果是奇数就加1。
318 Maximum Product of Word Lengths。最大的单词长度乘积。输入一个字符串数组words,返回最大的length(words[i])*length(words[j]),
思路一:难点是判断两个字符串是否包含相同字母。可以一个个遍历找到,效率会比较低。看看用位运算能怎么做。一共26个小写字母。表示不同的字母可以用one-hot类型表示(从词向量表示方法得到的启示)。
z = 10000…000(一共25个0)
y = 01000…000(一共25个0)
…
a = 0000…001
用26位的二进制表示不同的字母。
这样一个字符串 abc = 000….111
azc = 100…..101
两个字符串的值做与操作。如果结果为0,则说明没有相同字符。
public int maxProduct(String[] words) { int n = words.length; int[] values = new int[n]; for (int i = 0; i < n; i++) { for (int j = 0; j < words[i].length(); j++) { values[i] |= (1 << (words[i].charAt(j) - 'a')); } } int maxproduct = 0; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if ((values[i] & values[j]) == 0) { if (words[i].length() * words[j].length() > maxproduct) { maxproduct = words[i].length() * words[j].length(); } } } } return maxproduct;}
137 Single Number II。给定一个数组,每个数字出现三次,只有一个数字出现了一次。找到这个数字。
思路一:以前有个题目是说每个字母出现两次,只有一个字母出现了一次。这样用异或操作,就能找到只出现了一次的字母。但是出现三次?这里方法不管用了。
计算数组中所有数字的每一bit1的个数。1=01,如果数组中有三个1,那么第0位的1就有3个,3%3=0。你肯定会问题如果数组中有三个1和3呢?那第0位的1就会有6个,6%3=0。但是如果只有2个3,那第0位的1就只有5个,5%3!=0。那3就是要找的数字了。
public int singleNumber(int[] nums) { int result = 0; for (int j = 0; j < 32; j++) { int sum = 0; for (int i = 0; i < nums.length; i++) { sum += ((nums[i] >> j) & 1); } result += ((sum % 3)<<j); } return result;}
260 Single Number III。输入数组nums,每个数字都恰好出现了两次。但是有两个数字,都只出现了一次。找到这两个数字。
思路:记录只出现了1次的两个数分别为a,b。
出现了2次的时候,找不同,肯定需要异或操作。对所有nums的元素做异或,最后得到的是r=a|b。如果r的第i位等于1,那说明a和b在这个位置是有分歧的。我们利用这一个bit就可以将nums数组分为第i位是0和第i位是1,两个部分。这个时候再做异或,就能去掉出现了两次的数,留下只有一次的数。
只留下一个数某一位为1,其他都为0,的操作是:n &=-n。
public int[] singleNumber(int[] nums) { int[] ans = new int[2]; int diff = 0; for (int num : nums) { diff ^= num; } diff &= -diff; for (int num : nums) { if ((num & diff) == 0) { ans[0] ^= num; } else { ans[1] ^= num; } } return ans;}
78 subsets
思路:求子集,就是将数组中每一个元素,或者取或者不取,组合起来。从二进制的角度来表示。例如nums=[1,2,3]。下标分别是 0,1,2。每次取一个,2个,三个就遍历完成了。
用1表示取这一位的数字。
下标: 0 1 2
组合 0 0 0 =0
1 0 0 =4
0 1 0 =2
0 0 1 =1
1 1 0 =6
1 0 1 =5
0 1 1 =3
1 1 1 =7
发现下标用0 1 表示后的值就是从0到7. 7=2^3-1。所以找到规律。
做了这么久,第一次直接找到最佳回答。
显然还可以用深度优先搜索的思路理解。
public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> result = new ArrayList<List<Integer>>(); int n = nums.length; int total = (1<<n)-1; for(int i=0;i<=total;i++){ List<Integer> list = new ArrayList<Integer>(); for(int j=0;j<n;j++){ if(((i>>j) &1)==1){ list.add(nums[j]); } } result.add(list); } return result; }
总结:这次关于二进制习题的复盘就结束了。整个做题的过程还是比较沮丧的。平时二进制操作用的比较少。前面的习题基本都是得看答案才能知道二进制该怎么用。这些操作在笔记中,用加粗符号标记出来了。当习题做到中间的时候,已经开始能想到用什么操作,能实现什么功能了。但是还不能完全做对。到了最后终于能找到感觉了。这也说明了练习,总结,才能掌握一门技巧。多次练习、总结终会学到一门技巧。解决问题过程中,对规律的观察和总结,是解题的关键。
- 复盘二进制的习题(2)
- 复盘二进制的习题(1)
- JVM习题--二进制运算
- 习题3.1二进制转为十进制
- 算法习题28:整数的二进制表示中1的个数
- 二进制加法(算导课后习题)
- OC 习题2的答案
- 快速计算整数的二进制表示法中1的个数(编程珠玑9章课后习题7)
- 习题2
- 习题2
- 习题2
- 习题2
- 习题2
- 习题2
- 习题-2
- 习题2-8,子序列的和
- 习题3-2,单词的长度
- 栈的习题(1),(2)。
- maven解释、安装、添加依赖
- 尝试在win7x64系统上查看tex格式的文档
- Android Volley完全解析(一),初识Volley的基本用法
- 若干秒后不操作自动关闭窗口
- 汇编语言——寻址方式的综合应用及转移指令的原理
- 复盘二进制的习题(2)
- Python网络通信之黏包问题(五)基于SocketServer模块和socket模块
- SUSE Linux Enterprise Server 12 (x86_64) rpm方式 安装gcc
- 基于qt和opencv的远程视频监控与播放
- 二叉树基本操作汇总练习
- 求两个数的最大公约数和最小公倍数
- 初学Nginx windos下安装
- Java 8新特性汇总
- 文件IO编程八