当前位置: 首页 > news >正文

常用操作符,操作符相关笔试题(谷歌)及算法的优化

目录

  • 一、进制的转换
  • 1.1 10进制转2进制数字
  • 1.2 2进制转8进制
  • 1.3 2进制转16进制
  • 二、原码、反码、补码
  • 三、移位操作符
    • 3.1 左移操作符
    • 3.2 右移操作符
  • 四、位操作符:&、|、^、~
    • 4.1 面试题1
    • 4.2 面试题2(最早出现于谷歌面试题)
    • 4.3 面试题3
  • 五、逗号表达式
  • 六、[ ]下标引用操作符
  • 七、函数调用操作符
  • 总结


前言
这篇内容想了想打算不细节的写了,只写我认为需要写的部分,因为有的操作符太简单了,而且也在我之前的文章写到过一些。

一、进制的转换

1.1 10进制转2进制数字

2                125  余数为12                62  余数为02               31  余数为12              15  余数为1 2              7  余数为12             3  余数为12            1  余数为10           

这里是什么意思呢?
就是125/2商62余1,以此类推,最后最左边的余数行由下往上依次所得得余数就是10进制转换出的2进制

1.2 2进制转8进制

8进制的数字每一位是0-7,0-7的数组,各自写成2进制,最多有3个2进制位就足够了,比如7的二进制是111,所以在2进制转8进制的时候,从2进制序列中右边低位开始向左每3个2进制位会换算一个8进制位,剩余不够3个二进制位的直接换算
如:2进制的01101011,换成8进制:0153,0开头的数字,会被当成八进制

2进制    01 101 011
8进制    1   5   3

在这里插入图片描述

1.3 2进制转16进制

16进制的数字每一位是0-9,a-f的数字,各自写成2进制,最多有4个2进制位就足够了,比如f的二进制是1111,所以在2进制转16进制数的时候,从2进制序列中右边低位开始向左每4个2进制位会换算一个16进制位,剩余不够4个二进制位的直接换算

2进制     0110 1011
16进制     6    b

而且想找10进制对应的二进制数的时候,也有便捷的方法

2进制:   1   1   1   1   1   1   1   1128  64  32  16  8   4   2   11   0  0   0   1   1

比如说这里找35对应的二进制数,32+2+1等于35,在对应的位置下的二进制取1,其余位取0就可


二、原码、反码、补码

整数的2进制表示方法有三种,即原码、反码和补码

有符号整数的三种表示方法均有符号位数值位两部分,2进制序列中,最高位的1是被当做符号位,剩余的都是数值位。对于无符号整数来说,全部都是数值位

符号位都是用0表示”正“,用1表示”负"

正整数的原、反、补码都相同
负整数的三种表示方法各不相同

原码:直接将数值按照正负数的形式翻译成二进制得到的就是原码
反码:将原码的符号位不变,其他位依次按位取反就可以得到反码
补码:反码+1就得到补码

补码得到原码也可以使用:取反,+1的操作

用下面两张图来表示:
在这里插入图片描述
在这里插入图片描述

附以代码,整型是32个比特位

int main()
{int a = 10;//00000000000000000000000000001010 -> 原码//00000000000000000000000000001010 -> 反码//00000000000000000000000000001010 -> 补码int b = -10;//10000000000000000000000000001010 -> 原码//11111111111111111111111111110101 -> 反码//11111111111111111111111111110110 -> 补码//11111111111111111111111111110110 -> 补码//10000000000000000000000000001001 -> 取反//10000000000000000000000000001010 -> 原码return 0;
}

对于整型来说:数据存放在内存中其实存放的就是补码

为什么呢?

  • 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器,即CPU默认只能算加法)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路,但在程序中打印出来的是原码。

这里也附加代码更好理解

int main()
{//1-1//1 + (-1)//假设按照原码计算//00000000000000000000000000000001 --> 原码 1//10000000000000000000000000000001 --> 原码 -1//10000000000000000000000000000010 -->     -2////再使用补码计算//00000000000000000000000000000001 - 补码//10000000000000000000000000000001 - 原码//11111111111111111111111111111110 - 反码//11111111111111111111111111111111 - 补码//00000000000000000000000000000001 - 补码//00000000000000000000000000000000 - 这里是把高位去掉留低位,这样打印出来的值就正确了return 0;
}

三、移位操作符

**<<**左移操作符

**>>**右移操作符

注:移位操作符的操作数只能是整数

3.1 左移操作符

移位规则:左边抛弃、右边补0
在这里插入图片描述
在这里插入图片描述

3.2 右移操作符

移位规则:首先右移运算分两种:

  1. 逻辑右移:左边用0填充。右边丢弃
  2. 算数右移,左边用原该值的符号位填充,右边丢弃

右移到底是逻辑右移还是算术右移取决于编译器,大部分编译器上都采用算数右移的,例如VS2022:
在这里插入图片描述
注意:对于移位运算符,不要移动负数位,这个是标准未定义的。

四、位操作符:&、|、^、~

位操作符有:

&        //按位与
|        //按位或
^        //按位异或
~        //按位取反

注:这里的位都是二进制位的意思,且他们的操作数必须是整数

分别用代码展示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.1 面试题1

题目:不能创建临时变量(第三个变量),实现两个整数的交换

正常思路便是创建第三个变量,如下图:
在这里插入图片描述
这里实现醋和酱油的互换便是先将酱油放到空瓶里,再把醋倒进酱油瓶,最后将空瓶中的酱油倒进醋瓶里。

代码实现如下

在这里插入图片描述

Plan B

在这里插入图片描述
没什么好说的,略加分析就能明白,虽然实现了功能,但是有一定的缺陷,一个整型变量存放的值一定有它的上限,一个整型变量最大就四个字节,一但a,b特别大,加起来超过了能放的最大值,此时就会丢失一些位,也就是存在数据溢出的风险。

那么能不能把刚刚的操作符知识应用到这个题目里呢?
在这里插入图片描述
这里就要补充一下异或的特点了,0与任何数异或都是那个数,且异或是支持交换率的。

3    011
5    101
^    110
3    011
5    101

这里3先与5异或,异或出来的值再与3异或,得到的值还是5

Plan C

在这里插入图片描述
这个代码的优点就是异或运算的过程中并不会产生进位,所以不会出现溢出的情况,当然对应的缺点就是不容易想到,可读性差。

4.2 面试题2(最早出现于谷歌面试题)

题目:求一个整数存储在内存中二进制中1的个数

15 -->41的个数)
补码:
00000000000000000000000000001111
-1 -->321的个数)
10000000000000000000000000000001 -原码
11111111111111111111111111111110 -反码
11111111111111111111111111111111 -补码

首先先分析题目内存中二进制,即内存中二进制的补码形式。

首先这里的思路就是求它二进制的每一位,再判断这一位是否是1,是就++。在之前十进制获得每一位的方法是%10取到最后一位,再/10去掉最后一位。这里二进制也可以与之对应,即先%2再/2,这就形成了一个循环,循环终止条件就是这个数变为0,这时候必定一个1也没有了。
比如15%2=7…1得到的便是00000000000000000000000000001111的最后一个1,之后再10/2==7去掉该1,就得到00000000000000000000000000000111转到十进制就是7了。

Plan A

在这里插入图片描述
补充:这里count是统计的作用,判断这一位是1后便++,直到n变为0,此时便一个1也没有了,因为0的二进制便是32个0。
且无论这个位是0还是1,这一位统计之后都要去掉,所以n/=2。

但是呢,这个程序其实是有问题的:
在这里插入图片描述
在输入-1的时候,发现本应输出32缺输出了0,原因是-1%2=-1,-1/2=0,进入一次循环后便结束了。所以这串代码的功能只对0和正整数有效。

Plan B

这里提供第二种方法便是按位取与(&)
思路便是

15 -->41的个数)
补码:
00000000000000000000000000001111
1的补码
00000000000000000000000000000001

对应的二进制位进行与运算,只要有0则为0,两个同时为1,才为1

这里15的补码与1的补码按位与运算,最后一位与的结果是1,其他位与的结果都是0,这就说明最低位是1,反之。统计完该位后,向右移动一位便可去掉该位,一共32位,最高位0移动31位就够了,每移动一位便说明有一个1。

代码实现如下:

在这里插入图片描述
在这里插入图片描述
可以看到这串代码负数也是能正常实现的,但是呢,它还是存在缺点的,这里需要循环32次,效率不是很高,而且无论几个1,它都会循环32次。

Plan C

改进之前先介绍一种算法:

n=15 -> 1111(15二进制形式有41)
n = n & (n-1) //下面为该公式逻辑展示n = 1111
n - 1 = 1110
&n = 1110
n - 1 = 1101
& n = 1100
n - 1 = 1011
&n = 1000
n - 1 = 0111
&n = 0000            

可以发现n=n&(n-1)这个算法,每一次取与,都会去掉一个1,在变为0000之前,该取与执行几次,就去掉几个1,负数也是一样的,这里不再做推演
在这里插入图片描述
在这里插入图片描述
这就是谷歌的一个以前的一个笔试题,试想,面试官肯定想要的不是第一种和第二种代码吧。

这里还可以进行扩展:

题目:判断n是否是2的次方数?

4 = 2的平方      00100
8 = 2的三次方    01000
9不是2的平方

可以看到次方数都有一个特点,他们的二进制数都只有一个1,因此我们就可以用这串代码来判断:

if(n&(n-1) == 0)
{就是次方数;
}

经过该算法取余之后,仅有的一个1就没有了,就变为0了。

代码展示

在这里插入图片描述
在这里插入图片描述

4.3 面试题3

题目:二进制位置0或者置1

1的二进制序列:   00000000000000000000000000000001左移4位:   00000000000000000000000000010000
132进制序列:   00000000000000000000000000001101
将第5位置置为100000000000000000000000000011101
将第5位再置位000000000000000000000000000001101

这里的思路就是将1的二进制数左移4位,再按位取或,按位或,是对应的二进制位进行或运算,只要有1,就是1,两个同时为0才为0,这样13经过按位取或之后第五位就能变1

第五位再置0稍微麻烦一些,现是将1左移4位

1的二进制序列:   00000000000000000000000000000001
1的二进制序列左移 00000000000000000000000000010000取反 11111111111111111111111111101111
将第5位置置为100000000000000000000000000011101取与 00000000000000000000000000001101
132进制序列:   00000000000000000000000000001101

取与后的值是不是与13的2进制序列一样呢?

下面这样省略了0看起来更简洁一些,而且能够对齐

13 = 00001101
|    00010000000111011<<(5-1) //这里5-1的原因是将第五位修改为1,可作为固定公式
29 = 00011101
&    11101111  >-<   00010000   1<<(5-1)00001101

所以该程序的代码就是这样的:
在这里插入图片描述

五、逗号表达式

exp1,exp2,exp3,...expN

逗号表达式,就是用逗号隔开多个表达式。
逗号表达式,从左到右依次执行,整个表达式的结果是最后一个表达式的结果,例下几张代码:
在这里插入图片描述

int d = 0;
if (a = b + 1, c = a / 2, d > 0)
{}

这里if里面的判断条件就主要看d>0这里

逗号表达式也能优化一些代码,如下:

a = get_val();
count_val(a);
while (a > 0)
{//业务处理//...a = get_val();count_val(a);
}

这样一个循环如何用逗号表达式进行优化呢?

while (a = get_val(), count_val(a), a > 0)
{//业务处理
}

六、[ ]下标引用操作符

操作数:一个数组名+一个索引值(下标)

int arr[10] = {0};arr[5] = 8;//[] - 下标引用操作符
//arr , 5是[]的两个操作数

七、函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数

int Add(int x, int y)
{return x + y;
}void test()
{}int main()
{printf("hehe"); //()是函数调用操作符int r = Add(3, 5);//()是函数调用操作符//()也是有操作数:函数名和参数都是()的操作数test();return 0;
}

函数调用操作符至少一个操作数,因为其函数可能无参数。


总结

该篇文章主要囊括了常见的操作符,涉及操作符的相关面试笔试题,其中还有一些算法的优化,还是干货满满的,希望喜欢作者文章的兄弟们点赞支持一下,你们的支持就是我最大的动力~

http://www.xdnf.cn/news/912169.html

相关文章:

  • [蓝桥杯]整理玩具
  • 【乐企板式文件】货物运输类发票,多页支持
  • 爱普生研发全新恒温晶体振荡器 “省、小、精”加速通信产业释放新质动能!
  • Java并发编程实战 Day 12:阻塞队列与线程协作
  • 文件上传/下载接口开发
  • Python训练第四十六天
  • 将内网地址转换成外网地址,让局域网外也能访问本地服务资源
  • 第21讲、Odoo 18 配置机制详解
  • 双面沉金PCB应用:打造卓越电子设备的黄金工艺
  • Android第十四次面试总结
  • Redis 哨兵模式
  • 【Qt】:设置新建类模板
  • [Python学习日记-90] 并发编程之多线程 —— 线程理论
  • 大模型编程助手-Cline
  • 沉金电路板工艺解析——化学沉金的优势与挑战
  • 使用python写一个虚拟的舵机模拟器
  • 经典文献阅读之--LidaRefer(基于变换器的自动驾驶户外3D视觉定位)
  • Onenet云网关方案应用--基于米尔瑞芯微RK3576开发板
  • (附示例代码及图示)问题分解策略提升复杂问题检索正确率
  • 【公益护航青春路】 “成长的秘密花园”青春期身心成长讲座走进校园
  • 肖特基二极管的特性
  • 森马下沙奥莱旗舰店盛大启幕:以“新常服“理念重塑消费体验新范式
  • 2025.06.06【Ribo-seq】|riboWaltz:P-site定位与三碱基周期性分析流程
  • OpenLayers 地图定位
  • 好子集的数目概念及表达形式
  • 数据库精选题(四)(E-R模型)
  • 基于nacos2.5.1的MCP服务端微服务项目开发环境配置简介
  • 提升CPU、内存及磁盘利用率脚本
  • 群晖文件操作API
  • 自然语言处理的发展