剑指 Offer 57 - II. 和为s的连续正数序列
剑指 Offer 56 - I. 数组中数字出现的次数
剑指 Offer 56 - II. 数组中数字出现的次数 II
输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
题目大意:求和为target的所有连续正整数序列。
解题思路:最简单的写法就是枚举序列首部,然后循环求该序列首部连续和。考虑优化求和步骤,求区间[l,r]的数学公式为 ( ( l+r ) * ( r-l+1 ) ) / 2。利用这个公式我们可以将区间求和的时间优化为O(1),同时还可以跳过不必要求和的区间。
代码:
function(target) {
const res = [];
const temp = [];
for(let l = 1, r = 2; l < r;) {
const sum = ((l + r) * (r - l + 1)) / 2;
if( sum === target) {
temp.length = 0;
for(let i = l; i <= r; i++) {
temp.push(i);
}
res.push(temp);
} else if(sum < taregt) {
r++;
} else {
l++;
}
}
return res;
}
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
题目大意:找出一个序列中只出现1次的两个数。
解题思路:
首先,引入位运算的一些概念:
(1)异或运算符的运算规则是,相同为0,不同为1,也就是说两个不相同的数异或结果为1的数位是不同的
(2)任何数异或自己本身的结果都是0,比如5^5 === 0
(3)任何数异或0的结果都是0
利用概念(2),我们可以将整个数组中的数异或起来,然后最后得到的就是两个只出现了一次的值的异或值。然后我们需要将这两个数区分开来,这里就需要用到概念(1),即找出异或值中任意为1的一位。然后定义两个变量res1=0,res2=0。利用概念(3),将数位为1的数位异或到res1上,为0的数位异或到res2上,最终就可以求出只出现一次的两个数。
代码:
function(nums) {
let res = 0;
for(let item of nums) {
res ^= item;
}
const flag = 1;
while(!(res & flag)) {
flag <<= 1;
}
let res1=0, res2 = 0;
for(let item of nums) {
if(item & flag) {
res1 ^= item;
} else {
res2 ^= item;
}
}
return [res1, res2];
}
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
题目大意:找出一个序列中只出现1次的那个数。
解题思路:
假设数组中所有数都出现了三次,那么统计整个数组中各个数位上1出现的次数,肯定都是3的倍数。若此时在统计过程中,添加一个只出现了一次的数,那么必定会导致该数位上1的个数不是3的倍数,那么此时该数位就是只出现了一次的那个数的数位。此方法还可以应用到求出现n次的数组中出现一次的问题中,只需要改成n的倍数即可。
代码:
function(nums) {
let res = 0;
for(let i = 0; i < 32; i++) {
const flag = 1 << i;
let count = 0;
for(const item of nums) {
if(item & flag) count ++;
}
if(count % 3) res ^= flag;
}
return res;
}