尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Java面试22道经典算法题:从斐波那契到菱形打印的避坑指南

Java面试22道经典算法题:从斐波那契到菱形打印的避坑指南 简介面向Java初、中高级开发者的编程面试题资源聚焦面试中高频出现的基础编程题涵盖斐波那契数列、素数判断、水仙花数、分解质因数等经典题目。包体仅含1个doc文档整体大小约104KB内容以代码示例与逐题解析为主轻量便捷适合笔试前或技术面试冲刺阶段反复对照练习。目前已有141人学习可作日常备查与突击复习之用。文档对每道题均给出完整Java代码和核心思路例如用循环代替递归计算兔子总数利用Math.sqrt缩小整除判断范围通过取模和除法拆解三位数以验证水仙花数以及用while循环从最小质数2开始逐层分解质因数。这些典型题目覆盖循环结构、条件判断、算术运算及基础算法思想能有效巩固编程基本功通过阅读代码并尝试手写复现可以提升现场编程的逻辑条理和临场应变能力为这类实战考核做好更充分准备。1. Java编程面试题22道经典算法题从兔子数列到菱形打印Java编程面试题里最容易被临时考倒的往往不是框架八股反而是这些基础算法小题。这份资源是流传多年的一套Java面试题库22道题从兔子的斐波那契数列一路做到菱形打印覆盖循环、分支、数字拆分、递推和字符统计几乎把初级笔试的高频考点过了一遍。不管你是准备校招笔试还是给新人做入门编程摸底这套题都是现成的训练素材。代码是典型的老式控制台写法能跑但细节经不起推敲逐题拆一遍正好看清出题人想考什么也方便改出一版真正能拿给面试官看的代码。2. 数学算法与循环结构滚动变量、素数判断与数字拆分这一类的题核心是看你能不能把数学关系用最朴素的循环表达出来。面试手写代码时没有IDE提示也没有现成工具类可查循环边界、递推变量、取模拆数这三样基本功直接决定能不能一次写对。2.1 斐波那契数列滚动变量的循环写法程序1的兔子问题本质就是斐波那契数列。原题给出的循环版本是三步滚动public class lianxi01 { public static void main(String[] args) { int f1 1, f2 1, f, M 24; for (int i 3; i M; i) { f f2; // 暂存旧值 f2 f1 f2; // 新值 前两项之和 f1 f; // 旧值回填 System.out.println(第 i 个月的兔子对数: f2); } } }关键就在f f2; f2 f1 f2; f1 f;这三步。如果不做暂存直接写f1 f2; f2 f1 f2;f1被覆盖后下一步算出来的就是错的。理解了滚动变量的顺序比背递归公式重要得多。面试时如果追问时间复杂度和空间复杂度循环版是O(n)时间、O(1)空间递归版是O(2^n)时间这题答到这里基本就到点上了。实际运行还有个容易被忽略的边界月数M改成50时int会溢出成负数。主动说出结果超过int范围需要换long或BigInteger是明显的加分项。2.2 素数判断循环上限与标志位程序2要求统计101到200之间有多少个素数并输出全部。原题的判断逻辑是用2到sqrt(n)逐个试除这个数学思路是对的但代码里的标志位绕了个圈。我一般会改成更直白的写法public class PrimeCount { public static void main(String[] args) { int count 0; for (int i 101; i 200; i) { boolean isPrime true; for (int j 2; j Math.sqrt(i); j) { if (i % j 0) { isPrime false; break; } } if (isPrime) { count; System.out.println(i); } } System.out.println(素数个数是: count); } }这里改了两处。第一标志位默认值从false改成true只有确认被整除才置false逻辑一眼就能看懂。第二外层循环从i2改成i。原题用步长2跳过偶数在101到200这个区间确实没问题但一旦把范围挪到别的区间这种优化就容易翻车。面试时能把这两点改动的理由说清楚比背答案强得多。内层循环上限用Math.sqrt(i)是这题的核心考点。理由是一个合数若有因子必然有一个因子不超过它的平方根超过平方根的那一半是配对出现的。想明白这层对称关系就能理解为什么不用循环到i本身。2.3 水仙花数与数字拆分程序3的水仙花数是数字拆分里的经典题。思路很直接把三位数的百位、十位、个位分别拆出来判断立方和是否等于原数。public class Narcissus { public static void main(String[] args) { for (int m 101; m 1000; m) { int hundreds m / 100; int tens m % 100 / 10; int ones m % 10; if (hundreds * hundreds * hundreds tens * tens * tens ones * ones * ones m) { System.out.println(m 是一个水仙花数); } } } }数字拆分的三个表达式里最容易写错的是十位。m % 100 / 10是先模100得到后两位再除10得到十位m / 10 % 10是先除10丢掉个位再模10得到十位。两种写法结果一样但前者更贴近先取后两位的思路。这类数字拆分题在笔试里出现频率很高建议把取百位、十位、个位这一组写法练成肌肉记忆。这里还有个延伸考点如果把三位数改成任意位数求水仙花数直接套这个写法就不够了。常见做法是循环加Math.pow或者转成字符串逐位取能现场改出这版说明你对题目理解不是停留在背代码。2.4 分解质因数while循环加最小质数程序4要求把一个正整数分解成质因数的乘积例如90输出90233*5。原题用的是从2开始的候选因子k配合while循环public static void factor(int n) { int k 2; while (k n) { if (k n) { System.out.print(n); break; } else if (n % k 0) { System.out.print(k *); n n / k; } else { k; } } }这个写法的高明之处在于找到一个因子后不重置k。因为同一个质因数可能连续出现多次比如90里的3出现两次k保持不动就能在下一轮继续试除同一个数。如果n一直不能被k整除k再递增。相比先枚举所有质数再逐个试的方案这个写法少了一层循环手写时也不容易乱。有一个小边界值得注意输入n为1或0时while循环压根不进入什么也不输出。面试时我会主动加一个if (n 2) return;的防御判断这种细节考官通常看在眼里。3. 条件分支与状态计算三元嵌套、辗转相除与区间提成这一章的题目开始掺杂输入处理和复杂分支。面试现场写这类题最大的风险不是不会算法而是边界条件覆盖不全。一道分支题至少有正常值、临界值、非法值三类输入要测能主动想到这一点代码质量就拉开差距了。3.1 三元运算符嵌套与成绩分级程序5把成绩分成A/B/C三档原题用一行三元运算符嵌套char grade x 90 ? A : x 60 ? B : C;三元运算符是右结合的等价于x 90 ? A : (x 60 ? B : C)。这行代码简洁但有两个隐含问题。第一个是输入范围没校验负数和超过100的成绩都会落到C档第二个是Scanner.nextInt()遇到非数字输入会抛InputMismatchException笔试环境下如果允许加一层try-catch或者手动校验输入范围会更稳。我一般会建议面试时先写这种能跑通的简洁版然后在口头补充说明实际生产环境需要校验输入范围这样既展示了编码速度又展示了防御性编程意识。3.2 最大公约数与最小公倍数程序6求最大公约数用的是辗转相除法也就是欧几里得算法。原题的实现先做大小交换再循环取余逻辑完整但代码偏长。手写时有个简化写法public static int gcd(int x, int y) { while (y ! 0) { int temp y; y x % y; x temp; } return x; }这个版本不需要先比较大小因为x % y在x小于y时自然会得到x下一轮角色就互换了省掉交换逻辑反而更不容易写错。最小公倍数是两数乘积除以最大公约数要注意乘积可能溢出改成x / gcd(x, y) * y更安全。笔试里经常有人把辗转相除和更相减损术混淆。如果用减法实现大数会循环很多次效率差。面试官如果追问为什么选辗转相除答每次取余至少让数字规模减半log级别收敛就足够了。3.3 奖金提成的分段区间程序12是企业奖金按利润分段提成经典的多区间if-else题。原题写了六段区间代码很长核心难点是区间边界不能重叠也不能遗漏。原题用的是x 0 x 10、x 10 x 20这种左开右闭写法逻辑自洽但中间几段把累计提成直接硬编码成了20 * 0.175这种常量一旦提成比例调整就得重写整段。更可维护的写法是先把各段阈值和比例存成数组再用循环累加计算。面试时提到这种思路比背原题加分得多。不过如果时间紧张直接写if-else也能过关键是每段分支的边界值要测一遍尤其是利润正好等于10万、20万、40万这些临界点。3.4 日期计算与闰年判断程序14要求输入年月日判断是这一年的第几天。这种题最阴险的地方在闰年判断平年二月28天闰年29天。闰年条件要按顺序写boolean isLeap (year % 400 0) || (year % 4 0 year % 100 ! 0);这个顺序不能反否则1900年这种能被100整除但不能被400整除的年份会被误判成闰年。原题用switch挨个case加月份天数写法冗长但直白。更简洁的做法是用数组存每个月天数int[] daysInMonth {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; if (isLeap) daysInMonth[1] 29; int dayOfYear 0; for (int i 0; i month - 1; i) { dayOfYear daysInMonth[i]; } dayOfYear day;这个版本把数据和逻辑分离比switch堆代码好读。面试时可以提一句用数组代替switch新增逻辑不用改流程考官一般会认可。原题还有一层bugdo-while校验里只检查了month在1到12、day在1到31但2月30日这种非法组合仍然能通过。这个坑留到第5章展开说。3.5 三个数排序的交换逻辑程序15输入三个整数要求由小到大输出。核心就是三次比较加交换两个变量交换必须靠临时变量中转if (x y) { int t x; x y; y t; } if (x z) { int t x; x z; z t; } if (y z) { int t y; y z; z t; }这三个if的顺序不能乱先保证x最小再让y和z排好。很多人写第一个if时忘掉临时变量直接x y; y x数据就丢了。面试手写这种题宁可慢一点把三步写全也不要为了省行数翻车。提示三个数以上的排序建议直接用Arrays.sort但面试官大概率会追问底层原理手写比较交换反而能展示基本功。4. 字符处理与模式输出ASCII边界、连加累乘与双重循环这一章转向字符操作和图形输出代码技巧集中在字符编码判断、累加变量更新、双重循环的边界控制上。这几类题在笔试里出现频率很高因为它们能同时考察循环、字符串处理和格式化输出。4.1 字符统计toCharArray与ASCII边界程序7统计一行字符里英文字母、空格、数字和其他字符的个数。原题的思路是对的但代码有一个硬伤比较的是数组名而不是数组元素。修正后应该是Scanner sc new Scanner(System.in); String s sc.nextLine(); char[] ch s.toCharArray(); int digital 0, character 0, blank 0, other 0; for (int i 0; i ch.length; i) { if (ch[i] 0 ch[i] 9) { digital; } else if ((ch[i] a ch[i] z) || (ch[i] A ch[i] Z)) { character; } else if (ch[i] ) { blank; } else { other; } }原题里ch 0这种比较在Java里连编译都过不了因为数组对象不能和字符直接比较。另外大写字母的条件原题写的是ch A ch Z少了一个等于导致大写A被漏统计。这种ASCII区间判断的边界错误非常隐蔽面试时拿A、Z、a、9、空格各测一个字符就能全暴露。中文字符在这个统计里会落入other分支因为Java的char是Unicode编码中文字符的码点远大于ASCII范围。如果有需求区分中西文得用Character.UnicodeScript或者正则表达式但原题场景用不上。4.2 saaaaaa连加式的循环变量更新程序8求a aa aaa ...的和a由键盘输入项数n也由键盘控制。原题用两个变量维护递推long a, b 0, sum 0; Scanner s new Scanner(System.in); a s.nextInt(); int n s.nextInt(); int i 0; while (i n) { b b a; // b: 当前项 sum sum b; // sum: 累计和 a a * 10; // a: 下一位权值 i; } System.out.println(sum);这段代码的妙处是只用了一个while循环b表示当前项sum累加a不断乘10生成下一位。比如a2第一轮b2sum2a变成20第二轮b22sum24a变成200第三轮b222sum246正好对应222222。原题用long是考虑到累加会超出int范围但项数一大long同样溢出。九个9连加就是九亿多十项必爆int几十项long也撑不住。真想算大数得用BigInteger。面试时问一句项数上限是多少能答出这个溢出边界说明你对数值范围有概念。4.3 九九乘法表与菱形打印双重循环的边界控制程序16输出9x9乘法口诀程序19打印菱形这两个都是双重循环的典型题。乘法口诀的核心是内层循环上限取j i保证只输出左下三角for (int i 1; i 10; i) { for (int j 1; j i; j) { System.out.print(j * i (j * i) \t); } System.out.println(); }原题用字符串拼接加手动补空格对齐结果在控制台里歪歪扭扭。换成printf(%d*%d%-4d, j, i, j*i)用左对齐宽度4输出就整齐了。面试时顺口提一句用printf比字符串拼接更规范这种小细节很能体现工程习惯。菱形打印的难点在于每一行的空格数和星号数都是动态计算的。我通常先在草稿纸上写出行号、空格数、星号数的对应表再对应写循环。上半部分空格递减、星号递增下半部分反过来。原题要求高和宽必须相等且为奇数这个约束写注释里了如果改成偶数会错位。笔试时直接在循环里写死H7可以但最好改成参数传入方便验证多组尺寸。4.4 比赛配对约束条件下的组合输出程序18是两个乒乓球队的配对问题甲队a、b、c对阵乙队x、y、z条件是a不和x比c不和x、z比。原题用三层嵌套循环加continue跳过非法组合本质是穷举所有配对再过滤约束。这个题改为用约束条件直接排除比原题的硬编码continue链更清晰。所谓匹配本质是排列组合先列出所有可能再逐个判断约束是否满足。面试时能指出continue铺开写不利于扩展建议把禁赛条件收集成集合再做过滤这一层的抽象能力比跑出结果更有价值。原题的硬编码版本一旦条件变化比如b也不和x比代码就得改多处这就是工程上说的可维护性问题。5. 避坑原题代码里的五处典型问题与修正方法这套题流传多年代码有不少从早期教材或C语言版本迁过来的痕迹有的地方写得绕有的地方直接是bug。我整理了自己拆题时踩过的五处典型坑按现象、原因、解决三句话说明方便你对照自己的代码排查。5.1 素数判断的标志位绕圈现象程序2能跑出正确结果但标志位b的写法很难读懂把它挪到其他区间就不确定对不对。原因b初始为false内层循环每次在不能整除时设true一旦某轮整除就置false并break。碰巧在101到200范围内逻辑自洽换成别的区间就说不准了。解决把标志位默认true只有确认整除才置false。这个改动让判断逻辑和数学定义完全一致不再依赖循环执行的先后顺序。5.2 字符统计数组下标丢失现象程序7直接编译不过报错信息指向数组和字符比较的那几行。原因ch是char[]原题比较时却写成了ch 0这种形式典型的从C数组版迁移到Java时漏掉了下标[i]。解决全部改成ch[i]同时修正大写字母判断的为。改完记得用A这个边界字符验证一遍。5.3 日期校验挡不住非法日期现象程序14里输入2月30日程序会按正常日期计算输出错误的天数序号。原因do-while循环只检查了月份在1到12、日期在1到31没按具体月份校验天数上限2月30日这种不存在的日期漏过去了。解决把天数上限和月份映射放在一起判断先用数组拿到当月最大天数再做区间校验。这属于防御性编程的常见套路值得练习。5.4 整数范围理解偏差现象程序13求加上100后是完全平方数的数原题从x1开始遍历评论区很多人也在问为什么不是从负数开始。原因题目原文是一个整数按数学定义应包括负整数。原题代码注释里自己也承认把整数理解成了正整数导致漏掉了满足条件的负数。解决既然是面试题按常规理解写正整数版本没问题但在口头补充时说明负数场景如何处理反而能展示思维的全面性。5.5 比赛配对硬编码条件现象程序18输出结果恰好是三组正确配对但只要加一条约束条件输出就全乱了。原因所有排除逻辑都用continue写死在双层循环里条件之间没有任何抽象。如果规则一改continue链就得重新梳理。解决把禁赛关系抽象成集合或布尔矩阵再统一过滤。这个小重构既能让代码变短也能应对规则变化面试时主动做这种优化会给人留下好印象。6. 面试现场怎么用把旧题改写成能拿分的版本这套题用来刷熟练度没问题但直接照原样背去面试反而容易暴露问题。我的习惯是拿到一道题先做四步改造改造完的版本才是真正能拿给面试官看的。第一步是方法抽取。原题逻辑全塞在main里变量名是f1、f2、x、y这类无意义缩写。把核心逻辑抽成独立方法例如斐波那契抽成int fib(int n)最大公约数抽成int gcd(int a, int b)。方法签名自带语义面试官一看就知道你具备模块化思维。第二步是边界补全。给每个方法补上输入校验和溢出保护。比如成绩分级先校验score 0 || score 100就抛IllegalArgumentException斐波那契的返回值改成long或BigInteger日期计算对2月30日直接拒绝。这些代码多写五行但展现的是生产环境意识。第三步是复杂度说明。每道题做完主动说出时间和空间复杂度。例如斐波那契循环版O(n)、素数判断O(n√n)、辗转相除O(log n)。面试官问还能不能优化斐波那契可以提矩阵快速幂O(log n)素数判断可以提只遍历到sqrt的优化能接住追问就稳了。第四步是写验证代码。不要求现场写完整JUnit测试但可以在main里多放几个断言性质的调用System.out.println(fib(10)); // 期望 55 System.out.println(gcd(12, 8)); // 期望 4 System.out.println(lcm(12, 8)); // 期望 24 System.out.println(isPrime(17)); // 期望 true System.out.println(isPrime(9)); // 期望 false这几行输出比任何口头解释都有说服力。用这套方法过完这22道题以后我的一个习惯是每道题强制自己手写至少两版第一版按原题思路快速跑通第二版立即重构优化。从那以后我每次准备笔试或带新人做练习都强制走一遍拆题、改边界、抽方法、写断言的流程效果比闷头刷几十道新题好得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表