一个C语言面试的经典例题

简介: 一个C语言面试的经典例题

前言:这个题目是C语言面试题中非常经典的一道题目,当你面对这道题目是不是有点思路,然后到中间就断了?没错我第一次遇到的时候,也是卡在中间了,现在写下这篇博客为了更加的理清思路,加深印象!!!


题目:一个整型数组num里除两个数字之外出现一次,其他数字都出现了两次。请写程序找出这两个只出现一次的数字,要求时间复杂度为O(n),空间复杂度为O(1);例如数组为{1,3,5,7,1,3,5,9},找出7和9


注:时间复杂度:不是计算时间,是计算大概的运算次数

      空间复杂度:不是计算空间,是计算大概定义的变量个数;O(1)代表定义常数个变量,并不是单只1个。


解析:当拿到这个题目,第一感觉肯定是要用异或运算符,但是异或完以后呢?出现两次的都异或为0了;只剩下两个只出现一次的数字;怎么把这两个数字分开呢?这就是难点,方法很巧妙,很难想出来!!!所以我不妨先写简单一点的,假如这个题目只有一个出现一次的数字,怎么把它取出来?


更改后的题目:一个整型数组num里除一个数字之外出现一次,其他数字都出现了两次。请写程序找出这一个只出现一次的数字,要求时间复杂度为O(n),空间复杂度为O(1)

 

这样就很简单了,在输入整个数组的数后;直接定义一个变量(暂定为n=0);遍历整个数组,然后开始从第一个数组元素与n进行异或操作,最终结果就是我们想要的值!!!


下面看具体代码:


634657c7ea084997911db7565572f365.png


注意:


1. define定义的是标识符常量;数组我们就暂时定义为20个整型的大小;想要输入几个数,由我们自己决定;


2. ^是按位异或;相同为0,相异为1;0与任何数异或还是任何数它本身;我们遍历整个数组,都要异或一遍。

 

好了,简单的只有一个数出现一次的,我们已经解决了,现在来解决有两个数出现一次的;假如这两个数是a=5和b=6,那么整个数组异或的结果就是:m=a^b=5^6;最终异或的结果就是:


3f4ff5c0247043a8b8d183732fca7b94.png

那么我们怎么把这两个数分开呢?我们发现最终结果m的二进制位有0有1;那就找m的任何一个二进制位为1的数来作为划分;为什么?


答:因为当m的一个二进制数为1时,必然是这两个异或的数(a和b)对应的位数一个是0一个是1,才可能异或成结果为1的二进制位;所以,通过这个方法就能把他们分离开来。


有了这个思路,我们还有两个问题要解决:1.怎么求出一个数的位为1的下标?


                                                                   2.分开的两组数据,怎么办?在创建两个数组存起来?


1.怎么求出一个数的二进制位为1的下标?


这就需要另外一个知识点,比如我用的是VS2019(32位);我们就要写一个循环,取出它的每一个二进制位与1相与,这样就能把一个数的每一个二进制位为1的位取出来;下面看具体事例:


注:怎么取出每一位?假如这个数是5;对于32位机器,它的二进制表示是


00000000 00000000 00000000 00000101    我们可以把这个数利用左移操作符(>>)来取出它的每一个二进制位:


第一次不移位,看第一位是不是为1;


第二次移1位看第二位是不是1.........


直到移了第31位,就能让最后第32位的二进制取出来与1相与看是不是1


具体代码如下:


3d25bb66bfa84607956ea50baaccc8e4.png


当然在这个题目中我们只需要从数中找到一个二进制位为1的,所以找到一个后,我们就定义一个变量(暂定这个变量为pos, pos = i)来记住移了多少位,在break跳出循环;详细理解看下面:


2.分开的两组数据,怎么办?在创建两个数组存起来?  

分为两组数以后,怎么办呢?在用两个数组存起来?然后继续按照一个数字出现一次,其它数字都是出现两次的情况来处理?=====》想想都麻烦


答:我们在利用pos这个下标分组前,就先定义两个变量用来接收最后两边分组过后最终剩下的两个只出现一次的数(暂定两个变量为m1, n1);重新遍历整个数组,开始每个数左移pos位与1相&;最终分为两组=========>pos位为1的分为一组,pos位为0的分为一组;在每一组每添加一个新成员,就与m1或者n1进行异或,直到最终每一组所有成员都添加进来,异或结束,两个组剩的最后数值就是对应的两个只出现一次的数!!!


下面看具体实现代码:


ac911f1a4e0946d58cc7305bcd55e984.png


随便举一组数,进行验证:

d420b66ad3c64e4f9be0d7cebe014392.png



总结: 你也可以在定义时,就把数组的大小和元素的初始值都初始化上,这样就会直接输入最终的两个元素,看着界面比较干净美观;但是这样的代码并不通用,相当于写了,换一组数据改着太麻烦;所以我更喜欢这种灵活的方式。

   

刚开始写可能有些表述不清楚,遇到看不懂的可以先结合下面的代码结合着看;有错误可以及时留言告诉我,一起学习,一起进步!!!


相关文章
|
4月前
|
网络协议 编译器 Linux
【C语言】结构体内存对齐:热门面试话题
【C语言】结构体内存对齐:热门面试话题
152 0
|
8月前
|
存储 安全 编译器
C语言面试题1-10
指针声明后立即初始化。 内存释放后将指针置为NULL。 避免越界访问。 10. 一个指针变量占几个字节? 一个指针变量的大小与系统和编译器相关。在32位系统中,指针变量占4个字节;在64位系统中,指针变量占8个字节。 通过深入了解以上问题,能够更好地掌握C语言内存管理的核心概念,提高编写高效、安全代码的能力。
68 1
|
4月前
|
Serverless 编译器 C语言
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
【C语言】指针篇- 深度解析Sizeof和Strlen:热门面试题探究(5/5)
|
6月前
|
C语言
C语言操作符(补充+面试)
C语言操作符(补充+面试)
59 6
|
6月前
|
算法 C语言
【面试题】【C语言】寻找两个正序数组的中位数
【面试题】【C语言】寻找两个正序数组的中位数
55 0
|
8月前
|
机器学习/深度学习 人工智能 C语言
|
8月前
|
机器学习/深度学习 移动开发 人工智能
C语言程序设计例题
C语言程序设计50例
|
8月前
|
机器学习/深度学习 移动开发 人工智能
C语言编程例题分享
C语言编程经典100例
|
8月前
|
存储 安全 编译器
C语言面试题11至20题
在C语言中,可以使用以下方式实现循环: for循环:用于确定次数的循环。 for (int i = 0; i < 10; i++) { // 循环体 } while循环:用于条件控制的循环。 while (condition) { // 循环体 } do-while循环:至少执行一次的条件循环。 do { // 循环体 } while (condition); 通过深入理解这些面试题,可以更好地准备编程面试,展示对编程原理和技术细节的深刻掌握。
67 3
|
8月前
|
存储 缓存 C语言
C语言面试题30至39题
. 用变量a给出下面的定义 由于题目未明确定义,这里给出几个常见定义: 整数变量:int a; 字符变量:char a; 浮点变量:float a; 双精度浮点变量:double a; 指针变量:int *a; 通过理解和掌握这些面试题,可以更好地准备编程面试,展示对编程原理和技术细节的深刻掌握。
70 2