前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C语言】C语言基础习题详解(牛客网)&&二分查找逻辑

【C语言】C语言基础习题详解(牛客网)&&二分查找逻辑

作者头像
用户10925563
发布2024-06-04 13:58:41
780
发布2024-06-04 13:58:41
举报
文章被收录于专栏:c/c++&&linuxc/c++&&linux

1.三目运算符的使用

三目运算符,即a>b?a:b类型的,很多时候适当的使用三目运算符可以使得代码更简洁有序,减小代码的复杂程度,接下来的例子就可以很明显的展示三目运算符的作用

1.1 if-else语句

使用if-else语句来编写代码,如下

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int x=0;
    int y;
    scanf("%d",&x);
    if(x<0){
        y=1;
        printf("%d",y);
    }
    else if (x==0) {
        y=0;
        printf("%d",y);
    }
    else {
        y=-1;
        printf("%d",y);
    }
}

1.2 三目运算符

代码语言:javascript
复制
#include <stdio.h>
int main()
{
    int x,y;
    scanf("%d",&x);
    x>0?(y=-1):(x==0?(y=0):(y=1));
    printf("%d",y);
}

三条if语句即用一条代码代替了,这也是之前文章中提到的编程思维的体现,作为程序员,写程序不是为了简单的运行成功,而是要条理清晰的写出代码,让每一步的运行都有据可循,这样也能减少代码的出错率

2.求两个正整数的最小公倍数

2.1 题目描述

牛客网中的题目描述是这样的

题目链接:求最小公倍数__牛客网

2.2 题目分析

假设两个正整数a,b;

  • 最小公倍数,最小也是这两个数中的较大的一个

思路

我们可以定义一个变量,变量从这个较大的值开始,看能不能整除这两个数,如果不行,那就+1继续判断,如果不行就继续+1判断,直到可以整除这两个数,则返回最后这个数

2.3 代码(头脑风暴)

2.3.1 代码1
代码语言:javascript
复制
#include <stdio.h>

int main() {
    long a=0;
    long b=0;
    scanf("%ld %ld",&a,&b);
    long max=a>b?a:b;
    while(max%a!=0||max%b!=0){
        max++;
    }
    printf("%ld",max);
    return 0;
}

顺着这样的逻辑,我们可以写出这样的代码,看似没有问题,但是当遇到非常大的数字的时候,这个算法就显得很复杂,并不能在规定时间内运行,就像这样

究其原因,是因为我们一个一个试数字,这样的方法其实是最耗费时间的;

那有没有更快的算法呢?答案是肯定

2.3.2 代码2

我们假设存在一个数字m,同时能整除a和b;假设m/a=i,m/b=j;

i的取值肯定是从1开始的,假设我们得到一个i值,这个i*a能整除b,那就说明i*a就是最小公倍数

我们发现,在代码1的逻辑中,我们求最小公倍数用了10次循环; 在这个算法中,我们只用了5次循环便找出了最小公倍数,速度提升了很多

那么我们就顺着这个逻辑写我们的代码2

代码语言:javascript
复制
#include <stdio.h>
int main() {
    long a=0;
    long b=0;
    scanf("%ld %ld",&a,&b);
    long i=1;
    while(1){
        if((i*a)%b==0){
            break;
        }
        i++;
    }
    printf("%ld",i*a);
    return 0;
}

这个时候我们发现,代码顺利通过了

可见我们这个逻辑是行得通的,至此,我们的最小公倍数问题就求解成功了

2.3.3 思考:为什么要用long?

我们发现,题目的输入描述范围是1-100000

而int的表示范围有限,我们通过实践发现,用int型并不能很好的实现

3.倒置字符串

3.1 题目描述

题目链接:倒置字符串__牛客网

3.2 题目分析

思考一下,我们可以分为两步

  • 第一步,将整个字符串逆序
  • 第二步,把逆序后的每个单词再逆序

或者我们可以:

  • 第一步,逆序每个单词
  • 第二步,再逆序整个字符串

  • 逆序字符串,需要告诉字符串的起始位置和结束位置
  • 逆序单词,同样需要告诉单词的起始位置和结束位置

这两种算法思维都是可以的,那我们实践一下

3.3 代码

我们可以封装一个reverse函数来进行字符串逆序

实现逻辑是这样的

3.3.1 reverse函数

代码语言:javascript
复制
void reverse(char* left,char* right){
    while (left<right) {
    char tmp=*left;
    *left=*right;
    *right=tmp;
    left++;
    right--;
    }
}

逆序整个字符串,调用这个函数,逆序单词同样可以调用这个函数

用while循环,当开始指针遇到空格或者'\0'的时候就停止;没有遇到空格或者'\0'的时候,则是一个单词,逆序这个单词

可以看主函数的代码理解

3.3.2 主函数
代码语言:javascript
复制
#include <stdio.h>
void reverse(char* left,char* right){
    while (left<right) {
    char tmp=*left;
    *left=*right;
    *right=tmp;
    left++;
    right--;
    }
}
int main() {
    char arr[101]={0};
    gets(arr);
    //逆序整个字符串
    int len=strlen(arr);
    reverse(arr,arr+len-1);
    //逆序每个单词
    char* cur=arr;
    while(*cur){
        char* strat=cur;
        while (*cur!=' '&&*cur!='\0') {
            cur++;
        }
        char* end=cur-1;
        reverse(strat, end);
        if(*cur==' ')
        cur++;
    }
    printf("%s\n",arr);
    return 0;
}

这样,我们这个问题就解决了

3.3.3 为什么使用gets()接收字符串呢?

因为scanf()接收字符串,遇到空格就停止不会继续往后读取了

4.二维数组中的查找

二维数组中的查找,这是剑指offer中的一道数组方面的题目

牛客网中也有同样的题目

4.1 题目描述

4.2 题目分析

我们在把这个二维数组用图表示出来

​ 4.2.1 二维数组中数字7的查找

由题目可知,每一行的数字是从左向右增大的,每一列的数字是从上到下增大的,即

首先,我们选取数组右上角的数字9,由于9大于7,并且9是第四列第一个(也是最小的)数字,因此7不可能出现在数字9所在的列。于是,我们把这一列从需要考虑的区域内剔除,之后只需要分析剩下的3列。

在剩下的矩阵中,位于右上角的数字是8,同样8大于7,因此8所在的列我们也可以剔除。接下来我们只要分析剩下的两列即可。

在剩余两列组成的数组中,数字2位于数组的右上角。2小于7,那么要查找的7就可能出现在2的右边和下边,而在前两步中,我们已经排除了2右边的列,也就是说7不可能出现在2的右边,只有可能出现在7的下边。于是我们把2所在的行也剔除,只分析剩下的三行两列数字。

在剩下的数字中,数字4位于右上角,和前面一样,我们把数字4所在的行也剔除,最后只剩下两行两列数字。

在剩下的两行两列中,位于右上角的数字刚好就是我们要查找的数字7,于是查找过程结束。

用下图表示

4.2.2 二维数组中数字的查找规律

首先选取数组中右上角的数字。如果该数字等于要查找的数字,则查找过程结束;

如果该数字大于要查找的数字,则剔除这个数字所在的列;如果该数字小于要查找的数字,则剔除这个数字所在的行。

也就是说,如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。

4.3 代码示例

把整个查找过程分析清楚之后,我们再写代码就不是一件很难的事情了。

下面是关于该思路的代码:

代码语言:javascript
复制
bool Find(int target, int** array, int arrayRowLen, int* arrayColLen) {
	int iRow = 0;
	int iCol = 0;
	char bIsFind = false;
	if (NULL == array) 
		return false;
	iRow = 0;
	iCol = *arrayColLen - 1;
	while (iCol >= 0 && iRow < arrayRowLen) {
		if (target == array[iRow][iCol]) {
			bIsFind = true;
            break;
		}
		else if (target <= array[iRow][iCol]) {
			iCol--;
		}
		else {
			iRow++;
		}
	}
	return bIsFind;
}

5.数字在升序数组中出现的次数

这道题可以用遍历数组二分查找来处理

5.1 题目描述

5.2 题目分析

题目中有一个关键信息,非降序数组,我们可以使用if语句来处理这个问题

代码语言:javascript
复制
if(numsLen==0){
        return 0;
    }
else if (nums[numsLen-1]<k||nums[0]>k) {
        return 0;
    }
5.2.1 遍历数组方法
代码语言:javascript
复制
int GetNumberOfK(int* nums, int numsLen, int k ) {
    int count = 0;
    if(numsLen==0){
        return 0;
    }
    else if (nums[numsLen-1]<k||nums[0]>k) {
        return 0;
    }
    for(int i=0;i<numsLen;i++){
        if (k==nums[i]) {
            count++;
        }
    }
    return count;
}

遍历数组的方法应该是最直接有效的,当k出现一次,则count自增,最终返回count的值即是k出现的次数

5.2.2 二分查找方法

二分查找是我们经常使用的一种算法,他的逻辑是

升序或者降序无重复元素的数组中,比较目标值和数组中间值的方法,每次缩小一半的搜索范围,相比遍历可以加快计算的速度

假设:目标值为下标为4的数值,给定一个大小为10的数组,我们给定他的下界left=0,上界right=numsLen-1,中间下标mid=(left+right)/2

二分查找:

  • 判断目标值target是否等于num[mid];
  • 如果相等则返回mid

如果不相等则判断target与num[mid]的大小关系;

  • target>num[mid];则说明目标值在后半部分,因为mid与目标值不相等,那么left就变成mid+1;
  • target<num[mid];则说明目标值在前半部分,因为mid与目标值不相等,那么right就变成mid-1;

每次缩小范围后都需要继续执行上述步骤,我们可以使用一个while循环,当left<right的时候循环,直到找到目标值对应的下标,返回下标;或者没有目标值对应的下标,返回-1;

5.2.3 代码示例

按照这个思路,我们编写一下我们的代码

代码语言:javascript
复制
int position(int* data, int n, double k){
    int left = 0, right = n-1, mid = 0;
    while(left <= right){
        mid = (left + right)/2;
        if(data[mid] < k)
            left = mid + 1;
        else if(data[mid] > k)
            right = mid - 1;
        else
            return mid;
        }
    return left;
}

当然,这是二分查找的代码,根据题目要求,我们调用这个函数

代码语言:javascript
复制
int GetNumberOfK(int* nums, int numsLen, int k ){
    return position(nums,numsLen, k+0.5) - position(nums,numsLen, k-0.5);
}

找到比k小的第一个数作为左边界,找到比k大的第一个数作为右边界,右-左即k的个数

按普通找某个数的位置来找,只是把int 改为double, 找k-0.5和k+0.5

6.BM80 买卖股票的最好时机(一)

6.1 题目描述

题目链接:买卖股票的最好时机(一)_牛客题霸_牛客网 (nowcoder.com)

6.2 题目分析

我们看到这个题目中一个数组表示每一天的股价,那么最大利润怎么算呢,我们用图片来表示

假设这是我们输入的数组

根据题目的意思,我们需要在股价最低的时候买入,在股价最高的时候抛出;但是这有一个问题,我们只能在买入当天以后的股价中找利润最大的,如果没有最大的,那就返回0;我们推理一下

  • 第一天:最小值8 利润:8-8=0
  • 第二天:最小值8 利润:9-8=1
  • 第三天:最小值2 利润:2-2=0
  • 第四天:最小值2 利润:4-2=2
  • 第五天:最小值2 利润:10-2=8
  • 第六天:最小值1 利润:1-1=0
  • ......
  • 依次递推即可知道:最大利润值即为8

6.3 编写代码

根据这个逻辑,写代码的时候我们需要初始化最低股价min,最大效益maxProfit,当前股价price

  • 当前股价我们需要假设每一天的情况,所以我们用for循环遍历数组实现
  • 最低股价我们假设数组的第一个元素是最低股价,然后与当前股价比较得出最低股价
  • 每一天的最大利润是当前股价减去最低股价
  • 比较每天的最大利润,得出所有最大利润中最大利润返回
  • 没有利润则返回0

我们编写出下面这段代码

代码语言:javascript
复制
int maxProfit(int* prices, int pricesLen ) {
    // write code here
    int min=*prices;
    int maxProfit=0;//初始化最大利润
    int price=0;//初始化当前股价
    for(int k=0;k<pricesLen;k++){
        price=prices[i];
        min=min<price?min:price;//求出当前股价之前的最低股价
        maxProfit=maxProfit<(price-min)?(price-min):maxProfit;
    }
    return maxProfit;
}

7.二分查找逻辑

7.1 二分查找

二分查找是我们经常使用的一种算法,他的逻辑是

升序或者降序无重复元素的数组中,比较目标值和数组中间值的方法,每次缩小一半的搜索范围,相比遍历可以加快计算的速度

7.2 查找逻辑

假设:目标值为下标为4的数值,给定一个大小为10的数组,我们给定他的下界left=0,上界right=numsLen-1,中间下标mid=(left+right)/2

判断目标值target是否等于num[mid];

  • 如果相等则返回mid

如果不相等则判断target与num[mid]的大小关系;

  • target>num[mid];则说明目标值在后半部分,因为mid与目标值不相等,那么left就变成mid+1;
  • target<num[mid];则说明目标值在前半部分,因为mid与目标值不相等,那么right就变成mid-1;

每次缩小范围后都需要继续执行上述步骤,我们可以使用一个while循环,当left<right的时候循环,直到找到目标值对应的下标,返回下标;或者没有目标值对应的下标,返回-1;

7.3 题目练习

我们找到一个题目来练习一下

7.3.1 题目描述

牛客网的题目链接: 二分查找-I_牛客题霸_牛客网 (nowcoder.com)

7.3.2 代码示例

根据二分查找的逻辑,我们可以写下代码:

代码语言:javascript
复制
int search(int* nums, int numsLen, int target ) {
    if(nums==NULL){
        return -1;
    }
    int left=0;
    int right=numsLen-1;
    int mid=(left+right)/2;
    while (left<=right) {
        if (nums[mid]>target) {
            right=mid-1;
        }
        else if (nums[mid]<target) {
            left=mid+1;
        }
        else
        return mid;
        mid=(left+right)/2;
    }
    return -1;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.三目运算符的使用
    • 1.1 if-else语句
      • 1.2 三目运算符
      • 2.求两个正整数的最小公倍数
        • 2.1 题目描述
          • 2.2 题目分析
            • 2.3 代码(头脑风暴)
              • 2.3.1 代码1
              • 2.3.2 代码2
              • 2.3.3 思考:为什么要用long?
          • 3.倒置字符串
            • 3.1 题目描述
              • 3.2 题目分析
                • 3.3 代码
                  • 3.3.1 reverse函数
                  • 3.3.2 主函数
                  • 3.3.3 为什么使用gets()接收字符串呢?
              • 4.二维数组中的查找
                • 4.1 题目描述
                  • 4.2 题目分析
                    • 4.2.2 二维数组中数字的查找规律
                  • 4.3 代码示例
                  • 5.数字在升序数组中出现的次数
                    • 5.1 题目描述
                      • 5.2 题目分析
                        • 5.2.1 遍历数组方法
                        • 5.2.2 二分查找方法
                        • 5.2.3 代码示例
                    • 6.BM80 买卖股票的最好时机(一)
                      • 6.1 题目描述
                        • 6.2 题目分析
                          • 6.3 编写代码
                          • 7.二分查找逻辑
                            • 7.1 二分查找
                              • 7.2 查找逻辑
                                • 7.3 题目练习
                                  • 7.3.1 题目描述
                                  • 7.3.2 代码示例
                              领券
                              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档