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

主页:醋溜马桶圈-CSDN博客

专栏:C语言_醋溜马桶圈的博客-CSDN博客

gitee:mnxcc (mnxcc) - Gitee.com

目录


1.三目运算符的使用

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

1.1 if-else语句

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

#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 三目运算符

#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

#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

#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函数

void reverse(char* left,char* right){while (left<right) {char tmp=*left;*left=*right;*right=tmp;left++;right--;}
}

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

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

可以看主函数的代码理解 

3.3.2 主函数

#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 代码示例

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

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

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语句来处理这个问题

if(numsLen==0){return 0;}
else if (nums[numsLen-1]<k||nums[0]>k) {return 0;}

5.2.1 遍历数组方法

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 代码示例

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

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;elsereturn mid;}return left;
}

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

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

我们编写出下面这段代码

int maxProfit(int* prices, int pricesLen ) {// write code hereint 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 代码示例

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

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;}elsereturn mid;mid=(left+right)/2;}return -1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/572041.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4个AI智能写作平台,为你解决文章写作问题

在信息时代&#xff0c;写作是一项重要的技能&#xff0c;无论是在学术领域还是商务环境中。然而&#xff0c;对于许多人来说&#xff0c;写作并不是一件轻松的事情。但是&#xff0c;随着人工智能的发展&#xff0c;有许多AI智能写作平台可以帮助我们解决文章写作问题。在本文…

开放大学2024年春《数控技术 060253》综合大作业参考答案

答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 答案&#xff1a;更多答案&#xff0c;请关注【电大搜题】微信公众号 单选题 1数控系统的核心是&#xff08; &#xff09; …

#Linux系统编程(关于解决Source Insight自动补全的问题)

&#xff08;一&#xff09;发行版&#xff1a;Ubuntu16.04.7 &#xff08;二&#xff09;记录&#xff1a; &#xff08;1&#xff09;参考博文 Source Insight 4.0 添加函数库的头文件实现函数的自动补齐 - 简书 (jianshu.com)https://www.jianshu.com/p/96595eefb988 &am…

SQLAlchemy模型映射提示declarative_base() takes 0 positional arguments but 1 was given

原码&#xff1a; #SQLAlchemy模型映射表结构. from sqlalchemy import create_engine,Column,Integer,String from sqlalchemy.ext.declarative import declarative_base# 数据库的变量 HOST 127.0.0.1 PORT 3306 DATA_BASE itbz USER root PWD 123456 DB_URL fmysqlpy…

微服务架构学习汇报PPT

所有关于微服务架构的知识也好&#xff0c;经验也罢&#xff0c;不一定适合每个希望做微服务系统的技术人员的实际需求。“道无常道&#xff0c;法无常法&#xff0c;君子审时度势&#xff0c;自可得而法”。实际项目里需要做哪些工作&#xff0c;采取哪些策略&#xff0c;先后…

留存测试数据,Apipost接口用例详解

接口用例可以在不影响源接口数据的情况下对接口添加多个用例&#xff0c;方便测试并保存测试数据。 创建用例 左侧目录选择接口后进入接口用例页面&#xff0c;点击添加用例 在弹出窗口中修改各种参数。如登录接口&#xff0c;可修改用户名为空&#xff0c;并添加断言。 执行…

实现ls -l 功能,index,rindex函数的使用

index(); rindex();----------------------------------------------------------------- index第一次遇到字符c&#xff0c;rindex最后一次遇到字符c&#xff0c;返回值都是从那个位置开始往后的字符串地址 #include <stdio.h> #include <sys/types.h> #include &…

CentOS7.7安装XIAOJUSURVEY问卷系统

XIAOJUSURVEY问卷系统&#xff0c;适用于市场调研、客户满意度调研、在线考试、投票、报道、测评等众多场景。数据能力上&#xff0c;经过上亿量级打磨&#xff0c;沉淀了分题统计、交叉分析、多渠道分析等在线报表能力&#xff0c;快速满足专业化分析。 一、在CentOS7.7上安装…

01使用调试工具

文章目录 前言一、用openocd打开单片机二、利用4444端口向单片机写入hex文件三、利用3333端口和gdb进行调试四、之前我出的问题总结 前言 之前写了一篇关于在linux下搭建stm32标准库的文章后&#xff0c;有一些小伙伴们还是出现了一些奇奇怪怪的错误&#xff0c;这一篇文章就是…

160.乐理基础-等和弦与一些做题问题

如果到这五线谱还没记住还不认识的话去看102.五线谱-高音谱号与103.五线谱-低音谱号这两个里&#xff0c;这里面有五线谱对应的音名&#xff0c;对比着看 如果一章没落下&#xff0c;看到这里&#xff0c;但是看不懂什么意思&#xff0c;那就强行下看&#xff0c;看着看着指不…

git下载安装教程

git下载地址 有一个镜像的网站可以提供下载&#xff1a; https://registry.npmmirror.com/binary.html?pathgit-for-windows/图太多不截了哈哈&#xff0c;一直next即可。

外包干了5年,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…