一,一个数组,n个元素,元素值的范围在0~n-1之间检测数据有无重复出现,如果重复出现,报错。
暴力:
使用第一个元素与后续所有元素比较,第二个元素与后续所有元素比较......时间消耗O(n^2)空间消耗O(1)。
bool hasDuplicateBruteForce(const vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] == nums[j]) {
return true; // 发现重复,报错
}
}
}
return false; // 没有发现重复
}
排序:
先排序,后检验相邻两个元素是否相同,遍历一遍即可,但排序所耗费的时间复杂度高。时间复杂度 O(n log2 n)空间复杂度O(log2 n)。
bool hasDuplicateSort(vector<int>& nums) {
sort(nums.begin(), nums.end()); // 先排序
for (int i = 0; i < nums.size() - 1; ++i) {
if (nums[i] == nums[i + 1]) {
return true; // 相邻元素相同,说明有重复
}
}
return false; // 没有发现重复
}
计数:
定义n个计数器(长度为n数组,把数值当下标,数组基于下标访问,O(1)即可完成,n个O(1)为O(n)),记忆每个元素出现的次数,如果有一个元素出现两次,直接报错。时间复杂度O(n),空间复杂度O(n)
bool hasDuplicateCount(const vector<int>& nums) {
int n = nums.size();
vector<int> count(n, 0); // 初始化计数器数组
for (int num : nums) {
if (count[num] > 0) {
return true; // 计数器已经记录过该值,说明有重复
}
count[num]++;
}
return false; // 没有发现重复
}
以上方法均用时间换空间或用空间换时间的置换,所以接下来将介绍一种兼得的方法。
让每个元素回到自己的下标上,如果每个都能塞回去,自然是没有重复的,这种方法源于下标范围与元素范围的高度重合。但是此方法修改了数组,此为一弊端。
代码逻辑
循环 遍历:0~n-1
{
(1)检测当前元素与下标是否相同
1.相同:直接处理下一个元素
2.不相同:比较当前元素与当前元素作为下标处存放的元素
1>相等:报错
2>不相等:交换
}
bool HasCF(int arr[], int length)
{
for (int i = 0; i < length; i++)//遍历数组每一元素
{
while (arr[i] != i)//如果相等 下一个元素 如果不等 比较当前元素与其下表
{
int target = arr[i];
if (arr[target] = target)
{
return true;//相同就有重复
}
else
{ swap(arr[i], arr[target]);
}//不相同用swap交换
}
}
注:这里为大家提供更多的交换方法
int tmp = arr[i]; // 用临时变量保存 arr[i] 的值
arr[i] = arr[target]; // 将 arr[target] 的值赋给 arr[i]
arr[target] = tmp; // 将临时变量的值赋给 arr[target]
if (i != target) { // 确保 i 和 target 不相等
arr[i] ^= arr[target]; // 第一步:异或操作
arr[target] ^= arr[i]; // 第二步:再次异或操作
arr[i] ^= arr[target]; // 第三步:最终异或操作
}
int tmp = arr[arr[i]]; // 保存 arr[arr[i]] 的值
arr[arr[i]] = arr[i]; // 将 arr[i] 的值赋给 arr[arr[i]]
arr[i] = tmp; // 将临时变量的值赋给 arr[i]
二,数组中有一个元素仅出现一次,其他元素均出现两次,如何快速找到只出现一次的元素。
暴力:
用第一个元素与之后元素比较,出现过,就不是该元素。时间复杂度O(n^2),空间复杂度O(1)
int findSingleElementBruteForce(const vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n; ++i) {
bool foundDuplicate = false;
for (int j = 0; j < n; ++j) {
if (i != j && nums[i] == nums[j]) {
foundDuplicate = true;
break;
}
}
if (!foundDuplicate) {
return nums[i];
}
}
return -1; // 如果没有找到,返回-1
}
排序:
排序后检查,一对一对检查,如若这一对不相同,则找到了。时间复杂度O(n log2 n)空间复杂度O(log2 n)
int findSingleElementBySorting(vector<int>& nums) {
sort(nums.begin(), nums.end()); // 先对数组进行排序
int n = nums.size();
for (int i = 0; i < n; i += 2) { // 每次检查一对
if (i == n - 1 || nums[i] != nums[i + 1]) {
return nums[i]; // 找到只出现一次的元素
}
}
return -1; // 如果没有找到,返回-1
}
map/哈希表: 一组无序数据反复搜素
注:这里补充一下位运算相关知识
-
按位与(&)
-
规则:对应的两个二进制位都为1时,结果才为1;否则为0。
-
示例:
5 & 3- 5的二进制是
0101,3的二进制是0011 - 按位与运算:
0101 & 0011 = 0001 - 结果是1。
- 5的二进制是
-
-
按位或(|)
-
规则:对应的两个二进制位只要有一个为1,结果就为1;否则为0。
-
示例:
5 | 3- 5的二进制是
0101,3的二进制是0011 - 按位或运算:
0101 | 0011 = 0111 - 结果是7。
- 5的二进制是
-
-
按位异或(^)
-
规则:对应的两个二进制位相同则为0,不同则为1。
-
a ^ a = 0:相同的数异或结果为0。 -
a ^ 0 = a:任何数与0异或结果是它本身。 -
异或满足交换律和结合律:
a ^ b ^ c = (a ^ b) ^ c = a ^ (b ^ c)。 -
计算机底层理解 相等 !(x^y)
-
计算机清零操作 x^=x
-
不使用中间变量实行交换(必须得是两块空间 两个值不同)
a=a^b; b=a^b; a=a^b; -
加密算法
-
格雷码
-
示例:
5 ^ 3- 5的二进制是
0101,3的二进制是0011 - 按位异或运算:
0101 ^ 0011 = 0110 - 结果是6。
- 5的二进制是
-
-
按位取反(~)
-
规则:对每个二进制位取反,0变1,1变0。需要注意的是,对于有符号数,还会涉及到补码表示。
-
示例:
~5- 5的二进制是
0101,取反后为1010(这里假设在一个合适的位数范围内,如8位时前面补充符号位等) - 结果是一个负数(具体值取决于数据类型的位数和计算机中的补码规则)。
- 5的二进制是
-
-
左移(<<)
-
规则:将二进制数向左移动指定的位数,右边补0。相当于乘以2的若干次方(移动位数)。
-
示例:
5 << 1- 5的二进制是
0101,左移1位后为1010 - 结果是10。
- 5的二进制是
-
-
右移(>>)
-
规则:将二进制数向右移动指定的位数,对于无符号数左边补0,对于有符号数根据符号位补0或1(算术右移)。
-
示例:
10 >> 1- 10的二进制是
1010,右移1位后为0101 - 结果是5。
- 10的二进制是
-
异或:
只要当前两个元素出现在同一个异或表达式中,无论二者距离多远,都能抵消掉,即为0。
例如:
13^6^13=6
三,数组中有两个元素仅出现一次,其他元素均出现两次,如何快速找到只出现一次的元素。
代码逻辑:
(1)整体异或^(用第一个和第二个异或,再用其结果与第三个异或,遍历)
(2)找到 xorResult 中的第一个非零位
xorResult是两个唯一元素的异或结果。在这两个元素的二进制表示中,至少有一位是不同的(否则它们的异或结果会是0)。- 我们需要找到
xorResult中第一个非零位(即该位上两个元素的值不同),并用它来区分这两个元素。 - 使用一个变量
mask,初始值为1(二进制表示为0001),通过不断左移(mask <<= 1)找到xorResult中第一个非零位。
3> 与上1 之后左移(&1<<1)常用
res&a;
a<<=1;
(3)根据 mask 将数组分为两组,并分别异或
-
根据
mask,我们可以将数组中的元素分为两组:- 一组是与
mask按位与结果为1的元素; - 另一组是与
mask按位与结果为0的元素。
- 一组是与
-
对每组分别进行异或操作,最终得到的两个结果就是仅出现一次的两个元素。
void findUniqueElements(const vector<int>& nums) {
// Step 1: 对整个数组进行异或操作
int xorResult = 0;
for (int num : nums) {
xorResult ^= num;
}
// Step 2: 找到xorResult中的第一个非零位
int mask = 1;
while ((xorResult & mask) == 0) {
mask <<= 1; // 左移直到找到第一个非零位
}
// Step 3: 根据mask将数组分为两组,并分别异或
int group1 = 0, group2 = 0;
for (int num : nums) {
if (num & mask) {
group1 ^= num; // 属于第一组
} else {
group2 ^= num; // 属于第二组
}
}
// Step 4: 输出结果
cout << "The two unique elements are: " << group1 << " and " << group2 << endl;
}
四,有n个数的数组,无重复,后丢/多了一个,求丢掉的元素。
1.正常之和 S1 当前之和S2 用S1-s2 但是可能会越界。
2。S1^S2即可得到多/少出的元素
#include <iostream>
#include <vector>
int findMissingOrExtraElement(const std::vector<int>& original, const std::vector<int>& modified) {
// 计算原数组的和 s1
int s1 = 0;
for (int num : original) {
s1 += num;
}
// 计算当前数组的和 s2
int s2 = 0;
for (int num : modified) {
s2 += num;
}
// 返回丢失或多余的元素
return s1 - s2;
}
int main() {
// 示例输入
std::vector<int> original = {1, 2, 3, 4, 5}; // 原数组
std::vector<int> modified = {1, 2, 3, 5}; // 当前数组(丢失了 4)
// 找到丢失或多余的元素
int result = findMissingOrExtraElement(original, modified);
// 输出结果
if (result > 0) {
std::cout << "丢失的元素是: " << result << std::endl;
} else {
std::cout << "多出的元素是: " << -result << std::endl;
}
return 0;
}
本博客同步于CSDN,稀土掘金
承蒙厚爱