剑指offer之求数组里面只出现一次的的两个数据

举报
chenyu 发表于 2021/07/27 00:41:19 2021/07/27
【摘要】 1 问题 一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。           2 分析 第一种方法:我们用位运算 我们想到位运算 (1) a^a=0 (2)a^0=a (2)a^b^c=a^(b^c)=(a^c)^b   1) 对所有运算进行异或运...

1 问题

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

 

 

 

 

 

2 分析

第一种方法:我们用位运算

我们想到位运算


  
  1. 1) a^a=0
  2. 2)a^0=a
  3. 2)a^b^c=a^(b^c)=(a^c)^b

 

1) 对所有运算进行异或运算,最后结果就是两个出现一次的元素异或结果,接下来问题演变成了我们知道两个不同数据的异或值,那么怎么求出这两个值呢?

2) 因为这两个元素不相等,所以异或的结果肯定不是0,也就是可以再异或的结果中找到1位不为0的位,例如异或结果的最后一位为1,我们把这个位置标记为index,然后我们把原始数组分为2个数组,第一个数组中的每个数组的第index位都是1的数组和每个数组的第index位都是0的数组,然后我们再把这个两组数据进行异或处理,分别求出的数据就是我们不同的两个数。

 

第二种方法:我们用map,key为元素值,如果出现几次放进value里面去,然后最后遍历如果value是1的话,我们得到2个key就行。

 

 

 

 

3 代码实现

用异或处理的C++版本如下


  
  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. void findApperanceTwoNumber(vector<int>& input, int& num1, int& num2)
  5. {
  6. if (input.size() < 2)
  7. {
  8. std::cout << "input.size() < 2" << std::endl;
  9. return;
  10. }
  11. int sum = 0;
  12. //对所有数据进行异或处理
  13. for (int i = 0; i < input.size(); ++i)
  14. {
  15. sum ^= input[i];
  16. }
  17. //我们通过index找到这个2个不同元素异或值的位1的位置
  18. int index = 0;
  19. for (int i = 0; i < 32; ++i)
  20. {
  21. if (((sum >> i) & 1) == 1)
  22. {
  23. index = i;
  24. break;
  25. }
  26. }
  27. //然后我们遍历数组把每个数据的第index位和1进行异或处理,分别得到结果为1和0的2组数据,然后把这2组数据分别异或处理的和就是2个不同的数据
  28. for (int i = 0; i < input.size(); ++i)
  29. {
  30. if (((input[i] >> index) & 1) == 1)
  31. {
  32. num1 ^= input[i];
  33. }
  34. else
  35. {
  36. num2 ^= input[i];
  37. }
  38. }
  39. }
  40. int main()
  41. {
  42. vector<int> v2;
  43. v2.push_back(2);
  44. v2.push_back(2);
  45. v2.push_back(3);
  46. v2.push_back(4);
  47. v2.push_back(6);
  48. v2.push_back(6);
  49. int a = 0, b = 0;
  50. findApperanceTwoNumber(v2, a, b);
  51. std::cout << "a is "<< a << " b is " << b << std::endl;
  52. return 0;
  53. }

用map处理的C++版本如下


  
  1. #include <iostream>
  2. #include <vector>
  3. #include <map>
  4. using namespace std;
  5. void findApperanceTwoNumber2(vector<int>& input, int& num1, int& num2)
  6. {
  7. if (input.size() < 2)
  8. {
  9. std::cout << "input.size() < 2" << std::endl;
  10. return;
  11. }
  12. map<int, int> datas;
  13. for (int i = 0; i < input.size(); ++i)
  14. {
  15. //C++里面map如果要通过key获取value的话,我们先需要探测map里面是不是有这个key,我们可以count函数,这里如果是java版本的话,就算key不存在的话,我们执行get方法操作,得到的null,没关系。
  16. if (datas.count(input[i]))
  17. {
  18. if (datas[input[i]] == 1)
  19. {
  20. //这里用数组形式是因为如果用insert如果发现key一样的话,再次插入会失败,我们所以用数组的形式,这里是通过key更新value
  21. datas[input[i]] = 2;
  22. }
  23. }
  24. else
  25. {
  26. datas[input[i]] = 1;
  27. }
  28. }
  29. ///然后我们再遍历map
  30. for (map<int, int>::iterator it = datas.begin(); it != datas.end(); ++it)
  31. {
  32. if (it->second == 1)
  33. {
  34. if (num1 == 0)
  35. {
  36. //这里是获取key
  37. num1 = it->first;
  38. }
  39. else
  40. {
  41. //这里是获取value
  42. num2 = it->first;
  43. }
  44. }
  45. }
  46. }
  47. int main()
  48. {
  49. vector<int> v2;
  50. v2.push_back(2);
  51. v2.push_back(2);
  52. v2.push_back(3);
  53. v2.push_back(4);
  54. v2.push_back(6);
  55. v2.push_back(6);
  56. int a = 0, b = 0;
  57. findApperanceTwoNumber2(v2, a, b);
  58. std::cout << "a is "<< a << " b is " << b << std::endl;
  59. return 0;
  60. }

运行结果如下


  
  1. a is 3 b is 4

 

 

 

用map处理的java版本如下


  
  1. import java.io.*;
  2. import java.util.ArrayList;
  3. import java.util.*;
  4. class St
  5. {
  6. public St() {}
  7. ArrayList<Integer> findApperanceTwoNumber2(int[] datas) {
  8. ArrayList<Integer> list = new ArrayList<Integer>();
  9. if (datas == null || datas.length < 2)
  10. return list;
  11. HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
  12. for (int i = 0; i < datas.length; ++i) {
  13. if (map.containsKey(datas[i])) {
  14. if (map.get(datas[i]) == 1)
  15. {
  16. map.put(datas[i], 2);
  17. }
  18. } else {
  19. map.put(datas[i], 1);
  20. }
  21. }
  22. Set<Integer> keys = map.keySet();
  23. for (int key : keys) {
  24. if (map.get(key) == 1) {
  25. list.add(key);
  26. }
  27. }
  28. return list;
  29. }
  30. }
  31. class test
  32. {
  33. public static void main (String[] args) throws java.lang.Exception
  34. {
  35. ArrayList<Integer> list = new ArrayList<Integer>();
  36. St s = new St();
  37. int [] a = {1, 1, 3, 5, 4, 4};
  38. list = s.findApperanceTwoNumber2(a);
  39. for (int i : list)
  40. {
  41. System.out.println("value is " + i);
  42. }
  43. }
  44. }

运行结果如下


  
  1. value is 3
  2. value is 5

 

 

 

4 总结

如果我们有2个不同数的异或值,那我们怎么知道这2个数据值呢?(这里不是说直接知道这2个元素的数组意思,不然还要你求干吊) 因为这两个元素不相等,所以异或的结果肯定不是0,也就是可以再异或的结果中找到1位不为0的位,例如异或结果的最后一位为1,我们把这个位置标记为index,然后我们把原始数组分为2个数组,第一个数组中的每个数组的第index位都是1的数组和每个数组的第index位都是0的数组,然后我们再把这个两组数据进行异或处理,分别求出的数据就是我们不同的两个数。

C++版本的map操作,我们最好是用数组形式,因为数组形式既可以赋值和可以用来进行修改操作,如果是用insert函数插入的当key是同样而value不是同样值的时候会失败,然后我们获取的话最好也是通过数组形式的key获取,但是获取之前我们需要判断key是否存在map里面的key没有,如果没在的话,直接获取就有问题,但是java的话,就算没有key,获取的也是null值,没关系。

C++的话我们map用count(key)函数来判断是否key存在,而java的话,我么可以用map的containsKey(key)函数判断key是否存在,无论在C++版本还是java版本,我们要对map通过key获取value操作,如果不是通过keySet来获取(就是这个key必然存在map里面的时候),我们都要先用上面的函数进行探测是否包含key,这样代码的健壮性好点。

文章来源: chenyu.blog.csdn.net,作者:chen.yu,版权归原作者所有,如需转载,请联系作者。

原文链接:chenyu.blog.csdn.net/article/details/102714551

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。