OJ刷题:《剑指offer》之单身狗1、2 !(巧用位操作符,超详细讲解!)

简介: OJ刷题:《剑指offer》之单身狗1、2 !(巧用位操作符,超详细讲解!)

1.单身狗1


1.1 题目描述


在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。


例如:

数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,出5


1.2排序寻找


1. 对于一个无序的数组,当我们将他们进行排序后,一切问题都会简单很多的~


冒泡排序的伪代码~

void sort(int arr[], int len)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < len - 1; i++)
  {
    int flag = 1;
    for (j = 0; j < len - 1; j++)
    {
      if (arr[j] > arr[j + 1])
      {
        int tmp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = tmp;
        flag = 0;
      }
    }
    if (flag == 1)
      break;
  }//冒泡排序-》升序数组
}


2. 那我们要如何找到那个“单身狗”呢~


我们可以很容易的注意到相邻的2个元素是相同的,那我们是不是可以俩俩比较呢,若不同,则前面的那个元素一定就是“单身狗”啦~


下面是这部分的伪代码~

for (i = 0; i < len; i+=2)
{
  if (arr[i] != arr[i + 1])
  {
    printf("单身狗数字是:%d\n", arr[i]);
    break;
  }
}


3. 不过,我们还要考虑边界情况呢~


当我们将前面的元素都比较完后,还没有找到单身狗,而按照上面的算法逻辑的话,最后那个元素是无法俩俩比较的,那我们该怎么办呢~


我们不妨立个flag=1;若在上面的逻辑中找到单身狗,flag=0,若没有找到,flag还是=1,

那单身狗就是最后那个元素啦~

int flag = 1;
for (i = 0; i < len; i+=2)
{
  if (arr[i] != arr[i + 1])
  {
    printf("单身狗数字是:%d\n", arr[i]);
    flag = 0;
    break;
  }
}
if (flag == 1)
  printf("单身狗数字是:%d\n", arr[len-1]);

1.3巧用位操作符


1. 这个题目其实还有更为巧妙的方法呢~

这里先给俩个结论:1.相同的俩个元素^(抑或)结果为零~

                                2.零与其他元素抑或结果为那个元素本身~

那是为什么呢~(其实在我之前的博客也提到过,这里再说明一下)http://【有趣的移位操作符和位操作符(由浅入深轻松搞定!) - CSDN App】http://t.csdnimg.cn/q2ubr


2. 好了,知道了这些那这个题目就显得异常简单了啦~


我们将所有元素抑或起来是不是就找到了那个单身狗呢~

下面是这部分的伪代码~

int find_single_dog(int arr[], int sz)
{
    int ret = 0;
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        ret ^= arr[i];
    }
    return ret;
}//sz是数组的长度


2.单身狗2


2.1 题目描述


一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。


例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,6

只有5和6只出现1次,要找出5和6.


2.2排序寻找


1.这个题目如果用排序来做的话,主要的算法部分其实和前面的是差不多的~

唯一不同的是我们要找的俩个“单身狗”后才能结束寻找~


我们可以用count来计数,当count==2时,就结束寻找~

代码如下~

for (i = 0; i < len-1; i+=2)
{
  if (arr[i] != arr[i + 1])
  {
    printf("单身狗是%d\n", arr[i]);
    count++;
    i--;
  }
  if (count == 2)
    break;
}


2. 当然我们还要考虑边界情况的~


我们可以很容易的知道上面的代码逻辑一定可以至少找到一个单身狗5

当循环结束后i是等于len-1的,所以优化后的代码如下~

  for (i = 0; i < len-1; i+=2)
  {
    if (arr[i] != arr[i + 1])
    {
      printf("单身狗是%d\n", arr[i]);
      count++;
      i--;
    }
    if (count == 2)
      break;
  }
  if(i==len-1)
    printf("单身狗是%d\n", arr[i]);//考虑边界情况
}


2.3巧用位操作符


1. 上面的代码还是不够牛逼,让我们看看牛逼的代码~

当单身狗只有一个时,我们可以用位操作的方法快速找到,这里有俩个不同的元素,这时我们就不可以简单的将他们全部抑或起来了,那怎么办呢~


2. 我们可不可以想办法将这俩个单身狗分开来后再进行抑或呢~(举个栗子,数据不一定正确分配)


3. 我们先将所有元素抑或起来(还是以上面的元素为例~)


然后我们将结果放到临时变量tmp中,那tmp到底有什么用呢~


我们仔细思考一下就会惊奇的发现(找到tmp二进制中第一个1(相异为1)可以区分俩个不同的单身狗!)即:找到有分歧的一位。在这一位上,两个数一定是一个1一个0

找tmp二进制中的第一个1的代码如下~

//找到tmp二进制中第一个1(相异为1)区分俩个不同的元素
int count = 0;//用count来记录tmp二进制中的第一个1
while ((tmp & (1<<count))==0)
{
  count++;
}


4. 最后,我们要将数组中的所有元素右移count位后再与一按位与(此时一定可以将不同的单身狗区分开来,至于其他相同的元素不管他们那一位上是1还是0都会被分配到相同的部分)

//数组中的元素右移count后&1
for (int i = 0; i < len; i++)
{
  if (((arr[i] >>= count) & 1) == 1)
    *tmp1^= arr[i];
  else
    *tmp2^= arr[i];
}

注:tmp1和tmp2一定要初始化为零呢~,还要注意运算符的优先级(一定要加括号按照我们的思路计算!)


附:完整的代码~

void Fun(int arr[], int*tmp1, int*tmp2, int len)
{
  int tmp = 0;
  //将所有元素抑或起来结果放到tmp中
  for (int i = 0; i < len; i++)
     tmp ^= arr[i];
  //找到tmp二进制中第一个1(相异为1)区分俩个不同的元素
  int count = 0;//用count来记录tmp二进制中的第一个1
  while ((tmp & (1<<count))==0)
  {
    count++;
  }
  //数组中的元素右移count后&1
  for (int i = 0; i < len; i++)
  {
    if (((arr[i] >>= count) & 1) == 1)
      *tmp1^= arr[i];
    else
      *tmp2^= arr[i];
  }
}
相关文章
|
4月前
|
机器学习/深度学习 算法
面试题 08.12:八皇后
面试题 08.12:八皇后
38 0
|
6月前
|
JavaScript 前端开发 C语言
leetcode每日一题 2021/4/1 1006. 笨阶乘
leetcode每日一题 2021/4/1 1006. 笨阶乘
26 0
|
5月前
|
Java
每日一刷《剑指offer》字符串篇之左旋转字符串
每日一刷《剑指offer》字符串篇之左旋转字符串
40 0
每日一刷《剑指offer》字符串篇之左旋转字符串
|
5月前
|
机器学习/深度学习
蓝桥杯-2/14天-完全平方数【另类思路】
蓝桥杯-2/14天-完全平方数【另类思路】
|
12月前
卡特兰数—以leetcode22括号生成为例(笔记)
卡特兰数—以leetcode22括号生成为例(笔记)
|
C语言
【leetcode】学了栈和队列却觉得无用武之地?试试这几道题目吧!
【leetcode】学了栈和队列却觉得无用武之地?试试这几道题目吧!
71 0
|
算法 Java
代码随想录算法训练营第十天 | KMP算法 字符串总结 双指针回顾
代码随想录算法训练营第十天 | KMP算法 字符串总结 双指针回顾
84 0
|
存储 算法 Java
代码随想录刷题|LeetCode 332.重新安排行程 51. N皇后 37. 解数独
代码随想录刷题|LeetCode 332.重新安排行程 51. N皇后 37. 解数独
代码随想录刷题|LeetCode 332.重新安排行程 51. N皇后 37. 解数独
【刷题系列】 Java数组小题(一)
【刷题系列】 Java数组小题(一)
【刷题系列】   Java数组小题(一)