下面是链接为了解释练习2的并且还有与操作符相关的知识。
C语言与操作符相关的经典例题-CSDN博客
操作符详解(上)-CSDN博客
操作符详解(下)-CSDN博客
目录
练习1:在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。
练习2:两个整数二进制位不同个数
练习3:获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
前言:有些题目可能我们想破脑袋也不知道怎么写,但是如果用一些看似奇怪的方法去写,就能有一种柳暗花明又一村的感觉。虽然这些代码是那些大牛想到的,但是只要我们坚持看这些大牛的代码,我们总可以多学到一点。(个人感想,可忽略。)
步入正题:
练习1:在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。
这个题目是关键就是找到那个单独的数字。如果我们能想到用按位异或(^)的方法就比较简单了。
解题的知识储备:0 ^ n = n n ^ n = 0 。
思路:我们可以先定义一个变量等于0,用它来异或数组里的数。由于0 ^ n = n,所以这个变量不影响最终的求值。(当然这一步如果简化就是把这个变量初始化为数组的第一个元素,而异或开始的是用第二个元素。)用循环产生这些数组的元素,当我们把它们全部异或到一起时,就可以用到n ^ n = 0,这个最终剩下的就是那个单独的数字,也就是我们要找的那个数。
上面这个就是原理图 。之所以可以这样做,是因为异或也满足交换律。
#include <stdio.h>
int FindNumber(int* p, int sz)
{
int i = 0;
int flag = 0;
for (i = 0; i < sz; i++)
{
flag ^= *(p + i);
}
return flag;
}
int main()
{
int arr[] = { 1,2,3,4,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = FindNumber(arr, sz);//因为要遍历数组,所以需要数组和数组的元素
printf("%d\n", ret);
return 0;
}
#include <stdio.h>
int FindNumber(int* p, int sz)
{
int i = 0;
int flag = 1;
for (i = 1; i < sz; i++)
{
flag ^= *(p + i);
}
return flag;
}
int main()
{
int arr[] = { 1,2,3,4,5,4,3,2,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = FindNumber(arr, sz);//因为要遍历数组,所以需要数组和数组的元素
printf("%d\n", ret);
return 0;
}
第二个代码是用括号内写法的。
代码演示:
练习2:两个整数二进制位不同个数
两个整数二进制位不同个数_牛客题霸_牛客网
题目:
思路:题目是要求输入的两个数,二进制中又多少个比特位不相同。既然是让我们求两个数的二进制位不同个数,这里我们应该就可以联想到一个操作符:按位异或(^),相同的二进制位取0,不同的二进制位取1。我们就可以把这两个数按位异或存放到第三个数中,既然不同的二进制位是1,那么只要我们把这个1的个数统计出来了,也就意味着求出来二进制中不同位的个数了。
#include <stdio.h>
int Num(int c)
{
int count = 0;
while(c)
{
c = c & (c-1);//这一步如果看不懂的话,可以去看我前面的那一篇关于操作符的文章,我将链接放到文章的最前面了
count++;
}
return count;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
int c = a ^ b;
int ret = Num(c);//求二进制中一的个数(用函数来实现)
printf("%d\n",ret);
return 0;
}
之所以用函数的方法,是因为不想让这个主函数看起来很长,这样会影响代码的美观。
按位异或的思想其实就是在找不同,如果想要找到不同的地方,就可以使用按位异或的方法。
这个就类似题眼。
练习3:获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列
这个题目其实就是在变相的问一个整数二进制的0和1的个数并且按照奇偶打印出来。因为一个整数的二进制位要么是0,要么是1。那么我们怎么知道这个整数的二进制位是0还是1呢?这里就可以用按位与(&)来计算,当我们按位与上一个1是,如果这个位是0,那么最终的结果就是0;如果这个位是1,那么最终的结果就是1。这是第一位的结果,我们要的是32个位,那么就循环32次。我们只要分奇偶来按位与,并且输出就可以了。接下来,就是要分奇偶来讨论了。讨论奇数的时候怎么循环?偶数的时候怎么循环?我就用画图来描述:
根据8位,我们就可以推出32位的循环结果。如果i从0开始,那么结束的位置就都要减1。因为开始是0。
代码演示:
#include <stdio.h>
void Get(int n)
{
int i = 0;
printf("奇数位:");
for (i = 0; i < 31; i += 2)//打印奇数位
{
if (((n >> i) & 1) == 1)
{
printf("%d ", 1);
}
else
{
printf("%d ", 0);
}
}
printf("\n");
printf("偶数位:");
for (i = 1; i < 32; i += 2)//打印偶数位
{
if (((n >> i) & 1) == 1)
{
printf("%d ", 1);
}
else
{
printf("%d ", 0);
}
}
}
int main()
{
int n = 0;
scanf("%d", &n);
Get(n);
return 0;
}
其实这个还可以简化一点,既然 ((n >> i) & 1)的结果是1,就打印1;结果是0,就打印0。那么我们就直接把这个表达式当作是printf的参数就更好了。
#include <stdio.h>
void Get(int n)
{
int i = 0;
printf("奇数位:");
for (i = 0; i < 31; i += 2)
{
printf("%d ", (n >> i) & 1);
}
printf("\n");
printf("偶数位:");
for (i = 1; i < 32; i += 2)
{
printf("%d ", (n >> i) & 1);
}
}
int main()
{
int n = 0;
scanf("%d", &n);
Get(n);
return 0;
}
但是这里有的小伙伴可能会将1的位数给移动,因为移动1的位数相对比较简单。但是这个题目狡猾就狡猾在这里。你移动了那个位数之后,虽然比较出来的结果是1,但是它的权重变了,不再是2的0次方,而是2的1次方,2的2次方等等。这样写出来的程序就输出的不是1了。
#include <stdio.h>
void Get(int n)
{
int i = 0;
printf("奇数位:");
for (i = 0; i < 31; i += 2)//打印奇数位
{
printf("%d ", n & (1 << i));
}
printf("\n");
printf("偶数位:");
for (i = 1; i < 32; i += 2)//打印偶数位
{
printf("%d ", n & (1 << i));
}
}
int main()
{
int n = 0;
scanf("%d", &n);
Get(n);
return 0;
}
我们输入的是7,输出的却是1,2,4,但是我们会发现两者之和相等。这就是因为权重的问题。