C语言每日亿题(一)

一.统计匹配检索规则的物品数量

原题传送门力扣

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

int countMatches(char *** items, int itemsSize, int* itemsColSize, char * ruleKey, char * ruleValue){
}

items[i] = [typei, colori, namei],首先我们看一下数组里的内容一般一个数组里的元素不止一种的时候C语言里基本只有结构体二维数组这两种情况。但是仔细看items[i]里面的内容可以判断items它应该是一个二维数组:
在这里插入图片描述
我们再来看条件:
在这里插入图片描述
也就是说这里会给你两个字符串:ruleKey,ruleValue.
假如如果ruleKey里面的值是字符串"type",我们就判断数组items[i]里面typei那一块的字符串,如果这个字符串和ruleValue的值相等,就说明它满足条件,否则就不满足。
同理ruleKey里面的值是字符串"color",“name”,我们都要从数组的colori,namei这一块里面判断。

现在思路就可以有了,我们可以先判断ruleKey里面的内容是什么,然后再通过for循环判断二维数组里的每一个数组该块的内容(二维数组的每一行可以当成一个一维数组来判断)。假如ruleKey=“type”,我们只用遍历红方框那一块的内容就行。
在这里插入图片描述
我们在遍历一个二维数组的时候一般会用两个for循环,这里我们可以固定死循环里面的内容,然后通过函数strcmp比较即可。

int countMatches(char *** items, int itemsSize, int* itemsColSize, char * ruleKey, char * ruleValue){
	//通过if判断我们需要遍历一维数组的第几个元素
    int j = 0;
    if(strcmp(ruleKey, "type") == 0)
        j = 0;
    else if(strcmp(ruleKey, "color") == 0)
        j = 1;
    else
        j = 2;
 
 	//再通过for循环看该数组的这个位置上的字符串是否和ruleValue相等
 	//如果相等定义的count++
    int count = 0;
    for(int i=0; i<itemsSize; i++)
    {
        if(strcmp(items[i][j], ruleValue) == 0)
        {
            count++;
        }
    }
    return count;
}

这一题的知识点主要是二维数组的理解和函数strcmp的应用。

二维数组:我们可以把它当成一个一维数组,这个数组里面的元素又是一个一维数组。
一个数组items,因为数组名是首元素地址,所以*(items+i)可以找到items里的第i个元素。所以可以理解为 * (items+i) == items[i]。同理items[ i ][ j ]可以表示 * (* (items+i)+j)。items[ i ][ j ]找到的就是这个二维数组第i行第j列的元素。
还有的人可能会问函数的第一个形参为啥有三个星号?
这也好理解,二维数组里面的每个元素都是一维数组,而一维数组里面的元素虽然存的是字符串,但实际上存的是这个字符串的地址,也就是char类型的,而外面的一维数组名字是首元素地址也就是字符串地址的地址,换句话说这个二维数组其实是一个存着一个个二级指针的一维数组。当然二维数组的数组名也是首元素地址,所以二维数组数组名items就代表着一个字符串地址的地址的地址。形参接收用三个也很正常。

还有字符串的比较可不能和整型那些的相提并论,我们要用函数strcmp才可以比较.strcmp(str1,str2)。如果str1<str2函数返回小于0的数,如果str1>str2返回大于0的数,如果str1=str2则返回0.

二.替换空格

原题传送门力扣

题目概述
在这里插入图片描述
目的就是为了把一个字符串里的所有空格替换成%20。在这里我们需要开辟一块新的空间,因为只要它存在一个空格,整个字符串的长度必然比之前大,如果你不开辟新的空间,或者没有增加你原来空间的大小,肯定会造成越界访问的。

在这里我们用malloc函数来开辟一块动态空间。我们发现字符串每有一个空格,长度就要+2.我们可以遍历一遍数组,计算出空格的个数,然后在用strlen函数计算我们原有的字符串长度。这样我们开辟的空间大小就是原字符串长度+空格数*2+1.这里记着一定要+1,因为如果不加的话你空间虽然可以装下你的那块空间但是每个字符串末尾都会有一个\0,我们不加1,这个\0就会造成越界访问。

开辟好空间后我们通过字符串函数strcpy将原来的字符串拷贝到新开辟的字符串中。接下来考虑的问题就是如何移动了。
在这里插入图片描述
通过两幅图我们希望把原来字符串末尾往后移动几位,现在我们可以定义两个指针,第一个指针指向原来字符串末尾的位置也就是指向字符y。第二个指针指向全部添加完后那个字符串的末尾,也就是字符y后面加4个位置(因为这里有两个空格,所以加2*2)。
在这里插入图片描述

我们从后往前找,分为两种情况:
第一种:如果e1指向的位置不是空格,此时我们直接把e1指向的内容赋值给e2指向的内容。这样就达到移动的目的了。然后没赋值一次,两个指针–往前移动一步。

第二种:如果e1指向了空格:
在这里插入图片描述
因为空格都会被替换成%20,也就是说h前面的三个位置填的应该就是%20,我们就可以让e2连续往前走3步,每步都赋值%20的一个元素,最后变成了:
在这里插入图片描述

然后在放完%20后,%前面肯定要放空格之前的元素,所以在e2向前走三步后,e1在往前走一步:
在这里插入图片描述
后面的步骤就和前面的一样了,现在还有最后一个小问题,我们一步一步的走,肯定要放在一个循环里面吧,但是这个循环的终止条件是什么呢?我们仔细观察可以发现e1,e2之间再替换空格之前相差4个元素,替换一个后减了2个。也就是说没替换一次空格e1,e2之间相差的举例-2,那是不是可以讲当e1==e2时跳出来就行?

思路出来了,我们就可以写代码了:

char* replaceSpace(char* s)
{
    int count = 0;
    //计算字符串长度
    int len = (int)strlen(s);
    //计算空格个数
    for (int i = 0; i < len; i++) 
    {
        if (s[i] == ' ') 
        {
            count++;
        }
    }

    //将新开辟的空间地址传给指针tmp
    char* tmp = (char*)malloc((len + count * 2) * sizeof(char) + 1);
    if (tmp == NULL)
        exit(-1);

    //将原字符串内容拷贝到新开辟的空间中
    strcpy(tmp, s);

    //定义两个指针
    char* e1 = tmp + len ;
    char* e2 = tmp + len + count * 2;

    while (e1 != e2)
    {
        if (*e1 != ' ')
        {
            *e2-- = *e1--;
        }
        else
        {
            *e2-- = '0';
            *e2-- = '2';
            *e2-- = '%';
            e1--;
        }
    }
    return tmp;
}

现在还有最最后一个小问题,我们观察一下刚才写的思路,可以发现在通过两个指针来移动时,记得把原来字符串的’\0’也考过去,也就是说e1刚开始指向的其实是\0,上面为了好观察我就没写。

这一题也没用什么知识点,没有感情全是技巧。但是像malloc函数,可能有人不知道。还有一个strcpy,strlen函数。

malloc是开辟一块动态内存空间用到的函数。返回值是void*,也就是返回的是你开辟的那块空间的地址,如果你希望这块空间里的元素都是char类型的,你就把void强制类型转换成char,然后赋值给一个char*类型的指针。malloc函数的参数就一个,你需要开辟空间的大小,单位是字节。
如果对动态内存空间开辟这块比较感兴趣可以看看这个人写的文章C语言动态内存管理

strcmp(str1,str2);是将str2字符串的内容拷贝到str1中。
strlen(str);可以计算str这个字符串的字符个数,不包括\0.
当然strcpy,strlen和第一题的strcmp函数,这个人都有写,我的天,他好强。字符库函数(一)字符库函数(二)

三.斐波那契数列

原题传送门力扣

题目概述
在这里插入图片描述
如果知道函数可以递归,这题可以立马写出来

int fib(int n)
{
	if (n < 2)
		return n;
	return (fib(n - 1) + fib(n - 2)) % 1000000007;
}

模上1000000007是题目的要求,但是这样写真的行得通吗?
我们可以稍微估算一下n=50,发现这个函数要算50之前,肯定要知道fib(48),fib(49),只有知道他俩的值才能算出结果,但是fib(48)要知道fib(46),fib(47)。fib(49)要知道fib(47),fib(48)。就这样,一直往下运行,这个函数运行的次数是不是类似2^n了,这可是一个指数啊,2 ^ 50次方可是一个相当大的数了。我们算n=50就这么麻烦,那n=60,70呢?岂不是跑好长时间都算不出来?

所以在这里我们换一种方法:
在这里插入图片描述
此时前两个数是固定的,依然不用管,假如现在我们想算到55,也就是n=10。现在从n=2开始慢慢往后推:
在这里插入图片描述
要算n=2也就是c的值,是不是可以这样写:c=a+b.
现在我们在知道第三个数的基础上,计算n=3的值,我们可不可以把刚才算的b的值赋给a,c的值赋给b?也就是b在原来c的位置上,a在原来b的位置上。
在这里插入图片描述
就相当于a,b各自往前走一步,这时候在把a+b的值赋给c
在这里插入图片描述
现在c的值是不是就是n=3时候的值了?同理,n=4,n=5就是一直循环就可以了?
我们最后的问题就是要循环几次。想一下,在计算n=2的时候将c=a+b赋值一次,n=3的时候,在n=2的基础上c再被赋值一次,也就是2次。所以循环的次数就是n-1次。

我们来看代码:

int fib(int n){
    if(n<2)
    {
        return n;
    }

    int a = 0;
    int b = 1;
    long long sum = 1;

    for(int i = 0; i < n - 1; i++)
    {
        sum = (a + b) % 1000000007;
        a = b;
        b = sum;
    }
    return sum;
}

因为n的取值范围是0-100也就是说sum必须要足够大,但是我们发现long long再后来也会存不下,但是题目要求过结果要%1000000007,也就是不能大于1000000007,所以我们在没计算一次a+b就取一次模。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值