一、两数之和
题目:
/** * 不使用运算符 + 和 - ,计算两整数 a 、b 之和。 * * 示例 1: * 输入: a = 1, b = 2 * 输出: 3 * * 示例 2: * 输入: a = -2, b = 3 * 输出: 1 */
这道题的本意其实就是在考你的位运算,如何利用位运算来进行数的加减
可以分为两步,就像我们小学做算数运算一样,先把每位相加不考虑进位,然后再算一遍进位
总体思路:两个整数a, b; a ^ b是无进位的相加; a&b得到每一位的进位;让无进位相加的结果与进位不断的异或, 直到进位为0
public int getSum(int a, int b) {
int sum, carry;
sum = a ^ b;
//异或这里可看做是相加但是不显现进位,比如5 ^ 3
/*0 1 0 1
0 0 1 1
------------
0 1 1 0
上面的如果看成传统的加法,不就是1+1=2,进1得0,但是这里没有显示进位出来,仅是相加,0+1或者是1+0都不用进位*/
carry = (a & b) << 1;
//相与为了让进位显现出来,比如5 & 3
/* 0 1 0 1
0 0 1 1
------------
0 0 0 1
上面的最低位1和1相与得1,而在二进制加法中,这里1+1也应该是要进位的,所以刚好吻合,但是这个进位1应该要再往前一位,所以左移一位*/
if(carry != 0) //经过上面这两步,如果进位不等于0,那么就是说还要把进位给加上去,所以用了尾递归,一直递归到进位是0。
{
return getSum(sum, carry);
}
return sum;
}
把上述代码整理浓缩
public int method2(int a, int b) {
return b == 0 ? a : getSum(a^b,(a&b)<<1);
}
二、第一个不重复的字符
题目:
/** * 给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。 * * 案例: * s = "leetcode" * 返回 0. * s = "loveleetcode", * 返回 2. * 注意事项:您可以假定该字符串只包含小写字母。 */
之前有做过类似的题,这些题的思路都很相近,统计出每个字符串中每个字母出现的次数,然后再根据题目做操作。最笨的方法是使用Map,效率高的方法便是使用字符转换成ASCII码当数组下标来存。
public int firstUniqChar(String s) {
int[] m = new int[26];
for (int i = 0; i < s.length(); ++i)
m[s.charAt(i)-'a']++;
char[] chars=s.toCharArray();
for (int i=0;i<chars.length;i++){
if (m[chars[i]-'a']==1){
return i;
}
}
return -1;
}
三、第N个数字
题目:
/** * 在无限的整数序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ...中找到第 n 个数字。 * * 注意: * n 是正数且在32为整形范围内 ( n < 231)。 * * 示例 1: * 输入: * 3 * 输出: * 3 * * 示例 2: * 输入: * 11 * 输出: * 0 * * 说明: * 第11个数字在序列 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... 里是0,它是10的一部分。 */
题目说的很模糊,一时间很难理解,大致的意思就是把N个数串在一起,比如10个数的组合就是“12345678910”,这样的一个字符串,找到它的第十个数字就是1,第十一个就是0。
对于这种无限序列的或者数字很大的题目来说肯定是不可以用暴力的,肯定会超出时间限制的。那你就要想办法找出一些数学规律来解题。我们从数字的位数来发现规律,一位数是1-9有9个,两位是10-99有90个,三位数是100-999有900个,那么规律找到了,我们可否利用它来解题呢?
给定一个n,先找所在的区间(如1-9,10-99...),然后求出它是所在的区间的第多少个数字,再算出是这个数字的第几位。找区间按照公式n=n-9*1-90*2-900*3...-9*10^(i-1)*i, 求出的i指它在i位数的区间内,n为该区间第n个数字,假设公式得到的n=4,i=2,那么说明是两位数,4/i=2,说明是第2个数字,即11,4%i=0,说明是第二个数字的最后一位,即1。 再假设共识得到n=25,i=3,说明是三位数,25/3=8,25%3=1,说明三位数中第8+1个数字,是这个数字的第1位,即是108中的1。
public int method1(int n) {
long len = 1, base = 1;
long m = n;
while (m > 9 * base * len) {
m -= 9 * base * len;
len++;
base *= 10;
}
int curNum = (int) (base + (m - 1) / len);
return ((curNum + "").charAt((int) ((m - 1) % len)) - '0');
}
四、读表
题目:
/** * 二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)。 * 每个 LED 代表一个 0 或 1,最低位在右侧。 * * 二进制手表顶部有 4 个 LED 代表小时(0-11),底部的 6 个 LED 代表分钟(0-59)。 * 每个 LED 代表一个 0 或 1,最低位在右侧。 */
这个手表不错!和之前的一道题有异曲同工之妙,那道题是给定数字去组出时间,这道题只是拐了一个弯,但也为我们提供了方便。
给定亮的灯数去找所有可能的时间,这里要注意的问题就是边界,12:00不行,0:60也不行,还要注意格式问题
我们只需要去遍历所有可能的时间,共需要循环12*60,以计算机的速度来说很快,计算机很擅长这种重复的计算,然后挑出亮的等数是指定数量的就可以了,判断的方式很多种,但最好使用位运算,因为快。
一个重要的方法Integer.bitCount()该方法可以返回数值转换为二进制中1的数量。
public static List<String> readBinaryWatch(int num) {
List<String> ret = new ArrayList<String>();
for(int h = 0;h<12;h++){
for(int m = 0;m<60;m++){
if(Integer.bitCount(h<<6|m)==num){
ret.add(h+(m<10?":0":":")+m);
}
}
}
return ret;
}