狸猫的笔试——集合

来源:互联网 发布:个推公司怎么样知乎 编辑:程序博客网 时间:2024/05/16 18:31

1.计算1+2+3+……+n。不使用乘法。

分析:这道题有三种思路。
1.用for循环从一加到n。虽然简单,但是时间最长。
2.根据等差数列公式有 (1+n)*n/2 = sum,化简得 (n + n^2) >> 1 = sum.虽然最简单,时间也短,但是使用了次方。
3.依然基于等差数列求和。
例如。1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 +9  首尾相加有
1 + 9 = 2 + 8 = 3 + 7 = 4 + 6 
所以,为了减少计算 可以将 1 + 9 的结果累加四次。变成10 + 10 + 10 +10 + 5。加的次数减少到5次。
上式相加的过程中 10+10 = 20 这一步计算了两次  可以将式子 写成 20 + 20 + 5。加的次数减少到4次。
当n足够大时,可以有效的减少加法计算次数。

2.写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

分析:不使用四则运算,那只能用次方,开方。次方和开方都能完成两数相加。只能使用二进制中逻辑运算符了。
观察十进制中 5 + 7
先将 5 + 7 进行计算,不管进位。 个位是2. 然后将5 + 7 相加,只管进位,进位为1.这个1需要移动到十位上。变成10。
然后将10和02 相加,不管进位,得到12.然后计算 10 + 02,只管进位,进位为0.结束计算。
在这个过程中,持续的先计算本位,然后计算进位。进位向左移一位,然后与本位相加。如此循环,直到进位为0.
由此可推导出二进制的计算。
先计算本位 5 + 7 = 101 + 111 =  010 (这实际上是 101 & 111),
在计算进位 5 + 7 = 101 + 111 = 101 (只有第一位 和第三位产生了进位,且进位为1)(这实际上是101 ^111)
在将进位左移101 << 1 = 1010.
将进位与本位相加,计算新本位 1010 + 010 = 1000
计算进位 1010  + 010 = 0010
将进位左移一位 0010 << 1 = 100
将进位与本位相加,计算新本位 1000 + 100 = 1100
计算进位 1000 + 100 = 0000
此时进位为0.停止计算
    public int Add(int num1,int num2) {       while(num2 != 0){        int temp = num1 ^ num2;        num2 = (num1 & num2) << 1;        num1 = temp;        }        return num1;    }

3.一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

分析:如果采用标记数组,然后循环一次。再循环一次标记数组,找出出现一次的。时间为O(2n),空间是O(n).遍历一次原数组是必须的,否则不能确定哪个数只出现了一次。此外,两个重复的数之间可能间隔很长,因此必须记录下这个数,才能在后续遍历中除去这个数。但是,一定要记录在数组里吗。我们知道,如果两个数相同,那它的二进制一定相同,即a^a = 0.因此,只需要在遍历原数组时,不停的求  num ^= array[i].设两个数为a,b。则最终 num = a^b. 因为ab不相同,那它们的二进制一定不同,因此,找到a与b二进制不同位的最右侧未(必然是num二进制中,最右侧的1),设为倒数第三位 1000.然后根据这个,可以将原数组分割成倒数第三位为1的 和倒数第三位不为1 的。相同的两个数必然分到同一组,a和b因为必有一个第三位不为1,因此 ab被分到两个组。
//num1,num2分别为长度为1的数组。传出参数//将num1[0],num2[0]设置为返回结果public class Solution {    public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {      if(null == array || 1 > array.length){return;}int x = 0;for (int m : array) {x ^= m;}x &= -x;num1[0] = num2[0] = 0;for (int m : array) {if ((x & m) != 0) {num1[0] ^= m;} else {num2[0] ^= m;}}    }}




0 0
原创粉丝点击