C语言每日一题(四)
C语言作为嵌入式Linux开发的必备工具,作为嵌入式Linux开发的基础语言,那么在面试嵌入式工程师时C语言定是面试中的重中之重。作为一名大三的老学长,不得不为找工作做必要准备。每天做一道C语言面试题,为面试打基础。
C语言作为嵌入式Linux开发的必备工具,作为嵌入式Linux开发的基础语言,那么在面试嵌入式工程师时C语言定是面试中的重中之重 。作为一名大三的老学长,不得不为找工作做必要准备。每天做一道C语言面试题,为面试打基础。
本来还想着坚持每天一道面试题来准备找实习的,结果中间因为课设、电赛很多事情就给耽搁了发现距离上一次更新已经一个月过去了,决定重新拾起来继续开干。
2020.11.17
题目描述:
如果统计的个数相同,则按照ASCII码由小到大排序输出 。如果有其他字符,则对这些字符不用进行统计。
实现以下接口:
输入一个字符串,对字符中的各个英文字符,数字,空格进行统计(可反复调用),按照统计个数由多到少输出统计结果,如果统计的个数相同,则按照ASCII码由小到大排序输出,清空目前的统计结果,重新统计,调用者会保证:输入的字符串以‘\0’结尾。
输入描述:
输入一串字符。
输出描述:
对字符中的各个英文字符(大小写分开统计),数字,空格进行统计,并按照统计个数由多到少输出,如果统计的个数相同,则按照ASII码由小到大排序输出 。如果有其他字符,则对这些字符不用进行统计。
示例1
输入:
aadddccddc
输出:
dca
题目解析:
#include<stdio.h>
#include<string.h>
#define ASC_SIZE 128
int main()
{
char str[1000];
int i,len;
while(gets(str))
{
int count[ASC_SIZE]={0};
int max = 0;
len = strlen(str);
for(i=0;i<len;i++)
{
count[str[i]]++;
if(count[str[i]] > max)
max = count[str[i]];
}
while(max)
{
for(i=0;i<ASC_SIZE;i++)
{
if(count[i] == max)
printf("%c",i);
} max--;
}
printf("\n");
}
return 0;
}
当我见到题目的时候我也是比较懵逼的,没有想到比较好的解题方案,然后偷偷的打开了通过的代码,看到了高赞的代码。通过分析别人的代码发现了解题思路。这道题的思想类似于桶排序的思想,定义了一个拥有ASCII码长度的数组(count[ASC_SIZE]
)并且初始化为0,然后对输入的字符串进行检索。当遇到对应的字符时,对应下标为该字符的count['字符']
值加一,因为要按照字符个数从多到少的输出那么就可以定义一个max
变量用于统计出现次数最多的字符。
输出时,因为是要先输出出现次数最多的字符,如果次数相同则按照ASCII码的值从小到大进行输出。因此优先级最高的是max,遍历count数组输出对应的下标就是出现次数最多的字符,当然如果出现次数相同该怎么办呢?这就是这道题采用桶排序的巧妙之处,因为是采用桶排序的,下标按照从小到大存储的,我们就不需要对其进行排序,我们要做的只用关心这个地方的值是几就可以啦!当字符出现次数相同时,我们只需要将满足条件的下标给按照顺序输出就可以。
2020.11.18
题目描述:
有ABC三组,每组10名队员,求出ABC三组中成绩最高的前五名成绩并输出。
A={65,85,65,75,78,41,89,99,98,96};
B={65,85,65,75,78,41,89,99,91,92};
C={65,85,65,75,78,41,89,97,98,90};
输出描述:
A:99 98
B:99
C:98 97
题目解析:
#include<stdio.h>
#include<string.h>
#define ASC_SIZE 128
struct Data
{
int num_data;
char num_sub;
}data[30];
int sort(struct Data src[])
{
int x,y;
struct Data tem;
for(x=0;x<5;x++)
{
for(y=x+1;y<30;y++)
{
if(src[y].num_data>src[x].num_data)
{
tem=src[x];
src[x]=src[y];
src[y]=tem;
}
}
}
return 0;
}
int main()
{
int i;
int A[10]={65,85,65,75,78,41,89,99,98,96};
int B[10]={65,85,65,75,78,41,89,99,91,92};
int C[10]={65,85,65,75,78,41,89,97,98,90};
for(i=0;i<10;i++)
{
data[i].num_data=A[i];
data[i].num_sub='A';
}
for(i=10;i<20;i++)
{
data[i].num_data=B[i-10];
data[i].num_sub='B';
}
for(i=20;i<30;i++)
{
data[i].num_data=C[i-20];
data[i].num_sub='C';
}
sort(data);
printf("A:");
for(i=0;i<5;i++){
if(data[i].num_sub=='A')
printf("%d ",data[i].num_data);
}
printf("\nB:");
for(i=0;i<5;i++){
if(data[i].num_sub=='B')
printf("%d ",data[i].num_data);
}
printf("\nC:");
for(i=0;i<5;i++){
if(data[i].num_sub=='C')
printf("%d ",data[i].num_data);
}
return 0;
}
这道题目当时拿到题目的第一瞬间就是想用比较暴力的方法,把三个数组给合成一个数组,然后再对这个合成的数组进行排序输出前5个同学的成绩,但是看到题目的输出格式以后发现还要分组输出相关的信息,这个我用了一个比较复杂的方法就是结构体数组,结构体变量中存放了两个变量:一个是数组对应的值num_data
,另一个是对应的分组num_sub
,然后根据num_data来对结构体数组进行排序。因为只需要输出前5名学生的信息,采用冒泡排序外层循环只需要循环5次。然后就可以按照指定格式输出相关的信息了。
这道题肯定还有更好更优秀的方案能够降低时间复杂度和空间复杂度的方法,但是博主能力有限目前只想到了这一种方法。希望有那位大神看到这篇文章的时候可以在评论区分享更好的方法和思路。
2020.11.23
题目描述:
解密QQ号,题目是啊哈算法中队列篇中的。解密规则:首先将第 1 个数删除,紧接着将第 2 个数放到这串数的末尾,再将第 3 个数删除并将第 4 个数放到这串数的末尾,再将第 5 个数删除……直到剩下最后一个数,将最后一个数也删除。按照刚才删除的顺序,把这些删除的数连在一起就是小哈的 QQ 啦!
本题目旨在理解队列的基本操作以及简单应用,队列是一种特殊的线性结构,它只允许在队列的首部( head)进行删除操作,这称为“出队”,而在队列的尾部( tail)进行插入操作,这称为“入队”。当队列中没有元素时(即 head==tail),称为空队列。队列是广度优先搜索以及队列优化的Bellman-Ford 最短路算法的核心数据结构,队列的三个基本元素是:一个数组,两个变量。
题目解析:
#include <stdio.h>
struct queue
{
int data[100];
int head;
int tail;
};
int main()
{
struct queue q;
int i;
q.head=1;
q.tail=1;
for(i=0;i<9;i++)
{
scanf("%d",&q.data[q.tail]);
q.tail++;
}
while(q.head<q.tail)//当队列不为空的时候执行循环
{
printf("%d ",q.data[q.head]); //打印队首位置
q.head++;
//将新队首的数添加到队位
q.data[q.tail]=q.data[q.head];
q.tail++;
q.head++;
}
printf("\n%d %d",q.head,q.tail);
getchar();
return 0;
}
将引入两个整型变量 head 和 tail。 head 用来记录队列的队首(即第一位),tail 用来记录队列的队尾(即最后一位)的下一个位置。你可能会问:为什么 tail 不直接记录队尾,却要记录队尾的下一个位置呢?这是因为当队列中只剩下一个元素时,队首和队尾重合会带来一些麻烦。我们这里规定队首和队尾重合时,队列为空。
现在有 9 个数, 9 个数全部放入队列之后 head=1;tail=10;此时 head 和 tail 之间的数就是
目前队列中“有效”的数。如果要删除一个数的话,就将 head++就 OK 了,这样仍然可以保持 head 和 tail 之间的数为目前队列中“有效”的数。这样做虽然浪费了一个空间,却节省了大量的时间,这是非常划算的。新增加一个数也很简单,把需要增加的数放到队尾即 q[tail]之后再 tail++就 OK 啦。
2020.11.24
题目描述:
"xyzyx"是一个回文字符串,所谓的回文字符串就是指正读反读均相同的字符串序列,如“席主席”、“记书记”、“ahaha”均是回文字,通过栈这个数据结构很容易判断一个字符串是否为回文字符串,请输入一个字符串,判断这个字符串是否为回文字符串。
输入描述: 输入一个字符串
输出描述: 如果是回文字符串输出YES,不是回文字符串则返回NO。
示例:
输入:ahaha
输出:YES
题目解析:
#include <stdio.h>
#include <string.h>
int main()
{
char a[101],s[101];
int i=0,len,mid,next,top;
gets(a);
len=strlen(a);
mid=len/2-1;
top=0;//栈初始化
for(i=0;i<=mid;i++)
{
s[++top]=a[i];
}
if(len%2==0)
next=mid+2;
else
{
next=mid+1;
}
for(i=next;i<len;i++)
{
if(a[i]!=s[top])
break;
top--;
}
if(top==0)
printf("YES\n");
else
{
printf("NO\n");
}
}
2020.11.25
题目描述:
快速找到未知长度单链表的中间节点。
题目解析:
这是一道比较经典的面试题,普通方法很简单,就是遍历一遍单链表以确定单链表的长度L,然后再从头节点出发循环L/2次到中间节点。算法复杂度为O(3L/2)。
利用快慢指针原理,设置两个指针search和 mid分别指向链表的头节点。其中search的移动速度是mid移动速度的两倍,当*search指向末尾节点的时候,mid刚好在中间了。
Status GetMidNode(LinkList L,ElemType *e)
{
LinkList search,mid;
mid=serch=L;
while(serch->next!=NULL)
{
if(search->next->next!=NULL)
{
search=search->next->next;
mid=search->next;
}
else
{
search=search->next;
}
}
*e=mid->data;
return OK;
}
2020.11.26
题目描述: 如何判断一个链表是否为环?
题目解析: 判断一个链表是否为环,可能第一个想到的方法就是判断链表的最后一个节点是否与首节点相等,但是实际上这种方法是有局限性的。当遇到6字型的环形链表时,就可能会出现误判的情况,如果程序设计的不合理还很有可能陷入一个死循环。那么我们我们可以使用快慢指针法:就是让两个指针同时指向链表。在向后遍历的时候,一个指针每次走两步,称为快指针;一个指针每次走一步,称为慢指针。如果快慢指针相遇,则说明链表有环,否则无环。
int testLink(Link *head)
{
Link *t1 = head, *t2 = head;
while(t1->next && t2->next)
{
t1 = t1->next;
if (NULL == (t2 = t2->next->next))
return 0; //无环
if (t1 == t2)
return 1;
}
return 0;
}
2020.11.27
题目描述: 创建一个链表,并将链表逆置。
题目解析:
创建一个链表如下:
我们要将链表逆置,需要先定义两个结构体类型的指针变量*p (用于记录原链表的节点对应的data值)和 q(用于记录新链表头插的值),定义好节点以后将p指向data1,q指向p用来记录原链表和需要新插入的值,同时让原链表的头节点指向NULL。相当于重新建立一个链表。
当准备工作完成后就是重头戏了,通过一个while循环判断p节点所指向的下一个位置如果不为空,那么就将当前p节点的值赋值给q节点,赋值给q节点后,让q节点的下一个节点指向头结点的下一个节点,然后让头节点指向q节点。
通过以上步骤我们就完成了第一个节点头插入到新的链表中,完成后p向后移动一个节点继续重复以上步骤,就可以完成一个链表的逆置了。其代码核心就是将原链表头结点后面的节点顺序头插入到新的链表中。
#include<stdio.h>
#include<malloc.h>
typedef struct node
{
int data;
struct node*next;
}node;
node*creat()
{
node*head,*p,*q;
char ch;
head=(node*)malloc(sizeof(node));
q=head;
ch='*';
puts("单链表尾插法,?结束");
while(ch!='?')
{
int a;
scanf("%d",&a);
p=(node*)malloc(sizeof(node));
p->data=a;
q->next=p;
q=p;
ch=getchar();
}
q->next=NULL;
return(head);
}
void print(node*a)
{
puts("print ");
a=a->next;
while(a!=NULL)
{
printf("%d ",a->data);
a=a->next;
}
}
void reverse(node*head)
{
node*p,*q;
p=head->next;
head->next=NULL;
while(p)
{
q=p;
p=p->next;
q->next=head->next;
head->next=q;
}
}
main()
{
node*a;
a=creat();
print(a);
reverse(a);
puts("\nhaved reversed:");
print(a);
return 0;
}
2020.11.28
题目描述:
使用递归将一个字符串进行翻转。
输入描述:
输入一个字符串。
示例:
输入:hello world !
输出:! dlrow olleh
题目解析:
通过定义一个str_reverse(char *p1,char *p2)函数,传入的参数分别为字符串的首地址和尾地址。如果需要交换的字符的首地址和尾地址重合那么就结束当前函数,如果不重合就进行交换。这里并没有定义一个中间变量来进行交换两个变量,而是通过数学运算得到的(交换a,b:a=a+b;b=a-b;a=a-b;)。交换完成后判断是否为最后两个交换的数,如果是最后两个交换的数那么就结束当前的递归函数。
#include<stdio.h>
#include<string.h>
//#define SPACE ' '
//#define ENDL '\0'
void str_reverse(char *p1,char *p2)
{
if(p1==p2) return;//如果需要交换的只有一个字符
*p1=(*p1)+(*p2);
*p2=(*p1)-(*p2);
*p1=(*p1)-(*p2);
if(p1==p2-1)return;//交换后判断是否是最后两个交换的,如果是则结束程序避免再交换
else str_reverse(++p1,--p2);
}
int main()
{
char str1[100];
int len;
gets(str1);
len=strlen(str1);
str_reverse(str1,str1+len-1);
puts(str1);
return 0;
}
2020.11.30
交换两个参数的宏定义为:#define SWAP(A,B) (A)=(A)+(B);(B)=(A)-(B);(A)=(A)-(B);
2020.12.01
题目描述:
写一个函数实现将内存中的字符串进行翻转。
输入描述:
输入一个字符串。
示例:
输入:The quick brown fox jumps over the lazy dog
输出:dog lazy the over jumps fox brown quick The
题目解析:
这道题目比较方便的解法就是分为两大步骤,第一步:将输入的字符串进行整体的翻转;第二步:将整体翻转后的字符串对应的单词进行翻转。
#include<stdio.h>
#include<string.h>
#define SPACE ' '
#define ENDL '\0'
void str_reverse(char *p1,char *p2)
{
if(p1==p2) return;//如果需要交换的只有一个字符
*p1=(*p1)+(*p2);
*p2=(*p1)-(*p2);
*p1=(*p1)-(*p2);
if(p1==p2-1)return;
else str_reverse(++p1,--p2);
}
void word_reverse(char *str)
{
//q2用来记录需要交换的单词的尾地址
char *q1=str,*q2=str,*t;//q1用来记录字符串中,需要交换单词的起始地址。
while(*q1==SPACE)q1++;
if(*q1==ENDL)return;//如果结束标志则结束函数
else q2=q1+1;
while((*q2!=SPACE)&&(*q2!=ENDL))q2++;
t=q2--;//t用来记录下一个单词的起始地址
str_reverse(q1,q2);//交换这个单词的顺序
if(*t==ENDL)return;//交换后判断是否结束
else word_reverse(t);
}
int main()
{
char str1[100];
int len;
gets(str1);
len=strlen(str1);
str_reverse(str1,str1+len-1);
puts(str1);
word_reverse(str1);
puts(str1);
return 0;
}
不积小流无以成江河,不积跬步无以至千里。而我想要成为万里羊,就必须坚持学习来获取更多知识,用知识来改变命运,用博客见证成长,用行动证明我在努力。
如果我的博客对你有帮助、如果你喜欢我的博客内容,记得“点赞” “评论” “收藏”一键三连哦!听说点赞的人运气不会太差,每一天都会元气满满呦!如果实在要白嫖的话,那祝你开心每一天,欢迎常来我博客看看。
更多推荐
所有评论(0)