找出数组中单独的元素

简介: 此类题目需要非常熟悉位操作及位运算,同时要画图思考,才能将思路整理得很清楚。或许有很多读者对我提出疑问,他们会认为这只是针对我举例的数组,才会有这种结果。而我想说:你们可以有时间尝试换一换数组中的元素,并且打乱顺序,也是可以做到的。本篇博客的目的主要是阐明逻辑,因为思路很重要!

1. 一个数组中只有一个数字只出现一次,其他所有数字都出现了两次, 请找出这数字。



程序清单1:


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int search(int* arr, int sz);
int main(void)
{
  int arr[] = {1,3,4,1,3};
  int sz = sizeof(arr) / sizeof(arr[0]);
  int n = search(arr, sz);
  printf("%d\n", n);
  return 0;
}
int search(int* arr, int sz)
{
  int i = 0;
  int ret = 0;
  for (i = 0; i < sz; i++)
  {
    ret = ret ^ arr[i];
  }
  return ret;
}


如上,创建一个数组:arr [ ] = { 3,3,4,1,1 }

数字1和3出现了一次,数字4出现了两次。


分析:将数字之间两两异或

异或的规则是:同为0,异为1


48b6d3217c144318807be5a523355a9e.jpg


注意:


int 类型的二进制是32位的数据,而我每个数字只画出了4位,这是为了方便阐述逻辑

那么,根据上面图片,可以找到三个规律


  1. 0 ^ a = a
  2. a ^ a = 0
  3. a ^ a ^ b = a ^ b ^ a


相同元素“抵消”,唯一元素“保留”。


我们再看程序清单1:


分析:


创建一个变量 ret,接着让 ret 与数组中所有的元素异或,两个相同的数字就“抵消”,变成0,剩余的就是单独的数字,即是我们所要找的。


2. 一个数组中只有两个数字只出现一次,其他所有数字都出现了两次, 请找出这两个数字。



程序清单2:


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void search(int* arr, int sz);
int main(void)
{
  int arr[] = { 1,2,1,3,4,3 };
  int sz = sizeof(arr) / sizeof(arr[0]);
  search(arr, sz);
  return 0;
}
void search(int* arr, int sz)
{
  int i = 0;
  int ret = 0;
  int sign = 0;
  int s1 = 0;
  int s2 = 0;
  //1. 所有元素进行异或,得出 2 ^ 4 = 0110
  for (i = 0; i < sz; i++)
  {
    ret = ret ^ arr[i];
  }
  //2. 找出sign位置
  for (i = 0; i < 32; i++)
  {
    if (((ret >> i) & 1) == 1)
    {
      sign = i;
      break;
    }
  }
  //3.  每个数字sign位置 &1 为0 的数组放一起,sign位置 &1 为1 的数组放一起
  for (i = 0; i < sz; i++)
  {
    if (((arr[i] >> sign) & 1) == 1)
    {
      s1 = s1 ^ arr[i]; //得出s1
    }
  }
  s2 = s1 ^ ret;  // 得出s2(s1 ^ s1 ^ s2 = s2)
  printf("我们要找出的数字是:\n %d %d\n", s1, s2);
}


如上,创建一个数组:arr [ ] = { 1,2,1,3,4,3 }

2和4 即是我们所找的数字


分析:


有了程序清单1的规律,我们在其基础上可以找找程序清单2的规律

尝试思路:


1.和程序清单1一样,我们不妨把当前的整个数组都异或一遍

动点脑子,异或的结果指定是 2 ^ 4,因为这两个数字“抵消不了嘛!”

接下来继续分析:


6b82ff0c5b194a55a6727bb0ebe8f077.jpg


上图我们把数字 2和4 进行异或,结果是 0110,为什么会发生这种情况呢?这是因为两个不一样的数字进行异或,那么异或结果的二进制中,必然有数字1。可以发现:我之所以将上图用蓝色圈出来1,就是因为数字2的二进制,低位第二位是1,而数字4的二进制,低位第二位是0


2.那么,我们第二个思路就有了!就是将这两个数字拆开,然后分组,数字2分到一组,数组4分到另一组,之后按照程序清单1的异或操作就能得出结果啦。分组的依据是什么呢?分组的依据就如思路一所说的,因为两个数字不同,肯定有某个位数不相同。如数字 2和4,我们把二进制第二位是0的分到一组,二进制第二位为1的分到一组不就行了!


然而,道路是曲折的。我们要找的数字是2和4,数字2和数字4 在二进制中,低位第2位是不相同的,那么,如果我们要找的单独数字分别是1和5,情况就不同了。对于数字1和5,他们的前两位相同,低位第3位才是不同的,所以,如果我们进行下一步的编程,所编辑的程序一定要有适应所有的情况!


此时,我们回到程序清单2中的数组中,我决定要把数字 2 ^ 4 结果的 0110 按位与1,并且持续右移操作,直到找出某一位是1的位数,并记下这个位数的位置为sign,那么数字2和数字4 的在sign这个位置一定不一样!接着将数组中的其他元素( 1, 3 )分别按位与1,并且持续右移sign 个位置,这时已经分组完成。


3.最后,我们将第一组的所有元素进行异或,之后也可将第二组的所有元素进行异或。相同元素“抵消”,唯一元素“保留”。


6252cac50d9e4d3e85523e9a28d18a0a.jpgcbadea16a62e40feb9391a4b25fb9976.jpg


根据上面的图,并参照尝试思路,我们整理三个步骤:


  1. 将起初数组中的所有元素进行异或,得出 2 ^ 4 = 0110
  2. 找出 0110 不断右移并按位与1,得出位置sign,此时的sign位置有一个特点:

sign位置可以将要找的两个数字区分开来

  1. 每个数字sign位置&1为0的数组放一起,sign位置&1为1的数组放一起,最后分别遍历异或,相同元素“抵消”,唯一元素“保留”


输出结果:


a17c9bdda6fc4f52adef3897fb651d80.png


总结



此类题目需要非常熟悉位操作及位运算,同时要画图思考,才能将思路整理得很清楚。

或许有很多读者对我提出疑问,他们会认为这只是针对我举例的数组,才会有这种结果。而我想说:


你们可以有时间尝试换一换数组中的元素,并且打乱顺序,也是可以做到的。本篇博客的目的主要是阐明逻辑,因为思路很重要!


8a72dbed9fcc481cb4e7454f6e17589b.jpg


Over. 谢谢观看哟~

目录
相关文章
|
6月前
|
前端开发 Java
java前端:删除数组中指定元素的方法
java前端:删除数组中指定元素的方法
109 1
数组筛选,将数组[2,0,6,1,77,0,52,0,25,7]中大于等于10元素选出来,放入新数组,声明一个新的数组用于存放新数据newArr,遍历原来的旧数组,找到大于10的元素,依次追加新数组
数组筛选,将数组[2,0,6,1,77,0,52,0,25,7]中大于等于10元素选出来,放入新数组,声明一个新的数组用于存放新数据newArr,遍历原来的旧数组,找到大于10的元素,依次追加新数组
|
6月前
|
存储 Java API
Java数组元素的填充与替换技术详解
Java数组元素的填充与替换技术详解
67 1
|
5月前
|
索引
删除数组中的指定元素(了解如何删除数组中的指定元素,并返回一个新的数组,看这一篇就足够了!)
删除数组中的指定元素(了解如何删除数组中的指定元素,并返回一个新的数组,看这一篇就足够了!)
|
6月前
|
算法 前端开发
2635. 转换数组中的每个元素
2635. 转换数组中的每个元素
42 0
|
6月前
|
存储 JavaScript 前端开发
数组:数组是JS中的一种特殊对象,用于存储一组有序的数据。需要掌握数组的创建、访问、修改以及各种内置方法。
数组:数组是JS中的一种特殊对象,用于存储一组有序的数据。需要掌握数组的创建、访问、修改以及各种内置方法。
74 2
|
6月前
|
算法 Java C++
请实现一个队列,支持以下操作:添加元素、删除第一个元素、获取第一个元素。
请实现一个队列,支持以下操作:添加元素、删除第一个元素、获取第一个元素。
48 0
|
JSON C# 数据格式
数组比较的几种方式
1、string.Equals() ```csharp string[] strList1= new string[3] {"1", "2", "3"}; string[] strList2= new string[3] {"4", "5", "6"}; if (!string.Equals(strList1, strList2)) { // 比较数组的不同之处 } // 涉及到修改日志输出等数组可以直接json序列化然后用上述方法比较即可,如下 if (!string.Equals(JsonConvert.SerializeObject(list1), JsonConvert
76 0
将数组A中的内容和数组B中的内容进行交换。(数组一样大)
将数组A中的内容和数组B中的内容进行交换。(数组一样大)
110 0
将数组A中的内容和数组B中的内容进行交换。(数组一样大)
(第11列)C语言练习:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。五步带你解决。
(第11列)C语言练习:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。五步带你解决。
(第11列)C语言练习:输入数组,最大的与第一个元素交换,最小的与最后一个元素交换,输出数组。五步带你解决。