几个面试算法题,附源码
来源:互联网 发布:网络通信加密软件 编辑:程序博客网 时间:2024/04/26 05:56
昨天下午面试了一家,最后有道算法题,当时没想起来,就拍了张照,回来慢慢想,也算没白去。
原题 : 有N个人围成一圈,顺序排号。从第一个人开始报数(1到3),凡是报到3的人推出圈子,问最后留下的是原来第几号的那位?
先说下当时的思路,当时以为留下的人是有规律的,就想用数学归纳法,找到这个规律,但是下面写了结果,发现并没有什么规律。
n=1 , 留下1 ; n=2 , 留下2 ; n=3 , 留下2 ; n=4 , 留下1 ; n=5 , 留下4 ;
n=6 , 留下1 ; n=7 , 留下4 ; n=8 , 留下7 ; n=9 , 留下1 ; n=10 , 留下4 ;
数学归纳法的方法不行。那就试试走程序吧。
经过2个小时的调试,终于出了结果,下面是源码,结合注释看,
#include <stdio.h>void fun(int n){ int a[n]; int i = 0 ; for(i=0;i<n;i++){ a[i]=i+1; printf("%d\n",a[i]); } int m=0;//用来数123 int k=0;//k记录移动到的a数组下标,k总是有效的数组下标, int j=0;//记录a数组中大于0的个数,如果只剩一个说明游戏结束。 while(1){ m++; printf("当前的m=%d 对应的元素 %d\n",m,a[k]); if(m==3){ a[k]=-1;//把这个元素移除。 m=0;//m归零,重新开始下一轮计数 } k++;//寻找下一个元素,先向后寻找 for(;k<n;k++){ if(a[k]>0){ printf("向后找到的下一个元素 %d\n",a[k]); break; } } // 如果向后寻找失败,那就得从头开始继续找 if(k==n){for(i=0;i<n;i++){if(a[i]>0){ k=i; printf("向前循环找到的下一个元素 %d\n",a[k]); break; } } } //判断能否结束循环 j=0; for(i=0;i<n;i++){ if(a[i]>0){ j++; } } printf("当前数组中有效的元素个数是 %d\n",j); //j==1,说明可以结束了,k中的就是那个唯一剩下的元素了 if(j==1){ printf("最终留下的数字是 %d\n",a[k]); break; } } }int main(void) { fun(10); return 0;}
12345678910当前的m=1 对应的元素 1向后找到的下一个元素 2当前数组中有效的元素个数是 10当前的m=2 对应的元素 2向后找到的下一个元素 3当前数组中有效的元素个数是 10当前的m=3 对应的元素 3向后找到的下一个元素 4当前数组中有效的元素个数是 9当前的m=1 对应的元素 4向后找到的下一个元素 5当前数组中有效的元素个数是 9当前的m=2 对应的元素 5向后找到的下一个元素 6当前数组中有效的元素个数是 9当前的m=3 对应的元素 6向后找到的下一个元素 7当前数组中有效的元素个数是 8当前的m=1 对应的元素 7向后找到的下一个元素 8当前数组中有效的元素个数是 8当前的m=2 对应的元素 8向后找到的下一个元素 9当前数组中有效的元素个数是 8当前的m=3 对应的元素 9向后找到的下一个元素 10当前数组中有效的元素个数是 7当前的m=1 对应的元素 10向前循环找到的下一个元素 1当前数组中有效的元素个数是 7当前的m=2 对应的元素 1向后找到的下一个元素 2当前数组中有效的元素个数是 7当前的m=3 对应的元素 2向后找到的下一个元素 4当前数组中有效的元素个数是 6当前的m=1 对应的元素 4向后找到的下一个元素 5当前数组中有效的元素个数是 6当前的m=2 对应的元素 5向后找到的下一个元素 7当前数组中有效的元素个数是 6当前的m=3 对应的元素 7向后找到的下一个元素 8当前数组中有效的元素个数是 5当前的m=1 对应的元素 8向后找到的下一个元素 10当前数组中有效的元素个数是 5当前的m=2 对应的元素 10向前循环找到的下一个元素 1当前数组中有效的元素个数是 5当前的m=3 对应的元素 1向后找到的下一个元素 4当前数组中有效的元素个数是 4当前的m=1 对应的元素 4向后找到的下一个元素 5当前数组中有效的元素个数是 4当前的m=2 对应的元素 5向后找到的下一个元素 8当前数组中有效的元素个数是 4当前的m=3 对应的元素 8向后找到的下一个元素 10当前数组中有效的元素个数是 3当前的m=1 对应的元素 10向前循环找到的下一个元素 4当前数组中有效的元素个数是 3当前的m=2 对应的元素 4向后找到的下一个元素 5当前数组中有效的元素个数是 3当前的m=3 对应的元素 5向后找到的下一个元素 10当前数组中有效的元素个数是 2当前的m=1 对应的元素 10向前循环找到的下一个元素 4当前数组中有效的元素个数是 2当前的m=2 对应的元素 4向后找到的下一个元素 10当前数组中有效的元素个数是 2当前的m=3 对应的元素 10向前循环找到的下一个元素 4当前数组中有效的元素个数是 1最终留下的数字是 4
打印杨辉三角
#include <stdio.h>int main(void) {int n=9;int a[n][2*n+1];for(int i=0;i<n;i++){for(int j=0;j<2*n+1;j++){a[i][j]=0;}}//最顶点的1,作为初始值,直接赋值,其余的通过计算获得a[0][n]=1;for(int i=1;i<n;i++){for(int j=0;j<2*n+1;j++){//数组越界了,默认超出的值是0if(j-1<0){a[i][j]=0+a[i-1][j+1];continue;}if(j+1>=2*n+1){a[i][j]=a[i-1][j-1]+0;continue;}//普通的数 = 左上角 + 右上角a[i][j]=a[i-1][j-1]+a[i-1][j+1];}}for(int i=0;i<n;i++){for(int j=0;j<2*n+1;j++){if(a[i][j]==0){// 0 就不打印了,但是占位还是要有的printf(" ") ;continue;}printf(" %d ",a[i][j]) ;}printf("\n");}return 0;}
1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1
#include <stdio.h>int main(void) {// 初看以为会不止一种情况,但是自己动手算出了,发现只有一种情况// a-z b-x c-y// i 作为a的对手 j作为b的对手, k作为c的对手,求出ijk即可。char i,j,k;for(i='x';i<='z';i++) {// a的对手不是xif(i=='x') continue;for(j='x';j<='z';j++){if(i==j) continue;for(k='x';k<='z';k++){//c的对手不是x,zif(k=='x'||k=='z') continue;// 对手是抓对厮杀,不能一打Nif(i!=j&&i!=k&&j!=k){printf("a--%c b--%c c--%c \n",i,j,k);}}}}return 0;}
a--z b--x c--y
#include <stdio.h>int main(void) {char * str="adadcdcdabc";int start=0;char sChar=' ';for(int i=0;str[i]!='\0';i++){if(str[i]!='a'&&str[i]!='c'){continue;}if(sChar==' '){start=i;sChar=str[i];printf("第一次遇到起始字符 %c 位置为%d \n",sChar,start);continue;} if(str[i]==sChar){start=i;sChar=str[i];printf("遇到重复的起始字符 %c 位置需要更新为%d \n",sChar,start);} else {printf("遇到可以结束的字符 %c 位置为%d 长度为%d \n",str[i],i,i-start+1);sChar=str[i];start=i;printf("新的起始的字符 %c 位置为%d \n",str[i],i,i-start+1);}}return 0;}
第一次遇到起始字符 a 位置为0 遇到重复的起始字符 a 位置需要更新为2 遇到可以结束的字符 c 位置为4 长度为3 新的起始的字符 c 位置为4 遇到重复的起始字符 c 位置需要更新为6 遇到可以结束的字符 a 位置为8 长度为3 新的起始的字符 a 位置为8 遇到可以结束的字符 c 位置为10 长度为3 新的起始的字符 c 位置为10
这道题初看没什么思路,但是想起了把 这个数分解成两个正整数数乘积的形式 就行,但是对于 2的情况需要特殊考虑,在这个基础上,继续思考,发现不止是2,所有的偶数都是比较特殊的,所以最后就是分为 偶数和奇数个 连续数来分别计算的。
#include <stdio.h>int main(void) { int n=15; //从2开始找,3=1+2,最小的符合结果 for(int i = 2; i<=n;i++){ //i一定是偶数,下面还有一个i++,一次循环其实是 i+=2//结果分为偶数个 连续数,和奇数个 连续数// 先处理偶数个的 15=7+8 10=1+2+3+4这种// 15/2==2/2 10/4==4/2 // 也就是 n/偶数 == x.5这种的是可以的 if(n%i==i/2){ //查探一下最小的数是否是一个负数,负数就跳过 //n/i得到平均数-0.5 , i/2得到连续数的一半,在+1就是最小的连续数 //比如 15/2-2/1+1 就是最小的数 7 if(n/i-i/2+1 > 0){ for(int j=0;j<i;j++){ printf(" %d ",n/i-i/2+1+j); } printf("\n"); } } // 此时 i 肯定是一个奇数 i++; // 对奇数取余==0,说明可以整数,商就是连续数的个数 if(n%i==0){ //同样,计算连续数中的最小值, //n/i是中间值,i/2,i是奇数,得到的值恰好是中位数左边的数量 // 15/5-5/2=1 if(n/i-i/2>0){ for(int j=0;j<i;j++){ printf(" %d ",n/i-i/2+j); } printf("\n"); } } } return 0;}
结果
7 8 4 5 6 1 2 3 4 5
输出一下内容:
经过观察,# 号 在中间列必然出现,其余的 # 和行数有关, 做一个二维数组来存储,剩下的工作交给调试。
#include <stdio.h>int main(void) {int h = 8 ;int w = 15;char a[h][w];for (int i = 0;i<h;i++){for(int j = 0 ;j<w;j++){a[i][j]=' ';// 最重要的一行,如果 7-i<=j<=7+i,那么符合条件if(h-1+i>=j && h-1-i<=j){a[i][j]='#';}printf("%c",a[i][j]);}printf("\n");}return 0;}
给2个正整数,求2数的最大公约数和最小公倍数:
额~一开始 是懵B的,只是知道能求,只记住了名字叫辗转相除法,至于怎么求,完全不会,还好有百度,
用欧几里德算法(辗转相除法)求两个数的最大公约数的步骤如下:
先用小的一个数除大的一个数,得第一个余数;
再用第一个余数除小的一个数,得第二个余数;
又用第二个余数除第一个余数,得第三个余数;
这样逐次用后一个数去除前一个余数,直到余数是0为止。那么,最后一个除数就是所求的最大公约数(如果最后的除数是1,那么原来的两个数是互质数)。
#include <stdio.h>void func(int n,int m){int a=n;int b=m;// 保证 a是比b大大那个数if(a<b){int temp = a;a = b;b = temp;}int r=0;//r用来保存余数while(b!=0){r=a%b;a=b;b=r;}printf("最大公约数是 %d 最小公倍数是 %d\n",a,n*m/a);}int main(void) {func(3,4);return 0;}
将一个正整数分解质因数,如50分解为2*5*5:
瞬间想到的是递归,但是看别人的都是用的循环,但是还是感觉递归好一点。在i<=n这里小坑了一下,纸上谈兵和真正的调试还是差很多啊。
#include <stdio.h>void func(int n) {if(n==1){return ;}for(int i = 2; i<=n;i++){if(n%i==0){printf(" %d ",i);func(n/i);return;}}}int main(void) {func(98);return 0;}
2 7 7
输入一个字符串,输出字符串对应的整数,如“5144”变成int的5144:
细细想来,很多边界条件没有判断啊,负数没判断。如果字符串中像“123aaa213”这样怎么办。
#include <stdio.h>int main(void) {char * a="4722";int sum= a[0]-48 ;for(int i=1;a[i]!='\0';i++){sum = sum *10+a[i]-48;}printf("%d \n",sum);return 0;}
输入一个数字,输出字符串,如5144变成 字符串的 “5144”:
用对10取余获得每一个数字,然后把数字转成对应的char,把char拼起来,但是这样的char是倒序的,那么就倒序打印就好。
#include <stdio.h>int main(void) {int a = 5186214;char str[100];int i=0;while(a>0){ int r = a%10; a=a/10; str[i] = r+48; i++;}for(;i>=0;i--){printf("%c",str[i]);}return 0;}
反转字符串,如“123456”变成“654321”
#include <stdio.h>#include <string.h>int main(void) {char * a= "123456";int len = strlen(a);char b[len+1];char temp;for(int i = len-1,j=0;i>=0;i--,j++ ){b[j]=a[i];}b[len]='\0';printf("%s %s\n",a,b);return 0;}
查找子串是否存在,如“cd” 在 “abcdef”中是否出现。
记得有一个KMP算法是目前的最优解,算法的时间复杂度最低,效率最高。但是这算法挺难理解的。写个一般的,但是好理解的。
#include <stdio.h>int main(void) {char * a = "aaabcabd";char * b = "abc";for(int i=0,j=0 ;a[i]!='\0';i++){if(a[i]==b[j]){j++;if(b[j]=='\0'){printf("子串存在\n");return 0;}} else{i=i-j;j=0;}}printf("子串不存在\n");return 0;}
一个数组,下表从0-n,元素为从0-n的整数,判断这里面是否有重复元素:
看到题目瞬间就想到了桶排序,申明一个长度为n的数组,遍历原数组,把原数组的元素加入到新数组中,遍历结束后,打印新数组的个数,超过1说明了有重复。
#include <stdio.h>int main(void) {int a[10] = {1,2,2, 4,6,7, 8,1,3, 5};int b[10] = {0,0,0, 0,0,0, 0,0,0, 0};for(int i=0;i<10;i++){b[ a[i] ] ++;}for(int j=0;j<10;j++){printf("%d 出现了 %d 次 \n",j,b[j]);}return 0;}
0 出现了 0 次 1 出现了 2 次 2 出现了 2 次 3 出现了 1 次 4 出现了 1 次 5 出现了 1 次 6 出现了 1 次 7 出现了 1 次 8 出现了 1 次 9 出现了 0 次
给一个正整数,找出数字1出现的位数,如 421134,1出现在3,4位上:
#include <stdio.h>int main(void) {int a = 4512113;int n=0;while(a>0){int r=a%10;a=a/10;n++;if(r==1){printf("1在%d位上\n",n);}}return 0;}
有一张一元纸币换成1分,2分,5分,每种至少一枚,有多少种换法:
一开始想到了暴力枚举解决,但是这样有点傻,应该有更好的方式:
#include <stdio.h>int main(void) {for(int i=1;i<20;i++){for(int j=1;j<50;j++){for(int k=1;k<94;k++){if(i *5 + j*2 + k == 100){printf("5分 %d 个 , 2分 %d个 , 1分 %d 个\n",i,j,k) ;}}}}return 0;}
5分 1 个 , 2分 1个 , 1分 93 个5分 1 个 , 2分 2个 , 1分 91 个5分 1 个 , 2分 3个 , 1分 89 个5分 1 个 , 2分 4个 , 1分 87 个......5分 19 个 , 2分 2个 , 1分 1 个
超经典的,记得当初学c语言就有这个算法,判断一个数是不是质数:
按照定义求就行了,从2开始遍历,如果这个数对i取余为0,说明是合数,遍历到i *i 还没有,那就说明是质数。
为什么是i*i <n 就能判断了, 因为 n=根号n * 根号n, 如果n是合数,那么这2个乘数肯定一个比根号n大,一个比根号n小,现在遍历到了根号n,这个数还没有找到,那么就能说明,比根号n大的另一个乘数是不存在的,n为质数。
#include <stdio.h>int func(int n){// 其实1 既不是质数,也不是合数。没写那么细if(n<=2){return 1;}for(int i =2 ;i*i<=n;i++){if(n%i==0){printf("是合数");return 0;}}printf("是质数");return 1;}int main(void) {func(18);return 0;}
- 几个面试算法题,附源码
- 几个面试经典算法题Java解答
- 几个排序算法源码
- 几个排序算法源码
- 数组的几个面试算法
- (转)淘宝面试的几个算法题
- 面试中的几个题
- 几个面试基础题
- GL游戏算法(附源码)
- GL 游戏算法(附源码)
- 浅析KMP算法(附C++源码)
- SM4密码算法(附源码)
- SM4密码算法(附源码)
- SM4密码算法(附源码)
- 微软数据结构+算法面试100题【附前60题答案下载地址】
- 几个面试可能会用到的排序算法
- 程序员须知:面试中最容易被问到的18个算法题(附答案!)
- C/C++面试之算法系列--几个最大子字符串的算法题
- 两种开源聊天机器人的性能测试(二)——基于tensorflow的chatbot
- audioUnit实现步骤(无代码)
- 基于Kubernetes的DevOps实践培训 | 深圳站
- 容器监控的基石Prometheus 2.0到来
- 第十周项目1(2)-由先序序列和中序序列构造二叉树
- 几个面试算法题,附源码
- 图解HTTP笔记:返回结果的HTTP状态码及其表意
- Linux(RedHat)学习之路6.0之逻辑卷管理
- 金蝶首席用户体验官对“用户体验”的思考
- 后台app个推
- webpack入门
- hadoop 之MR的join操作
- echarts.js的基础
- jquery ajax、 原生js ajax、string和json之间的转换