【C语言】找单身狗问题

一.找单身狗问题初阶

1.问题描述

一个数组中只有一个数字出现一次,其他所有数字都出现了两次.编写一个函数,找出这个只出现一次的数字.

例如:

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

只有5出现了一次,要找出5.


2.解题思路

常规思路:

在常规思路中,我们首先想到的肯定是使用两层循环嵌套的方式遍历整个数组,

如果在遍历的过程中,有数字找到了和它相同的数字,那么终止循环,换下一个数字遍历,

直到找出那个遍历完整个数组都没有找到与它相同的数为止.

由该思路得出的代码如下:

#include<stdio.h>
int Find_single_dog(int* str, int sz)
{int i = 0;int j = 0;for (i = 0; i < sz; i++){int num = str[i];for (j = 0; j < sz; j++){if (i == j){continue;//当遍历到它自身时,该次循环无效,我们直接跳出本次循环}if (str[i] == str[j]){break;//当遍历到和它相同的数时,证明他不是"单身狗",终止循环,寻找下一个数}}if (j == sz){return str[i];//当遍历完整个数组时还没有找到和它相等的数,则该数字即为"单身狗"}}}
int main()
{int arr[] = { 1,2,3,4,5,1,2,3,4 };int sz = sizeof(arr) / sizeof(arr[0]);int single_dog=Find_single_dog(arr, sz);printf("%d", single_dog);return 0;
}

代码运行结果:

虽然该思路有效的完成了我们的要求,但该代码的循环次数是非常多的,

设数组一共有n个元素,则循环次数就几乎等于:n^n.

因此这种方法的时间复杂度非常高,程序的运行效率很低.


进阶思路:

在C语言中有一个异或(^)逻辑运算符,我们可以利用它的自反性质来找出"单身狗".

如果有对异或(^)还不是很了解的朋友可以先移步这篇博客,了解一下关于异或的一些性质,有助于理解后面的操作.【C语言】异或(^)操作符详解

先将文章里面的部分内容截出方便我们后续使用:

异或的运算法则(部分):

接下来我们画图来解释一下异或操作的步骤:

可以发现,凡是出现过两次的数字,两两异或后都变成了0,而唯一的只出现了一次的数字,与0异或的结果仍然是它本身,这说明整个数组相异或的结果恰好就是我们要找的"单身狗".

理解了进阶的思路后,代码的编写就很容易了,如下:

//进阶思路:利用异或操作的自反性来找出"单身狗"
int Find_single_dog(int* str, int sz)
{int num = 0;int i = 0;for (i = 0; i < sz; i++){num ^= str[i];}return num;
}int main()
{int arr[] = { 1,2,3,4,5,1,2,3,4 };int sz = sizeof(arr) / sizeof(arr[0]);int single_dog = Find_single_dog(arr, sz);printf("%d", single_dog);return 0;
}

代码运行测试:

可以看到,该代码同样成功得到了我们想要的结果,并且当数组中有n个元素时,代码循环的次数为n,比常规思路中的n^n的时间复杂度简化了不少,运行效率也非常高.


二.找单身狗问题进阶

1.问题描述

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次.编写一个函数,找出这个两个只出现一次的数字.

例如:

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

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


2.解题思路

常规思路:

在常规思路中,我们同样是使用两层循环嵌套的方式遍历整个数组,

如果在遍历的过程中,有数字找到了和它相同的数字,那么终止循环,换下一个数字遍历,

直到找出遍历完整个数组都没有找到与它相同的数,将这个数打印/存储,

再继续换下一个数遍历,寻找下一个"单身狗".

由该思路得出的代码如下:

void Find_single_dog(int* str, int sz)
{int i = 0;int j = 0;for (i = 0; i < sz; i++){int num = str[i];for (j = 0; j < sz; j++){if (i == j)continue;if (str[i] == str[j])break;}if (j == sz)printf("%d ", str[i]);}
}int main()
{int arr[] = { 1,2,3,4,5,1,2,3,4,6};int sz = sizeof(arr) / sizeof(arr[0]);Find_single_dog(arr, sz);return 0;
}

运行结果:

可以发现,在进阶问题中,常规思路和初阶问题的常规思路复杂度几乎没有区别,效率同样很低.


进阶思路:

先来观察数组:

int arr[]={1,2,3,4,5,1,2,3,4,6};

我们把这几个数组元素摘出来,便于观察:

接下来就是要解决问题了,首先我们想到的是,能不能将这些元素分成两组,
当然最主要的还是将5和6这两个单身狗分开,并且保证每组剩余的数是成对出现的:
如:

1 1 3 3 5  (第一组)
2 2 4 4 6  (第二组)
这样的话,我们就可以分别对第一组和第二组使用刚才初阶问题中的全部相异或的方法来得到5和6.
那么分组时,最重要的就是需要找出两个单身狗(5和6)的区别:
在本例中,显而易见,5是奇数,6是偶数,
也就是说,5的二进制位最低位一定为1,而6的二进制位最低位一定为0.
那么我们就可以将二进制位的最低位是否为1作为分组的依据,进而将数组的全部元素按该条件进行分组就可以达到我们的目的了.
但这样做的话还有一个问题,那就是当单身狗是6和8的时候呢?它们的二进制末位都是0时,该如何将它俩区分呢?
这时我们可以尝试将两个单身狗异或一下,就能找到其中的规律.
如:

6的二进制表示为  0 1 1 0
8的二进制表示为  1 0 0 0               根据异或运算的规则:相同取0,相异取1

它们异或的结果为 1 1 1 0


可以发现,除了末位,6和8异或后倒数第二位,倒数第三位和倒数第四位的结果都为1,说明这三位上它们的二进制都不相同.
那么我们就可以用这三位的任意一位来作为分组的依据,就可以将6和8分到不同的组中了.

因此,我们在最开始的时候将数组中的所有元素相异或,得到的其实就是两个单身狗相异或的结果,

然后将该结果的二进制位从最低位开始检索,直到找到为"1"的那一位,记录下这一位,并以此作为分组的依据,将数组元素分为两组后分别相异或,得到的两个结果就是要找的两个单身狗.

由该思路所得代码如下:

//进阶思路
void Find_single_dog(int* str, int sz)
{int i = 0;int num = 0;for (i = 0; i < sz; i++)num ^= str[i];//计算num的哪一位是1,并记录下来int pos = 0;for (i = 0; i < 32; i++){if (((num >> i) & 1) == 1){pos = i;break;}}int single_dog1 = 0;int single_dog2 = 0;for (i = 0; i < sz; i++){if (((str[i] >> pos) & 1) == 1)//以第pos位是否为1为条件,将元素分为两组分别异或得出结果single_dog1 ^= str[i];elsesingle_dog2 ^= str[i];}printf("%d %d\n",single_dog1,single_dog2);
}int main()
{int arr[] = { 1,2,3,4,5,1,2,3,4,6 };int sz = sizeof(arr) / sizeof(arr[0]);Find_single_dog(arr, sz);return 0;
}

运行结果:

当数组的元素为n时,可得进阶的时间复杂度约等为:2n+32.相比常规思路的n^n效率高了不少.

因此在后续的类似找"单身狗"的问题中,希望大家可以多多使用异或的方式来提升查找的效率.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/131716.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

@DS注解方式springboot多数据源配置及失效场景解决

1.使用教程 导入依赖 <!--多数据源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency>配置数据源 datasource:…

stu04-快速生成HTML5文档结构

1.直接输入一个英文的感叹号“!”&#xff0c;然后按Tab键&#xff0c;自动生成 2.输入“html:5”&#xff0c;然后按Tab键自动生成 3.直接复制粘贴以下代码&#xff1a; <!doctype html> <html lang"en"> <head><meta charset"UTF-8&q…

“金钥匙”转动!安全狗成功护航第二十三届中国国际投资贸易洽谈会举办

9月8日至9月11日&#xff0c;为期4天的第二十三届中国国际投资贸易洽谈会在厦门顺利举办。 作为国内云原生安全领导厂商&#xff0c;安全狗凭借突出的安全综合实力&#xff0c;受委托并担任此次会议网络安保技术支撑单位。 厦门服云信息科技有限公司&#xff08;品牌名&#xf…

【C语言】每日一题(半月斩)——day1

目录 &#x1f60a;前言 一.选择题 1.执行下面程序&#xff0c;正确的输出是&#xff08;c&#xff09; 2.以下不正确的定义语句是&#xff08; &#xff09; 3.test.c 文件中包括如下语句&#xff0c;文件中定义的四个变量中&#xff0c;是指针类型的变量为【多选】&a…

ARM架构指令集--专用指令

四、状态寄存器专用指令 CPSR寄存器-N Z C V T为0时 为ARM状态 F为0时 为开启FIQ状态 I为0时 为开启IRQ状态 图1 图2 一开始都是SVC指令&#xff0c;因为在操作系统启动的时候&#xff0c;在做一些初始化的操作&#xff0c;不允许被打断 图3 复位后CPSR寄存器为0xD3--…

BUSMASTER使用记录(一):基本收发、报文过滤、报文录制和数据回放

目录 一、概述二、基本收发2.1 连接设备2.2 接收2.3 发送 三、DBC加载和转换DBF文件四、报文过滤4.1 新增过滤器4.2 使能 五、报文录制/回放报文录制数据回放 一、概述 以往使用过的CAN盒虽然厂家不一样&#xff0c;但都兼容周立功的CANPro。这次使用的BusMaster&#xff0c;需…

【Hive SQL 每日一题】统计用户连续下单的日期区间

文章目录 测试数据需求说明需求实现 测试数据 create table test(user_id string,order_date string);INSERT INTO test(user_id, order_date) VALUES(101, 2021-09-21),(101, 2021-09-22),(101, 2021-09-23),(101, 2021-09-27),(101, 2021-09-28),(101, 2021-09-29),(101, 20…

9月12日作业

作业代码 #include <iostream>using namespace std;class Shape { protected:double cir;double area; public://无参构造Shape() {cout<<"无参构造"<<endl;}//有参构造Shape(double c, double a):cir(c), area(a){cout<<"有参构造&quo…

弄懂软件设计模式(一):单例模式和策略模式

前言 软件设计模式和设计原则是十分重要的&#xff0c;所有的开发框架和组件几乎都使用到了&#xff0c;比如在这小节中的单例模式就在SpringBean中被使用。在这篇文章中荔枝将会仔细梳理有关单例模式和策略模式的相关知识点&#xff0c;其中比较重要的是掌握单例模式的常规写法…

朋友圈大佬都去读研了,这份备考书单我码住了

作者简介&#xff1a; 辭七七&#xff0c;目前大二&#xff0c;正在学习C/C&#xff0c;Java&#xff0c;Python等 作者主页&#xff1a; 七七的个人主页 文章收录专栏&#xff1a; 七七的闲谈 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f…

React如何实现国际化?

目录 一、Redux准备工作 commonTypes.js commonActions.js commonReducer.js rootReducer.js 二、然后定义SelectLang组件 index.js index.less 三、创建语言包 welcomeLocale.js index.js 四、使用 react的入口文件 App.js welcome.js 附 关于如何实现国际…

进程地址空间(Linux虚拟内存机制)

文章目录 一.Linux进程地址空间的结构二.Linux管理进程地址空间的方式三.Linux进程使用物理内存的模型四.进程地址空间的存在意义 本章理论基于32位平台的Linux–kernel 2.6.32版本内核 一.Linux进程地址空间的结构 为了保证内存安全,现代操作系统不允许应用程序(进程)直接访问…

Redis总结(二)

目录 Redis线程模型 Redis是单线程吗&#xff1f; Redis采用单线程为什么那么快&#xff1f; I/O多路复用模型 Redis持久化 Redis如何保证数据不丢失&#xff1f; AOF日志 AOF三种写回策略 AOF重写机制 触发机制 重写原理 RDB快照 执行快照时&#xff0c;数据能被…

实现 js 中所有对象的深拷贝(包装对象,Date 对象,正则对象)

通过递归可以简单实现对象的深拷贝&#xff0c;但是这种方法不管是 ES6 还是 ES5 实现&#xff0c;都有同样的缺陷&#xff0c;就是只能实现特定的 object 的深度复制&#xff08;比如数组和函数&#xff09;&#xff0c;不能实现包装对象 Number&#xff0c;String &#xff0…

180B参数的Falcon登顶Hugging Face,vs chatGPT 最好开源大模型使用体验

文章目录 使用地址使用体验test1:简单喜好类问题test2:知识性问题test3:开放性问题test4:中文支持test5:问题时效性test6:学术问题使用地址 https://huggingface.co/spaces/tiiuae/falcon-180b-demo 使用体验 相比Falcon-7b,Falcon-180b拥有1800亿的参数量

【Axure高保真原型】日历日期原型模板

今天和大家分享日历日期的原型模板&#xff0c;包括月计划、周计划、日计划的原型案例&#xff0c;以及日期、时间、月份、区间选择器……具体效果可以点击下方视频观看 【原型预览及下载地址】 Axure 原型 备用地址&#xff1a;Untitled Document 【原型效果】 【原型效果…

Unity技术手册-UGUI零基础详细教程-Canvas详解

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

【Unity】Unity坑的集锦之RenderTexture打包黑屏

问题&#xff1a;Camera Output Texture设置RenderTexture后&#xff0c;打包用来Save PNG&#xff0c;黑屏 如果你打AB 包&#xff0c;然后是相机的OutputTexture是拖拽的话&#xff0c;记得将包一起打入 或者你可以代码赋值 Camera.targetTexture await Loader.LoadAsset&l…

【算法系列 | 8】深入解析查找算法之—二分查找

序言 心若有阳光&#xff0c;你便会看见这个世界有那么多美好值得期待和向往。 决定开一个算法专栏&#xff0c;希望能帮助大家很好的了解算法。主要深入解析每个算法&#xff0c;从概念到示例。 我们一起努力&#xff0c;成为更好的自己&#xff01; 今天第8讲&#xff0c;讲一…

IO day7

1->x.mind 2-> A进程 B进程