【C语言】经典编程题

1. Fibonacci数列 ⭐️

做题链接:Fibonacci数列

Fibonacci数列是这样定义的:
F[0] = 0
F[1] = 1
for each i ≥ 2: F[i] = F[i-1] + F[i-2]
因此,Fibonacci数列就形如:0, 1, 1, 2, 3, 5, 8, 13, …,在Fibonacci数列中的数我们称为Fibonacci数。给你一个N,你想让其变为一个Fibonacci数,每一步你可以把当前数字X变为X-1或者X+1,现在给你一个数N求最少需要多少步可以变为Fibonacci数。

  • 输入描述:输入为一个正整数N(1 ≤ N ≤ 1,000,000)
  • 输出描述:输出一个最小的步数变为Fibonacci数"

示例:

  • 输入:15
  • 输出:2

思路:当 n 落在第一个斐波那契数的左边的时候似乎没有什么意义,它只有变成第一个斐波那契数的可能,没有最小步数。所以只有当 n 落在第一个斐波那契数和第二个斐波那契数的中间时,才求最少需要多少步可以变成斐波那契数。

  • 如果 n 等于第二个斐波那契数,步数就等于0。
  • 如果 n 小于第二个斐波那契数,分别求第一个斐波那契数和第二个斐波那契数分别减 n 的绝对值差,并比较大小。这里引用求绝对值的库函数abs,头文件 #include <math.h> 。当 abs(f1-n)<abs(f2 - n);则打印输出abs(f1-n)的值;反之则输出abs(f2-n)的值。
  • 如果 n 不等于也不小于第二个斐波那契数,n 大于第二个斐波那契数的时候,我们这边用赋值进行下一组数的计算。

代码如下:

#include <stdio.h>
#include <math.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int f1 = 0;
	int f2 = 1;
	int f3 = f1 + f2;
	while (1)
	{
		if (n == f2)
		{
			printf("0\n");
			break;
		}
		if (n < f2)
		{
			if (abs(f1 - n) < abs(f2 - n))
			{
				printf("%d\n", abs(f1 - n));
			}
			else
			{
				printf("%d\n", abs(f2 - n));
			}
			break;
		}
		//n>f2
		f1 = f2;
		f2 = f3;
		f3 = f1 + f2;
	}
	return 0;
}

在这里插入图片描述

2. 替换空格 🌟

做题链接:替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

这里有的人就会想将空格替换成%20有什么意义呢?是不是太闲了呢?
在这里插入图片描述
当我们在百度搜索栏中输入We Are Happy就会看到地址栏里出现We%20Are%20Happy,在搜索链接里会将空格替换成%20,这是为什么呢?因为空格的ASCII码值是20。

在这里插入图片描述
当我们点击链接会发现这道题在牛客网是这样的,这种是接口型的OJ题目。
OJ题目分为两种:

  1. IO型:自己完成输入输出,包括 main 函数
  2. 接口型:只需要完成接口就可以了
    接口型的OJ题目要假定 main 函数已经有了,并且调用了replaceSpace(),传递了何时的参数。

做题思路:

  • char *str,int length 这句代码的意思是把 str 指向的字符串里的空格替换成20,str指向的字符串长度是length。
  • 一个空格要被替换成%20,就是一个字符要变成3个字符,所以字符本质上要往后挪,遇到一个空格就要增长2个字符,假设有 n 个空格,字符串的长度就等于nx2+原字符串的长度。
  • 给两个标记,end1 标记替换之前的字符串的最后一个元素,end2 标记替换之后的字符串的最后一个元素,在 end1 没有遇到空格之前,把 end1 标记的元素挪移到 end2 标记的位置之后,end1 和 end2 都向前挪动,当 end1 遇到空格后,end1 向前挪动一步,把 end2 标记的位置依次写入02%(从后往前赋值),在写的过程中,每写一个字符,end2 向前移动一步,循环这个过程,直到把所有空格替换完成,当 end1 和 end2 指向同一个元素时替换完成。
    在这里插入图片描述
#include <stdio.h>
#include <string.h>
void replaceSpace(char* str, int length) {
    int space_count = 0;//统计空格的个数
    char* cur = str;
    //*cur!=\0,开始遍历
    while (*cur)
    {
        if (*cur == ' ')
            space_count++;
        cur++;
    }
    //计算end1,end2
    char* end1 = str + length;
    char* end2 = str + length + 2 * space_count;
    while (end1 != end2)
    {
        if (*end1 != ' ')
        {
            *end2-- = *end1--;//后置--,先赋值再减
        }
        else
        {
            *end2-- = '0';//从后往前赋值
            *end2-- = '2';
            *end2-- = '%';
            end1--;
        }
    }
}
int main()
{
    char arr[40] = "We Are Happy";
    int len = strlen(arr);
    replaceSpace(arr, len);
    printf("%s\n", arr);
    return 0;
}

在这里插入图片描述

3. 找单身狗 💫

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字。

示例:

  • 输入数组的为:1 2 3 4 5 1 2 3 4 6
  • 输出:5 6

思路:
异或操作符^:相同为0;相异为1;异或操作符是支持交换律的。

a^a = 0;
0^a = 0;
1^3^1 = 3;
1^1^3 = 3;

找一个单生狗 1 2 3 4 5 1 2 3 4,用异或操作符就能找出单生狗是5了。
找两个单生狗1 2 3 4 5 1 2 3 4 6,我们这里就需要给他分组,把5和6分到两个组里,每一个组里只有一个单生狗,其余的都是成对的。例如:A组:1 2 1 2 5;B组:3 4 3 4 6;或者A组 1 3 1 3 5;B组 2 4 2 4 6。怎样才能实现以上的分组你呢?5的二进制位101,6的二进制位110;5^6 = 011,异或的结果为011,证明他们的最低为和第二位都不相同。5的最低位是1,6的最低位是0;我们就将最低位为1的分到A组,最低位为0的分到B组。或者用第二位分组也可以。
分组思想:

  1. 计算5和6二进制中的哪一位为1(第n位)
  2. 以第n位为标准,第n位为1的放一组,第n位为0的放一组。
#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
	//所有数字异或
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int ret = 0;
	for (i = 0; i < sz; i++)
	{
		ret ^= arr[i];
	}
	//ret 就是5^6的结果,二进制中一定有1
	//计算ret的第几位是1
	int pos = 0;
	for (i = 0; i < 32; i++)
	{
		if ((ret >> i) & 1 == 1)
		{
			pos = i;
			break;
		}
	}
	//ret的pos位是1
	//把arr数组中的每个元素的第pos位为1的数字异或在一起
	int num1 = 0;
	int num2 = 0;
	for (i = 0; i < sz; i++)
	{
		if (((arr[i] >> pos) & 1) == 1)
		{
			num1 ^= arr[i];
		}
		else
		{
			num2 ^= arr[i];
		}
	}
	printf("%d %d", num1, num2);
	return 0;
}

在这里插入图片描述

4. 模拟实现 atoi ✨

atoi 把字符串转换成对应的数字

atoi的使用:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int ret = atoi("123");
	printf("%d\n", ret);
	return 0;
}

在这里插入图片描述
模拟实现atoi

//模拟实现atoi会出现的情况
//my_atoi(NULL)//异常
//my_atoi("")//异常
//my_atoi("    +123")//正常
//my_atoi("-123")//正常
//my_atoi("123abc")//异常
//my_atoi("1111111111111111111111111")//异常
//my_atoi("-1111111111111111111111111")//异常

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
//枚举定义状态值
enum Status
{
	VALID,//合法
	INVALID //非法
};
enum Status status = INVALID;//定义一个全局的状态值为非法
//根据返回值判断是非法的还是合法的
int my_atoi(const char* str)
{
	if (str == NULL)
	{
		return 0;
	}
	if (*str == '\0')
	{
		return 0;
	}
	//空白字符
	while (isspace(*str))
	{
		str++;
	}
	int flag = 1;
	if (*str == '+')
	{
		flag = 1;
		str++;
	}
	else if (*str == '-')
	{
		flag = -1;
		str++;
	}
	//处理数字字符
	//123
	long long ret = 0;
	while (isdigit(*str))
	{
		ret = ret * 10 + flag * (*str - '0');
		if (ret<INT_MIN || ret>INT_MAX)//值越界了
		{
			return 0;
		}
		str++;
	}
	if (*str == '\0')
	{
		status = VALID; //定义一个局部的状态值为合法
		return (int)ret;
	}
	else
	{
		return (int)ret;
	}
}
int main()
{
	int ret = my_atoi("   -123");
	if (status == VALID)
		printf("合法转换:%d\n", ret);
	else
		printf("非法转换:%d\n", ret);
	return 0;
}

在这里插入图片描述

本章到这里就结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话请给个三连支持一下吧!
Fighting!!!✊

  • 49
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 40
    评论
评论 40
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值