尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

力扣412题Fizz Buzz详解:从基础到优化的多语言解法与面试技巧

力扣412题Fizz Buzz详解:从基础到优化的多语言解法与面试技巧 最近在准备面试或者刚开始刷力扣LeetCode的同学大概率会遇到一道名为Fizz Buzz的题目。这道题编号为412常年出现在各大公司的面试题库中也被收录在力扣的“热题100”和“初级算法”等经典列表里。它看似简单却是一个绝佳的起点能帮你快速理解力扣的题目格式、测试用例逻辑并建立起解决算法问题的基本思维框架。本文将带你从零开始彻底搞懂 Fizz Buzz 问题不仅给出多种解法还会深入分析其背后的编程思想、代码优化技巧以及如何举一反三应对更复杂的变种问题。1. 背景与核心概念为什么是 Fizz Buzz在深入代码之前我们先搞清楚这道题到底在问什么以及它为什么如此经典。1.1 问题描述与规则力扣第 412 题 “Fizz Buzz” 的官方描述如下给你一个整数n找出从1到n各个整数的 Fizz Buzz 表示并以字符串数组ListString或vectorstring等形式返回答案。表示规则如下如果整数是3的倍数则用“Fizz”表示。如果整数是5的倍数则用“Buzz”表示。如果整数同时是3和5的倍数则用“FizzBuzz”表示。如果整数不满足以上任何条件则用该整数本身的字符串形式表示。示例 1输入n 3 输出[1,2,Fizz]示例 2输入n 5 输出[1,2,Fizz,4,Buzz]示例 3输入n 15 输出[1,2,Fizz,4,Buzz,Fizz,7,8,Fizz,Buzz,11,Fizz,13,14,FizzBuzz]1.2 这道题考察什么别看题目简单它考察了程序员的多项基本功循环控制如何遍历从 1 到 n 的整数序列。条件判断如何清晰、无遗漏地处理多个条件3的倍数、5的倍数、同时满足。字符串操作将整数转换为字符串以及字符串的拼接。数据结构理解并使用数组或列表来存储和返回结果。边界与细节注意起始值1和结束值n的处理以及条件判断的顺序“FizzBuzz”的判断必须先于单独的“Fizz”或“Buzz”。对于面试官而言这道题是一个很好的“开胃菜”能快速评估候选人的编码风格、逻辑严谨性和对基础语法的掌握程度。一个写得清晰、健壮的 Fizz Buzz 解法往往能给面试官留下良好的第一印象。2. 环境准备与解题思路在开始编码前我们明确一下解题环境。力扣支持多种语言本文将以Java和Python这两种最流行的语言为例进行讲解。你可以使用任何你熟悉的 IDE如 IntelliJ IDEA, VS Code或直接在力扣的在线编辑器中编写代码。核心思路拆解无论用哪种语言解决 Fizz Buzz 的算法流程都是高度一致的可以分解为以下几步初始化创建一个用于存放结果的空列表或数组。遍历使用一个循环从i 1开始到i n结束。判断在循环体内对当前的i进行条件判断如果i能被 15 整除即同时被 3 和 5 整除则向结果列表添加“FizzBuzz”。否则如果i能被 3 整除则添加“Fizz”。否则如果i能被 5 整除则添加“Buzz”。否则将i转换为字符串后添加。返回循环结束后返回这个结果列表。这个思路清晰直接接下来我们看看如何用代码实现它并探讨不同实现方式的优劣。3. 基础解法条件判断的艺术我们先从最直观的解法开始即使用多个if-else if语句。3.1 Java 基础解法import java.util.ArrayList; import java.util.List; class Solution { public ListString fizzBuzz(int n) { // 1. 初始化结果列表 ListString answer new ArrayList(n); // 指定初始容量可提升性能 // 2. 遍历 1 到 n for (int i 1; i n; i) { // 3. 条件判断 if (i % 3 0 i % 5 0) { answer.add(FizzBuzz); } else if (i % 3 0) { answer.add(Fizz); } else if (i % 5 0) { answer.add(Buzz); } else { answer.add(Integer.toString(i)); // 将整数转为字符串 } } // 4. 返回结果 return answer; } }代码解析ListString answer new ArrayList(n);这里使用了ArrayList并指定了初始容量n。这是一个很好的优化习惯可以避免在添加元素过程中因扩容而产生的额外开销。条件判断的顺序至关重要。我们必须先检查i % 3 0 i % 5 0即能被15整除因为如果先检查i % 3 0当i15时会匹配到“Fizz”而错过“FizzBuzz”。Integer.toString(i)是 Java 中将int转换为String的标准方法之一等同于String.valueOf(i)。3.2 Python 基础解法class Solution: def fizzBuzz(self, n: int) - List[str]: # 1. 初始化结果列表 answer [] # 2. 遍历 1 到 n for i in range(1, n 1): # 3. 条件判断 if i % 3 0 and i % 5 0: answer.append(FizzBuzz) elif i % 3 0: answer.append(Fizz) elif i % 5 0: answer.append(Buzz) else: answer.append(str(i)) # 将整数转为字符串 # 4. 返回结果 return answer代码解析range(1, n 1)Python 的range函数是左闭右开的所以要写到n1才能包含n。str(i)Python 中内置的str()函数可以将任何对象转换为其字符串表示形式对于整数来说非常方便。逻辑与 Java 版本完全一致体现了算法思想与语言语法的分离。4. 进阶与优化解法基础解法虽然正确但我们可以从可读性、可维护性和性能角度进行优化。面试中在写出基础解法后如果能主动提出优化思路会是很大的加分项。4.1 优化一字符串拼接法我们注意到“FizzBuzz” 实际上是 “Fizz” 和 “Buzz” 的拼接。我们可以先构建一个空字符串然后根据条件逐步拼接。// Java 字符串拼接版 class Solution { public ListString fizzBuzz(int n) { ListString answer new ArrayList(n); for (int i 1; i n; i) { StringBuilder sb new StringBuilder(); // 使用StringBuilder更高效 if (i % 3 0) { sb.append(Fizz); } if (i % 5 0) { sb.append(Buzz); } // 如果sb为空说明既不是3的倍数也不是5的倍数 if (sb.length() 0) { sb.append(i); } answer.add(sb.toString()); } return answer; } }# Python 字符串拼接版 class Solution: def fizzBuzz(self, n: int) - List[str]: answer [] for i in range(1, n 1): s if i % 3 0: s Fizz if i % 5 0: s Buzz if not s: # 如果s是空字符串 s str(i) answer.append(s) return answer优势分析逻辑更清晰消除了复杂的if-else if嵌套每个条件独立判断互不影响。易于扩展如果未来规则变为“遇到7的倍数加Jazz”只需要增加一个if (i % 7 0)的判断和sb.append(“Jazz”)即可无需修改原有逻辑结构。这在软件工程中是一个重要优点。性能Java 中使用StringBuilder进行字符串拼接比直接用号或多次创建字符串对象更高效。Python 中字符串是不可变的每次都会生成新对象但对于本题的规模性能差异可忽略不计。4.2 优化二哈希表映射法极致可扩展性这是力扣官方题解中提到的一种非常优雅的解法尤其适用于规则可能动态变化或非常复杂的场景。其核心思想是将映射关系除数 - 输出词存储在一个哈希表字典里。// Java 哈希表映射版 import java.util.*; class Solution { public ListString fizzBuzz(int n) { // 定义映射关系 key-除数 value-对应的输出词 MapInteger, String fizzBuzzDict new LinkedHashMap() {{ put(3, Fizz); put(5, Buzz); // 未来可以轻松添加 put(7, Jazz) }}; ListString answer new ArrayList(n); for (int i 1; i n; i) { StringBuilder sb new StringBuilder(); // 遍历字典中的每个除数 for (Integer key : fizzBuzzDict.keySet()) { if (i % key 0) { sb.append(fizzBuzzDict.get(key)); } } // 如果没有任何匹配则使用数字本身 if (sb.length() 0) { sb.append(i); } answer.add(sb.toString()); } return answer; } }# Python 哈希表映射版 class Solution: def fizzBuzz(self, n: int) - List[str]: # 使用有序字典或列表保证输出顺序如3在5前则Fizz在Buzz前 fizz_buzz_dict {3: Fizz, 5: Buzz} answer [] for i in range(1, n 1): s for key in fizz_buzz_dict: if i % key 0: s fizz_buzz_dict[key] if not s: s str(i) answer.append(s) return answer优势分析极高的可维护性和可扩展性所有业务规则都集中在字典fizzBuzzDict中。要修改或增加规则例如增加7-“Jazz”只需修改字典循环和核心逻辑完全不用动。这符合“开闭原则”对扩展开放对修改关闭。代码与数据分离将“什么条件下输出什么”这部分易变的业务逻辑数据化使得代码主体更加稳定。面试亮点在写出基础解法后如果能主动提出“如果规则未来会频繁变更我们可以用哈希表来解耦”这体现了你的软件设计思维远超仅仅“解题”的层面。5. 常见问题与排查思路在实现 Fizz Buzz 时新手常会遇到一些典型的错误。下面是一个排查清单问题现象常见原因解决思路输出结果中缺少“FizzBuzz”15被输出为“Fizz”或“Buzz”。条件判断顺序错误。先判断了i % 3 0或i % 5 0导致15提前匹配。确保最先判断i % 3 0 i % 5 0或使用字符串拼接法各条件独立判断。循环从0开始导致第一个输出是“FizzBuzz”因为0能被任何数整除。循环初始值设错。for (int i 0; ...)。将循环初始值改为1for (int i 1; i n; i)。输出结果比预期的少一个缺少数字n。循环条件错误。使用了i nPython中是range(1, n)。将循环条件改为i nPython中是range(1, n1)。数字没有被正确转换为字符串导致类型错误或输出异常。在应添加字符串的地方直接添加了整数。例如answer.add(i)。使用Integer.toString(i),String.valueOf(i)(Java) 或str(i)(Python) 进行转换。在Python中返回类型提示List[str]报错NameError。没有导入typing模块中的List。在文件顶部添加from typing import List。性能问题当n极大时。使用了字符串直接拼接在Java中或列表未预分配空间。Java使用StringBuilder初始化列表/数组时指定容量如new ArrayList(n)。6. 举一反三Fizz Buzz 的变种与扩展掌握基础解法后我们可以看看一些常见的变种问题这有助于深化理解。6.1 变种一Fizz Buzz 进阶LeetCode 1195假设有四个线程分别负责打印数字、打印“fizz”、打印“buzz”、打印“fizzbuzz”。你需要设计一个机制使得输出序列完全正确。这考察的是多线程同步与通信难度大幅提升。核心是使用锁、条件变量或信号量来控制线程的执行顺序。6.2 变种二自定义规则 Fizz Buzz题目可能将3和5替换成其他数字比如a和b或者增加更多的映射规则如7-“Jazz”。这时前面提到的哈希表映射法的优势就体现出来了。你的代码几乎不需要改动只需要修改映射字典即可。6.3 变种三统计次数不输出具体序列而是要求返回一个字典记录从1到n中“Fizz”、“Buzz”、“FizzBuzz”和数字各自出现的次数。这需要你在循环中增加计数逻辑。# 统计次数的变种示例 def fizzBuzz_count(n: int) - dict: count {“Fizz”: 0, “Buzz”: 0, “FizzBuzz”: 0, “number”: 0} for i in range(1, n1): if i % 15 0: count[“FizzBuzz”] 1 elif i % 3 0: count[“Fizz”] 1 elif i % 5 0: count[“Buzz”] 1 else: count[“number”] 1 return count7. 最佳实践与工程建议即使在这样简单的题目中也能体现出良好的工程习惯。命名清晰变量名使用answer,result而非arr,list循环变量使用i或index。注重性能细节Java在知道最终大小的情况下使用new ArrayList(n)初始化列表。字符串拼接使用StringBuilder。Python虽然本题性能瓶颈不在此但在大规模数据处理中列表推导式或预分配列表 ([None]*n) 可能比append稍快。代码可读性优先在面试和实际项目中清晰的逻辑远比一点点的性能“奇技淫巧”重要。if-else结构清晰明了是第一选择。优化应在清晰的基础上进行。考虑扩展性在写出解法后可以思考“如果需求变了怎么办”。主动提及哈希表映射法展示你的设计思维。写注释对于复杂的逻辑或易错点如判断顺序简单的注释能极大提升代码的可维护性。测试你的代码在力扣上提交前自己用几个例子测试一下特别是边界情况n1,n3,n5,n15。8. 总结与学习路线通过本文对力扣 412 题 “Fizz Buzz” 的深入剖析我们不仅掌握了从基础到优化的多种编码实现更重要的是学习了解决算法问题的通用思维模式理解问题仔细阅读题目明确输入、输出和规则。设计算法用自然语言或伪代码描述步骤循环、判断、存储。编码实现选择熟悉的语言将算法转化为代码注意语法细节。测试验证用题目给的例子和自定义的边界案例进行测试。分析优化思考时间/空间复杂度是否有更优雅、更易维护的写法举一反三思考问题的变种将解决方法抽象成模式。下一步学习建议如果你刚接触力扣建议以“探索”栏目中的“初级算法”为起点里面都是类似 Fizz Buzz 这种巩固基础的经典题目。重点关注“数组”、“字符串”相关题目它们是几乎所有复杂算法的基础。养成写题解的习惯。尝试用不同的方法解决同一道题并比较优劣。力扣社区有很多优质题解多看看别人的思路尤其是高票答案中关于时间/空间复杂度的分析。不要盲目追求题量。吃透一道像 Fizz Buzz 这样的简单题理解其背后的编程思想和优化策略远比稀里糊涂刷完十道题更有价值。力扣刷题是一场马拉松而不是百米冲刺。从 Fizz Buzz 这样的小步开始建立信心掌握方法逐步挑战更复杂的题目。当你再看到“热题100”或“周赛”里的题目时就不会感到无从下手了。
返回列表