目录
只出现一次的数字|
只出现一次的数字||
只出现一次的数字|||
杨辉三角(vector>的理解)
删除排序数组中的重复项
删除排序数组中的重复项||
数组中出现次数超过一半的数字
只出现一次的数字|
. - 力扣(LeetCode)
思路:
我们都知道,对于二进制的异中,相同的两个数异或为 0, 不同的两个数异或为 1,即,相同为 0 ,相异为 1。而 0 异或任何数都是该数本身。
举例:
假设 a = 2, b = 2; a^b = 0;
假设 a = 0, b = 2; a^b = 1;
要在数组中找只出现一次的数字,其它数字出现了两次并且是相同的,我们就可以把所有的数异或起来,相同的数异或会抵消成了 0,而 0 异或任何数都是 0,最后就只剩下那个出现一次的数。
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int val = 0;
for(auto e : nums)
{
val ^=e;
}
return val;
}
};
只出现一次的数字||
. - 力扣(LeetCode)
思路:
在很多实际应用场景中,32 位整数已经足够满足大部分的需求。很多常见的数据和计算任务并不需要用到 64 位的整数范围。
① 在二进制中,把每个数的每一位都相加起来,为什么相加呢 ?题目中所说,三个数相同,另外一个异类,也就是 4个数的每一位相加,如果取模 3 正好是 1,说明这一位就是 1。
当时7个数(3相同+3相同+1异类) 的时候也是同理。
例如: 2 2 3 2
0 0 1 0
0 0 1 0
0 0 1 1
+ 0 0 1 0
---------------------------------------
0 0 4 1
1 和 4 取模 3 等于1,说明该位的值为 1 。
② 得到了该位为 1 ,然后在按位或 |,这里不能使用按位与&,
假设当前
ans
是0110
(假设这是前面部分位的计算结果),现在处理第二位(从右往左数),且第二位的累计异或结果为 1,表示只出现一次的那个数在第二位是 1。如果使用 “|”(按位或):
执行ans |= (1 << 1)
,1 << 1
得到0010
,按位或操作后结果为0110 | 0010 = 0110
,因为该位原本就是 1,所以结果保持不变,没有影响到其他位。如果使用 “&”(按位与):
执行ans &= (1 << 1)
,1 << 1
得到0010
,按位与操作后结果为0110 & 0010 = 0010
,原本的第三位和第一位被清零了,导致结果错误。
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ans = 0;
for(int i = 0; i < 32; ++i)
{
int ctn = 0;
for(int j = 0; j < nums.size(); ++j)
{
ctn += (nums[j] >> i) & 1 ;
}
if(ctn % 3 == 1)
{
ans |= (1 << i);
}
}
return ans;
}
};
只出现一次的数字|||
. - 力扣(LeetCode)
思路:
① 先全部异或起来,两两相同的全部抵消,最终得两个不同的数异或的结果。
② 找到该数的第 i 位为1。
③ 分组异或出最终结果。
a. 我们找到了第 i 位为 1, 在这一位中,要么是 0,要么是1。
b. 我们就可以把所有的数 按位与&1 结果不是 0 就是 1,正好把结果为 0 的分成一组,结果为1 的分成一组。为什么这样分组呢???
两个不同的数异或的结果,肯定是一个为 0 ,一个为 1,所以必然可以分成两组。假设除掉这两个数外,剩下的要么都是结果为 1 的组 或者都是结果为 0 的组,都被分到了一组,他们异或都抵消了,最后还是剩下两个不同的数。
c.把0的这一组全异或起来,把1的这一组全异或起来。相同的数都异或成了0,最终就出来了这个数。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
//1,全部异或起来
int val = 0;
for(auto e : nums)
{
val ^= e;
}
//2,找到第 i 位为 1
int m = 0;
for(int i =0; i< 32; ++i)
{
if(val & (1 << i))
break;
else
++m;
}
//3,分组异或出最终结果
int a = 0, b = 0;
for(auto e : nums)
{
if(e & (1 << m))
a ^= e;
else
b ^= e;
}
vector<int> v;
v.push_back(a);
v.push_back(b);
return v;
}
};
杨辉三角(vector<vector<int>>的理解)
. - 力扣(LeetCode)
思路:
① 创建二维数组 vector,并调整大小。
② 初始化每行的首尾元素。
③ 计算中间元素的值。
④ 返回结果。
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
vector<vector<int>> vv;
vv.resize(numRows);
for(size_t i = 0; i < vv.size(); ++i)
{
vv[i].resize(i + 1);
vv[i][0] = 1;
vv[i][vv[i].size() - 1] = 1;
}
for(size_t i = 0; i < vv.size(); ++i)
{
for(size_t j = 0; j < vv[i].size(); ++j)
{
if(vv[i][j] != 1)
{
vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
}
}
}
return vv;
}
};
删除排序数组中的重复项
. - 力扣(LeetCode)
思路:
定义两个指针,一个快指针,一个慢指针。
慢指针指向开头,快指针指向第二个元素。让快指针去遍历该数组,如果和慢指针的数相同就跳过,如果找到了不同的数,让慢指针走一步,并把快指针的值给慢指针,这样就可以到达去重的效果。
最后慢指针位置加一就是去重后数组长度。
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
int slow = 0;
for(int fast = 1; fast < nums.size(); ++fast)
{
if(nums[slow] != nums[fast])
{
nums[++slow] = nums[fast];
}
}
return slow + 1 ;
}
};
删除排序数组中的重复项||
. - 力扣(LeetCode)
思路:
在有序数组中,
特殊情况处理:
if(nums.size() < 3)
:如果数组的长度小于 3,那么肯定不存在重复出现三次的情况,直接返回数组的长度即可。初始化指针:
int slow = 2
:慢指针初始指向数组第三个位置(索引为 2),因为要保证每个元素最多出现两次,前两个位置不需要检查是否重复。for(int fast = 2; fast < nums.size(); ++fast)
:快指针从第三个位置开始遍历整个数组。去重过程:
if(nums[slow - 2]!= nums[fast])
:检查当前慢指针往前数第二个位置的元素与快指针指向的元素是否不同。- 如果不同,说明快指针指向的元素不是重复出现三次的元素,可以将其放入去重后的数组中。
nums[slow++] = nums[fast]
:将快指针指向的元素赋值给慢指针位置,并将慢指针向后移动一位。返回结果:
- 最终慢指针的值就是去重后数组的长度。
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
if(nums.size() < 3)
{
return nums.size();
}
int slow = 2;
for(int fast = 2; fast < nums.size(); ++fast)
{
if(nums[slow-2] != nums[fast])
{
nums[slow++] = nums[fast];
}
}
return slow;
}
};
数组中出现次数超过一半的数字
数组中出现次数超过一半的数字_牛客题霸_牛客网
思路:
解法一:
大家都会的解法,就是先排序,然后在取中间的下标,返回这个值就是那个数字。
题目说了该数字超过了一半,所以排序之后必定是这个超一半的数,这种解法大家都会。
代码如下:
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int>& numbers)
{
sort(numbers.begin(),numbers.end()); //先排序
int mid = numbers.size() / 2; // 找到中间值
return numbers[mid];
}
};
解法二:
此题主要是让大家理解摩尔投票法。
核心思想:
在一组数据中,找出出现次数超过一半(大于数组长度的一半)的元素。其主要思想是通过不断 “抵消” 不同的元素,最终剩下的可能就是出现次数超过一半的元素。
具体过程:
初始化阶段:
- 选取第一个元素作为候选元素,同时设置一个计数器为 1。
遍历阶段:
- 从第二个元素开始遍历数组。
- 如果当前元素与候选元素相同,计数器加 1。
- 如果不同,计数器减 1。
- 当计数器变为 0 时,重新选取当前元素为候选元素,并将计数器重置为 1。
验证阶段(可选):
- 遍历结束后,得到的候选元素可能是出现次数超过一半的元素,但需要进一步验证。
- 再次遍历数组,统计候选元素出现的次数。
- 如果候选元素出现的次数确实超过数组长度的一半,那么它就是所求的元素;否则,说明数组中不存在出现次数超过一半的元素。
#include <iostream>
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int>& numbers)
{
if (numbers.empty()) return 0;
int candidate = numbers[0];
int count = 1;
for(int i = 1; i < numbers.size(); ++i)
{
if(count == 0)
{
candidate = numbers[i];
count = 1;
}
else if (numbers[i] == candidate)
{
++count;
}
else
{
--count;
}
}
return candidate;
}
};