C++中vector和set删除一亿个数字中的奇数

原文地址:http://blog.youkuaiyun.com/corcplusplusorjava/article/details/45115057  向原作者致敬

一、vector

先贴代码再解释:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <vector>  
  4. #include <set>  
  5.   
  6. using namespace std;  
  7.   
  8. const unsigned int NUM = 100000000;  
  9.   
  10. void removeOdd1(vector<int>& a)  
  11. {  
  12.     for(vector<int>::iterator it = a.begin();it!=a.end();)  
  13.     {  
  14.         if((*it)%2==1)  
  15.         {  
  16.             it = a.erase(it);//注意这里和set的区别,erase当前元素后面的元素会自动不过了所以不需要++了  
  17.         }  
  18.         else  
  19.         {  
  20.             it++;  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. bool isOdd(unsigned int x)  
  26. {  
  27.     if(x%2==1)  
  28.     {  
  29.         return true;  
  30.     }  
  31.     else  
  32.     {  
  33.         return false;  
  34.     }  
  35. }  
  36.   
  37. void removeOdd2(vector<int>& a)  
  38. {  
  39.     a.erase(remove_if(a.begin(), a.end(), isOdd),a.end());  
  40. }  
  41.   
  42.   
  43. int main()  
  44. {  
  45.     vector<int> a(NUM, 0);  
  46.     for(unsigned int i=0;i<NUM;++i)  
  47.     {  
  48.         a[i] = i;  
  49.     }  
  50.       
  51. //  removeOdd1(a);  
  52.     removeOdd2(a);  
  53.       
  54.     return 0;  
  55. }  
一般情况下我们最直观的方法就是用方法一,对于小数据量这些都没有问题,但是数据量大时运行速度就不行了。这是因为vector其实是对数组的封装你每找到一个奇数就移除那么其后面的数据是要向前移动的也即是每次删除一个元素后面的每个元素就要移动一次。对于一亿的数据量我在我的笔记本上没有等到运行结果!对于方法二remove_if()函数的原理是把vector中的偶数拷贝到另一个个vector中(其实remove_if()是在同一个vector上操作的),也即a中前一段存的是偶数而后一段存的还是原理的值,remove_if()返回的是所有符合条件的数据的下一个位置,因此使用a.erase()擦除后面的数据。以下代码是对其进行的验证:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <vector>  
  4. #include <set>  
  5.   
  6. using namespace std;  
  7.   
  8. const unsigned int NUM = 10;  
  9.   
  10. bool isOdd(unsigned int x)  
  11. {  
  12.     if(x%2==1)  
  13.     {  
  14.         return true;  
  15.     }  
  16.     else  
  17.     {  
  18.         return false;  
  19.     }  
  20. }  
  21.   
  22. int main()  
  23. {  
  24.     vector<int> a(NUM, 0);  
  25.     for(unsigned int i=0;i<NUM;++i)  
  26.     {  
  27.         a[i] = i;  
  28.     }  
  29.       
  30.     vector<int>::iterator t = remove_if(a.begin(), a.end(), isOdd);  
  31.     a.erase(t, a.end());  
  32.     for(vector<int>::iterator it = a.begin(); it<t; ++it)  
  33.     {  
  34.         cout << *it << endl;  
  35.     }  
  36.     cout << endl;  
  37.     for(vector<int>::iterator it=t; it<a.end(); ++it)  
  38.     {  
  39.         cout << *it << endl;  
  40.     }  
  41.       
  42.     return 0;  
  43. }  

二、set

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <set>  
  4.   
  5. using namespace std;  
  6.   
  7. //const unsigned int NUM = 100000000;  
  8. const unsigned int NUM = 100;//因这里建树比较慢所以取值小了点  
  9.   
  10. void removeOdd1(set<int>& s)  
  11. {     
  12.     for(set<int>::iterator it = s.begin(); it!=s.end();++it)//注意这里和vector的不同,即使erase当前元素迭代器还是要++  
  13.     {  
  14.         if((*it)%2==1)  
  15.         {  
  16.             s.erase(it);  
  17.         }  
  18.     }  
  19. }  
  20.   
  21. int main()  
  22. {  
  23.     set<int> s;  
  24.     for(unsigned int i=0; i<NUM; ++i)  
  25.     {  
  26.         s.insert(i);  
  27.     }  
  28.     removeOdd1(s);  
  29.   
  30.     return 0;  
  31. }  

set是使用红黑树实现的因此插入和删除操作都在lg(n)时间内完成,因此直接删除就可以了。set是一个集合适合查找元素,而vector是一个连续的内存空间可以随机存取。但从查找一个元素来看set的效率要比vector高。

1、如果你需要高效的随即存取,而不在乎插入和删除的效率,使用vector

2、如果你要查找一个元素是否在某集合内存中,则使用set存储这个集合比较好

### 使用 C++ 位运算找到数组中唯一出现一次的元素 在解决此类问题时,可以利用 **XOR 运算** 的特性来高效处理。以下是具体方法: #### 方法一:单个数字仅出现一次的情况 对于一个数组中除了某个数字外其余所有数字均出现两次的情形,可以通过 XOR 运算快速定位该唯一的单一数字。 XOR 运算具有如下性质: - `a ^ a = 0` (任何数与其本身异或结果为零) - `a ^ 0 = a` (任何数与零异或结果为其本身) 因此,遍历整个数组并对其中的所有元素执行 XOR 操作即可得到最终的结果[^1]。 ```cpp class Solution { public: int singleNumber(std::vector<int>& nums) { int result = 0; for(auto num : nums){ result ^= num; // 对每个元素进行异或操作 } return result; // 返回最后剩下的那个唯一值 } }; ``` 此算法的时间复杂度为 O(n),空间复杂度为 O(1)[^1]。 --- #### 方法二:两个数字各出现一次的情况 当存在两个独立的数值各自只显现一次而其它全部成双呈现的时候,则需进一步扩展逻辑。首先依旧采用整体 XOR 来获取这两个特殊值之间的差异点;接着依据某一位上的区别把原始数据分割开来再分别做 XOR 处理从而分离出目标值们[^2]。 下面是基于这种方法的具体实现代码片段: ```cpp #include <stdio.h> #include <stdlib.h> void findTwoNumbers(int* src, int n, int* pNum1, int* pNum2){ int xorSum = 0; /* Step 1: Compute the overall XOR */ for(int i=0;i<n;i++) xorSum ^= src[i]; /* Step 2: Find out which bit is set between two unique numbers*/ unsigned int pos = 0; while(((xorSum >> pos) & 1)==0) ++pos ; /* Initialize both results as zero */ *pNum1=*pNum2=0; /* Separate and compute individual values based on differing bits */ for(int j=0;j<n;++j){ if((src[j]>>pos)&1){ (*pNum1)^=src[j]; }else{ (*pNum2)^=src[j]; } } } int main(){ int arr[]={1,1,2,3,4,5,6,6,7,7}; int num1,num2; findTwoNumbers(arr,sizeof(arr)/sizeof(*arr),&num1,&num2); printf("The non-repeating elements are %d and %d\n",num1 ,num2 ); } ``` 这里的关键在于如何区分那两位独一无二的整型变量——通过检测它们之间第一个不同的比特位完成分类并再次运用之前提到过的简单版本的技术得出结论[^2]. --- #### 方法三:有序数组中的特殊情况 如果给定的是已排序好的序列,并且知道除了一项之外每一对连续项目都是完全一样的情况下的解法会更加简洁明了一些。我们能够借助二分查找配合按位计算技巧迅速锁定答案所在区域[^3]: ```cpp class Solution { public: int singleNonDuplicate(std::vector<int>& nums) { int left = 0, right = nums.size()-1; while (left < right){ int mid = left + ((right-left)>>1); if(nums[mid]==nums[(mid^1)]){ left = mid+1; } else{ right = mid; } } return nums[left]; } }; ``` 在这个变体里边主要依赖于观察到的事实就是说只要保持住当前考察范围内的长度始终维持在一个奇数状态的话那么必然有一个单身汉藏匿于此区间之中[^3]。 --- ### 总结 以上介绍了三种不同场景下应用 C++ 中的位运算技术去发现那些隐藏起来仅仅现身过一次的数据成员的办法。无论是面对无序列表还是已经排列整齐待检视的对象集合都能够游刃有余地解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值