前言
近期做简单题的时候,发现有的题不止一种解法,并且有的解法很简便,所以写一篇文章记录一下。
大家在看题目的时候也可以试一试自己能想出几种解法,如果大家觉得自己的解法也很巧妙,欢迎和我交流~
喝汽水
题目:
喝汽水,1瓶汽水1元,2个空瓶可以换一瓶汽水,给20元,可以喝多少汽水。
(在做题时,可以将20换成n)
解法一
直接通过简单的数学计算一下,这是最容易想到也是最直接的解法。
代码如下
int main() { int money = 0; int bottle = 0; int empty = 0; scanf("%d", &money); bottle = money; empty = money; while (empty > 1) { bottle += empty / 2; empty = (empty / 2) + (empty % 2); } printf("%d\n",bottle); return 0; }
解释
就是喝一次买一次,喝光了再用剩下的空瓶子兑换新的
解法二
int main() { int money = 0; int bottle = 0; scanf("%d", &money); if (money > 0) bottle = 2 * money - 1; else bottle = 0; printf("%d\n", bottle); return 0;
解释
我们可以发现一个数学规律:
钱数与最终能喝到的饮料数的关系为:
bottle = 2 * money - 1;
利用这个表达式,就可以更简单的求出结果
找1
题目
写一个函数返回参数二进制中 1 的个数。
解法一
int count_num_of_1(unsigned int n) { int count = 0; while (n) { if (n % 2 == 1) count++; n /= 2; } return count; } int main() { int num = 0; scanf("%d", &num);//-1 int ret = count_num_of_1(num); printf("%d\n", ret); return 0; }
很常规也很简单的一种解法,这里就不解释了
解法二
int count_num_of_1(int n) { int count = 0; int i = 0; for (i = 0; i < 32; i++) { if (((n >> i) & 1) == 1) count++; } return count; } int count_num_of_1(int n) { int count = 0; while (n) { n = n & (n - 1); count++; } return count; }
解释
使用移位操作符,使用二进制来解题,更直观。
总结
善用操作符可能有时候会简化代码
找不同
题目
两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同
解法一
int count_diff_bit(int m ,int n) { int i = 0; int count = 0; for (i=0; i<32; i++) { if (((m >> i) & 1) != ((n >> i) & 1)) count++; } return count; } int main() { int m = 0; int n = 0; scanf("%d %d", &m, &n); int ret = count_diff_bit(m ,n); printf("%d\n", ret); return 0; }
解释
if语句就是判断在同时右移i个bit位之后,二者这是否相同
解法二
int count_diff_bit(int m, int n) { int r = m ^ n; int i = 0; int count = 0; while (r) { r = r & (r - 1); count++; } return count; } int main() { int m = 0; int n = 0; scanf("%d %d", &m, &n); int ret = count_diff_bit(m, n); printf("%d\n", ret); return 0; }
解释
两个点异或和while循环中r的计算
异或操作符:对应的二进制位相同为0,相异为1,那m和n的不同的二进制位异或后一定是1
之后数一下r的二进制中1的个数,就是m和n中不同位的个数
因为二进制的数字减1,就一定有一位变为0,所以将r与(r-1)进行按位与操作(&)后即可。
总结
无论是解法一的移位操作还是解法二中的异或操作,其实考察的就是对于操作符是否能熟练运用,个人觉得并无优劣之分,只是分享一下不同的解法。
逆置数组
将一个字符串的内容颠倒过来,并输出。
如:
i am student
输出:
tneduts ma i
提示:
下面两种解法,不做比较,但我本人更推荐第二种解法
解法一
#include <stdio.h> #include <string.h> int main() { char arr[10001] = { 0 }; gets(arr); int len = strlen(arr); int i = 0; for (i = len - 1; i >= 0; i--) { printf("%c", arr[i]); } return 0; }
解释:
就是正着输入,倒着打印
解法二
int main() { char arr[10001] = { 0 }; gets(arr); char* left = arr; char* right = arr + strlen(arr) - 1; while (left < right) { char tmp = *left; *left = *right; *right = tmp; left++; right--; } printf("%s\n", arr); return 0; }
解释:
采用双指针的方式,比使用if语句或者其他语句的解法要更高效,但在此就不展示其他解法了。
输出月份天数
输入一个值,输出对应的月份天数,这道题有很多种解法,在此只介绍两种,一种简单解法,一种优化解法。
像使用if语句的解法在此就不引入了,(这种解法有点吓人(捂脸))
解法一
int is_leap_year(int y) { if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) return 1; else return 0; } int main() { int y = 0; int m = 0; while (scanf("%d %d", &y, &m) == 2) { int d = 0; switch (m) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: d = 31; break; case 4: case 6: case 9: case 11: d = 30; break; case 2: d = 28; } if (is_leap_year(y) && m == 2) d++; printf("%d\n", d); } return 0; }
很简单,在此解释一下多组输入这个问题,因为scanf函数接收数据时,接收一个数据返回值是1,接收两个数据,返回值就是2,所以当输入的数据有两个时,就一直判定。
解法二
int is_leap_year(int y) { if ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) return 1; else return 0; } int main() { int y = 0; int m = 0; int days[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; while (scanf("%d %d", &y, &m) == 2) { int d = days[m]; if (is_leap_year(y) && (m == 2)) d++; printf("%d\n", d); } return 0; }
解释
使用到了数组,还是比较神奇的一种解法,相较于解法一就简单了一些。
结语
打算新开一个系列,关于题目的多种解法的,因为经常在阅读其他人写的代码的时候,就有一种感觉,这个人怎么写的那么好,他怎么想的。
所以就想开一个专栏关于题目解法的优化的,遇到好题记录一下分享给大家,目前暂定也是一篇文章五道题,目前大都是简单题,慢慢来。