acwing-14
数组中唯一只出现一次的数字(困难)
在一个数组中除了一个数字只出现一次之外,其他数字都出现了三次。
请找出那个只出现一次的数字。
你可以假设满足条件的数字一定存在。
思考题:
- 如果要求只使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢?
样例
输入:[1,1,1,2,2,2,3,4,4,4]
输出:3
- 因为每个数字都只出现三次,所以倘若我们通过计算元素(以二进制表示)中每一位出现1的个数,通过模3取余得出的就是只出现一次的元素(以二进制表示)中对应位上的值,这样我们便可得出计算结果
- 因为int是32位整形数,所以总体的算法复杂度为O(32n),因为是int,所以算法复杂度是大于**O(nlogn)**的
class Solution {
public:
int findNumberAppearingOnce(vector<int>& nums) {
int cnt=0,res=0;
for(int i=31;i>=0;i--){
for(int j=0;j<nums.size();j++){
if((nums[j]&(1<<i))==1<<i)cnt++;
}
res=res*2+cnt%3;
cnt=0;
}
return res;
}
};
- 优化算法:
class Solution {
public:
int findNumberAppearingOnce(vector<int>& nums) {
int one=0,two=0;
for(int i=0;i<nums.size();i++){
one=(one^nums[i])&~two;
two=(two^nums[i])&~one;
}
return one;
}
};
-
该代码形成了一个状态机,
-
异或运算是对于每一位的异或运算,其实变相的承担了32位操作。
-
上述的操作中两个数分别为one,two(二进制表示)中每一位上的状态变化,通过两个表达式
one=(one^nums[i])&~two; two=(two^nums[i])&~one;
构建了状态机,其实只要关心第一位(one)就可以了,one每次针对异或1操作,其实和模3取余的效果是一样的
-
这样one的其中的32位均是实现了模3取余的效果,所以结果的值为one
和为S的两个数字(简单)
输入一个数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。
如果有多对数字的和等于s,输出任意一对即可。
你可以认为每组输入中都至少含有一组满足条件的输出。
样例
输入:[1,2,3,4] , sum=7
输出:[3,4]
- 通过建立哈希表的方式,判断target-nums[i]是否存在,存在得到值target-nums[i],nums[i]
class Solution {
public:
vector<int> findNumbersWithSum(vector<int>& nums, int target) {
map<int,bool> hap;
for(int i=0;i<nums.size();i++){
hap[nums[i]]=true;
if(hap[target-nums[i]]==true)return vector<int> {target-nums[i],nums[i]};
}
}
};
和为S的连续正数序列(中等)
输入一个正数s,打印出所有和为s的连续正数序列(至少含有两个数)。
例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以结果打印出3个连续序列1~5、4~6和7~8。
样例
输入:15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
- 采用双指针,i j 均从1开始
- j指针不断向后推进直至遇到i—j的连续正整数序列之和大于等于sum
- 如果结果等于于sum,并且j>i,将结果填入结果集中,如果结果大于sum,将i指针向后推进,返回上一步操作
- 直到i的值为sum,算法结束
class Solution {
public:
vector<vector<int> > findContinuousSequence(int sum) {
vector<vector<int>> res;
int i=1,j=1,s=1;
for(i=1;i<=sum;i++){
while(s<sum){s=s+(++j);}
if(s==sum && j-i>0){
vector<int> ans;
for(int k=i;k<=j;k++)ans.push_back(k);
res.push_back(ans);
}
s-=i;
}
return res;
}
};