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];
  }
}
相关文章
|
Java 数据库连接 数据库
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
Spring Boot整合MyBatis Plus集成多数据源轻松实现数据读写分离
752 2
|
SQL Java 关系型数据库
技术心得记录:开源BI分析工具Metabase配置与完全使用手册
技术心得记录:开源BI分析工具Metabase配置与完全使用手册
2328 0
|
存储 JSON 前端开发
一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)
本篇文章记录的为SpringBoot Jackson序列化,ObjectMapper,configureMessageConverters,MappingJackson2HttpMessageConverter消息转换器相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬
4881 0
记十次面试字节/美团失败总结的《520道LeetCode题Java版答案》
去字节、美团、BAT等大厂面试,刷LeetCode上的数据结构+算法题是必修课。许多读者说,刷题的时候经常会遇到困难,想要找一本答案题解做参考。 下面分享几个用Java语言实现的开源LeetCode题解,也要感谢这些优秀的开源作者们,分享真的会让这个世界变得很美好。 LeetCode题解答案pdf(基于Java实现) 这是一本基于Java语言实现的LeetCode题解,格式为PDF,可作为刷题的辅助和参考,方便阅读,也方便打印出来学习。
|
自然语言处理 JavaScript 前端开发
ECMAScript 6 新特性详解(上)
ECMAScript 6 新特性详解(上)
|
存储 XML 数据格式
一起谈.NET技术,.NET的资源并不限于.resx文件,你可以采用任意存储形式 [上篇]
  为了构建一个轻量级的资源管理框架以满足简单的本地化(Localization)的需求,我试图直接对现有的Resource编程模型进行扩展。虽然最终没能满足我们的需求,但是这两天也算对.NET如何进行资源的存取进行了深入的学习,所以将我对此的认识通过博文的方式与诸位分享。
936 0
|
5天前
|
云安全 人工智能 自然语言处理
|
9天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
850 26
|
3天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
435 4