C语言编程实战:大家一起云刷题 - day3

C语言编程实战:大家一起云刷题 - day3

🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言 🔥专栏:《C语言从零开始到精通》 《C语言编程实战》 《数据结构与算法》 《小游戏与项目》 💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。

前言:

本系列博客将会更新一些每日的刷题代码以及对题目的解读,帮助大家更好地理解编程思路和实现方法

文章目录

前言:题目:1. n的阶乘2. 打印一个整数的每一位3. 交换两个变量(不创建临时变量)4. 统计二进制中1的个数5. 求两个数二进制中不同位的个数6. 单身狗17. 打印整数二进制的奇数位和偶数位

知识点1. 右移操作符1.1. 逻辑右移(Logical Right Shift)1.2 算术右移(Arithmetic Right Shift)1.3 关键区别总结1.4 C 语言中的右移规则

题目:

1. n的阶乘

递归和非递归分别实现求n的阶乘(不考虑溢出的问题)

阶乘的定义是n! = n × (n-1) × … × 1,其中0!和1!都为1。下面分别用递归和循环(非递归)的方式实现:

//递归和非递归分别实现求n的阶乘(不考虑溢出的问题)

#include

//递归实现:将大问题分解为规模更小的子问题,直到达到终止条件

int fact(int n)

{

//终止条件:1的阶乘是1,2的阶乘是2(也可简化为n<=1时返回1)

if (n == 1 || n == 2)

{

return n;

}

else

{

//递归公式:n! = n × (n-1)!

return n * fact(n - 1);

}

}

int main()

{

//测试递归实现,计算6的阶乘

printf("%d\n", fact(6));

//循环(非递归)实现:通过迭代累乘得到结果

int n = 6;

int facter = 1; //初始化乘积为1(乘法的单位元)

while (n > 0)

{

facter *= n; //累乘当前值

n--; //逐步减小数值,直到n变为0

}

printf("%d\n", facter);

return 0;

}

2. 打印一个整数的每一位

递归方式实现打印一个整数的每一位

要打印一个整数的每一位(如951打印为9 5 1),可以通过递归不断提取高位数字,直到剩下最后一位时开始打印,从而实现从高位到低位的输出。

//递归方式实现打印一个整数的每一位

#include

//递归实现:先处理高位,再打印当前位

void print(int n)

{

//终止条件:如果n是个位数,直接打印

if (n < 10)

{

printf("%d ", n);

}

else

{

//先递归处理去掉最后一位的数(获取高位)

print(n / 10);

//再打印最后一位(通过取余10得到)

printf("%d ", n % 10);

}

}

int main()

{

//测试:打印951的每一位

print(951); //输出结果:9 5 1

return 0;

}

3. 交换两个变量(不创建临时变量)

不允许创建临时变量,交换两个整数的内容

通常交换两个变量需要借助临时变量,这里利用异或()的特性实现无临时变量交换。异或的特点是:aa=0,a^0=a,且满足交换律和结合律。

#include

int main()

{

int a = 6;

int b = 12;

printf("交换前:a=%d b=%d\n", a, b);

//第一步:a存储a和b的异或结果(a^b)

a = a ^ b;

//第二步:b = (a^b) ^ b = a ^ (b^b) = a ^ 0 = a(此时b的值变为原来的a)

b = a ^ b;

//第三步:a = (a^b) ^ a(新b是原a)= (原a^原b) ^ 原a = 原b ^ (原a^原a) = 原b ^ 0 = 原b

a = a ^ b;

printf("交换后:a=%d b=%d\n", a, b);

return 0;

}

4. 统计二进制中1的个数

二进制中1的个数

统计二进制中1的个数可以利用位运算的技巧:n & (n-1) 会消除n二进制中最后一个1。通过循环执行该操作,直到n变为0,执行的次数就是1的个数。

//写一个函数返回参数二进制中 1 的个数。

//比如: 15 0000 1111 4 个 1

#include

void Count(int n)

{

int count = 0; //计数器,记录1的个数

while (n != 0)

{

//n & (n-1) 会消除n的二进制中最后一个1

n = n & (n - 1);

count++; //每消除一个1,计数器加1

}

printf("%d\n", count);

}

int main()

{

int n = 0;

scanf("%d", &n); //输入一个整数

Count(n); //统计并打印该整数二进制中1的个数

return 0;

}

5. 求两个数二进制中不同位的个数

编程实现:两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?

输入例子:

1999 2299

输出例子:7

关键思路是利用异或(^)操作:两个数异或后,结果的二进制中,1的位置就是两个数二进制不同的位置。因此,只需统计异或结果中1的个数即可。

#include

//统计一个数二进制中1的个数(复用第4题的逻辑)

int Count(int n)

{

int count = 0;

while (n != 0)

{

n = n & (n - 1);

count++;

}

return count;

}

int main() {

int a, b;

//循环读取输入,直到文件结束(EOF)

while (scanf("%d %d", &a, &b) != EOF)

{

//a^b的结果中,1的位置即为a和b二进制不同的位置

int c = a ^ b;

//统计c中1的个数,即为不同位的数量

int count = Count(c);

printf("%d\n", count);

}

return 0;

}

6. 单身狗1

在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。例如:

数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5

这里使用双重循环的暴力解法:遍历每个元素,统计其在数组中出现的次数,次数为1的即为目标数字。(更优的解法可以利用异或特性:aa=0,0a=a,将所有元素异或后结果即为只出现一次的数字)

//在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,请找出那个只出现一次的数字。

//数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5

#include

int main()

{

int arr[] = { 1,2,3,4,5,5,4,6,3,2,1 }; //示例数组,6是只出现一次的数字

int sz = sizeof(arr) / sizeof(arr[0]); //计算数组元素个数

//遍历数组中的每个元素

for (int i = 0;i < sz;i++)

{

int count = 0; //统计当前元素出现的次数

//再次遍历数组,计数与当前元素相等的元素个数

for (int j = 0;j < sz;j++)

{

if (arr[i] == arr[j])

{

count++;

}

}

//如果次数为1,说明找到目标数字

if (count == 1)

printf("%d",arr[i]);

}

return 0;

}

7. 打印整数二进制的奇数位和偶数位

获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

整数的二进制位从右向左(即从低位到高位)编号,最右边为第1位(奇数位),第2位为偶数位,以此类推。通过循环取余和除法提取每一位,分别存入奇数位和偶数位的数组,最后打印即可。

//获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

#include

void print(int n)

{

int count = 0; //记录当前是第几位(从1开始)

int arr1[16] = {0}; //存储奇数位(第1,3,5,...位),int型共32位,故最多16个奇数位

int arr2[16] = {0}; //存储偶数位(第2,4,6,...位),最多16个偶数位

while (n != 0)

{

int m = n % 2; //提取当前最低位(0或1)

count++; //位数加1

//判断当前位是奇数位还是偶数位,存入对应数组

if (count % 2 != 0) //奇数位(第1,3,5...位)

{

arr1[(count - 1)/2] = m; //计算存储位置(0,1,2...)

}

else //偶数位(第2,4,6...位)

{

arr2[count/2 -1] = m; //计算存储位置(0,1,2...)

}

n = n / 2; //移除已经处理的最低位

}

//打印奇数位(从右向左,即从第1位开始)

printf("奇数位,从右向左依次:");

for (int i = 0;i < 16; i++)

{

printf("%d ", arr1[i]);

}

printf("\n");

//打印偶数位(从右向左,即从第2位开始)

printf("偶数位,从右向左依次:");

for (int i = 0;i < 16; i++)

{

printf("%d ", arr2[i]);

}

}

int main()

{

print(0b1111); //测试二进制1111(十进制15),其奇数位为1,1,偶数位为1,1

return 0;

}

知识点

1. 右移操作符

在计算机中,右移运算(>>)是对二进制位的操作,根据处理符号位的方式不同,分为逻辑右移和算术右移,二者的核心区别在于移位后左侧空出的位如何填充。下面详细讲解:

1.1. 逻辑右移(Logical Right Shift)

规则:不管原数是正数还是负数,右移时左侧空出的位一律用 0 填充,右侧超出边界的位直接丢弃。适用场景:通常用于无符号整数(unsigned),因为无符号数没有符号位,仅表示数值大小。示例: 以 8 位二进制为例,对无符号数 0b10110100(十进制 180)进行逻辑右移 2 位: 原二进制:10110100 右移 2 位后:00101101(左侧补 0,右侧丢弃最后 2 位) 结果为十进制 45。

1.2 算术右移(Arithmetic Right Shift)

规则:右移时,左侧空出的位用原数的符号位(最高位)填充,右侧超出边界的位直接丢弃。

若原数是正数(符号位为 0),则左侧补 0;若原数是负数(符号位为 1),则左侧补 1。 适用场景:主要用于有符号整数(signed),目的是保持移位后数值的符号不变(即移位后与原数同号),且近似于“除以 2 的 n 次方”(向下取整)。

示例: ① 正数右移:8 位有符号数 0b00110100(十进制 52)算术右移 2 位: 原二进制:0 0110100(符号位为 0) 右移 2 位后:0 0001101(左侧补符号位 0) 结果为十进制 13(52 ÷ 4 = 13,符合预期)。

② 负数右移:8 位有符号数 0b11001100(十进制 -52,补码表示)算术右移 2 位: 原二进制:1 1001100(符号位为 1) 右移 2 位后:1 1110011(左侧补符号位 1) 结果为十进制 -13(-52 ÷ 4 = -13,符合预期)。

1.3 关键区别总结

类型左侧填充值适用数据类型核心目的逻辑右移固定填充 0无符号整数(unsigned)仅移动位,不考虑符号算术右移填充原符号位有符号整数(signed)保持符号不变,近似除以 2ⁿ

1.4 C 语言中的右移规则

C 语言标准没有明确规定有符号数的右移是逻辑右移还是算术右移,具体由编译器实现(大多数编译器对有符号数采用算术右移,对无符号数采用逻辑右移)。例如:

int a = -52; // 二进制补码:11111111 11111111 11111111 11001100(32位)

a = a >> 2; // 算术右移后:11111111 11111111 11111111 11110011(结果为 -13)

unsigned int b = 180; // 二进制:00000000 00000000 00000000 10110100

b = b >> 2; // 逻辑右移后:00000000 00000000 00000000 00101101(结果为 45)

因此,在处理有符号数的右移时,需注意编译器的行为,避免依赖未定义的操作。

本节完…

相关推荐

阴包的准确位置图片及功效
365bet繁体中文

阴包的准确位置图片及功效

📅 10-28 👁️ 3272
小米手机使用手册
365足彩推荐

小米手机使用手册

📅 09-13 👁️ 5428
杭州也可以看小鹿,周末就来良渚古城遗址公园
365足彩推荐

杭州也可以看小鹿,周末就来良渚古城遗址公园

📅 10-18 👁️ 3251