如何解决编程面试中的挑战

2025-05-24

如何解决编程面试中的挑战

就像生活中的许多事情一样,精通需要练习,编程面试也不例外。然而,很多时候,面试官的重点是从一开始就尝试找到最佳解决方案,而不是如何迭代并最终实现目标。

在我看来,最重要的部分是先把事情做好。在你的日常编程生涯中,如果不先经过一系列迭代,你很少能够写出“完美”的代码。

编码面试期间的方法不应该有所不同,如果运用得当,应该可以帮助您获得宝贵的分数,展示您的解决问题的能力。

如果您被要求解决著名的 FizzBu​​zz 挑战,我将向您介绍您和面试官之间的真实对话。

挑战

编写一个程序,打印从 1 到 100 的数字。如果数字是 3 的倍数,则打印 Fizz 而不是数字本身;如果数字是 5 的倍数,则打印 Buzz。如果数字既是 3 的倍数,又是 5 的倍数,则打印 FizzBu​​zz。

背景

FizzBu​​zz 挑战并非 JavaScript 独有,几乎所有编程语言的编程面试流程都包含这项挑战。它通常用于快速评估应聘者的基本编程能力,但如果面试官愿意,也可以将其转化为对应聘者深度知识的评估。
它通常是在屏幕共享模式下进行的轻量级首次技术面试的一部分。对于非 JavaScript 程序员来说,这也是一个热门的提问方式,可以快速评估应聘者的技术知识和方法。

在 Javascript 环境中,需要熟悉以下部分或全部概念:

  • 逻辑运算符
  • 循环
  • 虚假值
  • 三元运算符
  • 类型强制

方法

就像你可能遇到的任何问题一样,即使是那些看起来很熟悉的问题,也必须仔细阅读并分解成小部分。要向面试官明确说明,你需要3到5分钟的时间冷静地阅读,并提出重写你理解的建议。

如果你愿意把这部分大声说出来,那就更好了。例如,我可能会这样重写:

  • 因此,将 1 到 100 的数字记录到控制台 - 我需要一个循环
  • 对于 3 的倍数,输出字符串“Fizz”而不是数字
  • 对 5 的倍数执行相同操作,输出为“Buzz”
  • 如果数字是 3 和 5 的倍数,则输出“FizzBu​​zz” - 如何检查 a 是否是 b 的倍数?
  • 如果以上所有情况都失败,则直接输出数字

我可能会问面试官是否应该担心极端情况或错误的输入。通常情况下,面试官会暗示输入是正确的,所以极端情况可能没有必要。不过,你提出这个问题,反而能为你的解决问题的方法增添一丝雄辩。

解决方案

面试过程中,关键且值得练习的一件事是,在构建解决方案时,引导面试官逐步完成你的步骤。从显而易见的部分开始,你可能需要用一个函数或类作为主要构造。从这里开始,并始终牢记 KIASSAP 原则 :)——尽可能保持简单。

第一步

// comments are me talking out loud
// let's build the function structure
function fizzBuzz( start = 1, end = 100) { // default parameters to set the default range
    // I need a loop - let's go with for
    for( let i = start; i <= end; i++) {
        // probably a variable for what will be outputted
        let output = i;

        // rest of the logic here

        // outputting the result
        console.log(output);
    }
}
// call the function
fizzBuzz(); // this prints out 1 to 100 - fancy ;)

以上满足了我对重写挑战理解的第一个目标

第二步

现在,如果我遵循挑战的节奏,我将解决两个问题:

- Choosing the proper operator to find if a number is a multiple of another
- Apply it for the multiple of 3 condition and output 'Fizz'

余数运算符 %是这里的完美工具。如果数字a是数字b的倍数,

( b % a) === 0; // will be true;
// 4 is a multiple of 2
( 4 % 2 ) === 0; // is true

让我们在函数主体中应用它

// rest of the logic here
if( (i % 3) === 0 ) {
    output = 'Fizz';
}
// Knowing that 3,6 and 9 are multiple of 3 let's
// quickly test a small sequence by calling

fizzBuzz(1,10); 
// this should output
// 1, 2, 'Fizz', 4, 5, 'Fizz', 7, 8, 'Fizz', 10

最后一步

由于 Fizz 条件运行完美,我们现在可以将相同的逻辑应用于其余部分

// multiple of 5
if( (i % 5) === 0 ) {
    output = 'Buzz';
}

// multiple of 3 and 5
if( (i % 3) === 0  && (i % 5 === 0)) {
    output = 'FizzBuzz';
}


哇!这满足了所有条件,并且一旦组装并删除所有注释,我们就得到了这个解决方案的杰作。

function fizzBuzz( start = 1, end = 100) { // default parameters to set the default range
    for( let i = start; i <= end; i++) {
        let output = i;
        if( (i % 3) === 0 ) {
            output = 'Fizz';
        }
        if( (i % 5) === 0 ) {
            output = 'Buzz';
        }
        if( (i % 3) === 0  && (i % 5) === 0) {
            output = 'FizzBuzz';
        }
        console.log(output);
    }
}
fizzBuzz();

现在,我找到了一个可以满足挑战要求的方案。接下来的事情在面试中非常棘手。我的代码有些地方让我很困扰。最后一个检查35的倍数的if 语句块似乎有些多余。

现在我应该大声说出来并建议重构它还是应该等待面试官说出来?

面试的关键在于时间管理,如何最大限度地发挥你的优点,而不是缺点。如果你非常有信心,相信自己能在可控的时间内拿出更扎实的方案,那就去争取吧。如果有疑问,就等着对方提问。

这样,面试官就决定你剩下的时间可能值得深入探讨这个问题。

如果决定重构很有趣,这可能是一种处理重构步骤的方法

重构

当然,我们可以针对这个特殊的挑战,用一句漂亮的话来表达,但我并不是特别喜欢为了漂亮或漂亮而做事。

所以让我们打开开关,这次我要做的是向你展示我的最终解决方案,并向你介绍我是如何得到这个解决方案的。

如果你需要阅读和理解别人的代码,或者需要向别人解释代码,那么这项技能会非常有用。多年来,我针对这个难题提供了许多解决方案,但下面这个是迄今为止我最喜欢的。

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output =  ( (i % 3) ? '' : 'Fizz' ); // if multiple of 3 is falsy
        output += ( (i % 5) ? '' : 'Buzz') ; // if multiple of 5 is falsy
        console.log(output || i); // output value or i if output is falsy
    }
}
fizzBuzz(1,15);

该解决方案使用三元运算符语法来设置条件,并利用一些对于未经训练的人来说可能一开始不太明显的东西——JavaScript 虚假值。

让我们先从 JavaScript 中的虚值说起,我们到底在说什么呢?Mozilla 开发者网络 (MDN)提供了一个很好的定义:

假值 (falsy) 是指在布尔上下文中遇到时被视为假的值。JavaScript
使用类型转换将任何值强制转换为布尔值,例如条件语句和循环。

对于我们特定的上下文,重要的关键词是“布尔上下文”“条件”,因为它们与我们的解决方案相关。在了解其应用方式之前,以下是 JavaScript 中最常见的假值列表:

  • 布尔值false与字符串'false'不同
  • 数字0 - 这与字符串“0”不同
  • 对象
  • 原始类型undefined分配给未初始化的变量
  • 任何空字符串的表示形式,例如单引号、双引号或反引号。

重写

让我们集中讨论一下 fizzBu​​zz 函数的一部分

if( (i % 3) === 0 ) {
output = 'Fizz';
}
// this could be refactored as
if( !(i % 3) ) output = 'Fizz';

分解重构后的线路,我们得到如下图所示

  • if (...) ==>外部条件构造 - 内部布尔上下文
  • ! ==>为假
  • (i % 3) ==>类型强制 - 将检查值是否为假或真

用几个数字代替i以便更好地理解它

if (!( 1 % 3) ...) /*becomes*/ if (!( 3 ) ...) /*3 is not false or falsy so check fails*/
if (!( 2 % 3) ...) /*becomes*/ if (!( 6 ) ...) /*6 is not false or falsy so check fails*/
if (!( 3 % 3) ...) /*becomes*/ if (!( 0 ) ...) /*0 is not false but is falsy so check passes*/

我现在可以使用上述逻辑重写我的整个函数

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output = i;
        if( !(i % 3) ) output = 'Fizz';
        if( !(i % 5) ) output = 'Buzz';
        if( !(i % 3) && !(i % 5) ) output = 'FizzBuzz';
        console.log(output);
    }
}

当我想到这个解决方案时,我欣喜若狂,但不幸的是,它并没有持续太久。最后一行对我来说仍然是多余的,说实话,这让我很烦。我怎么才能一次性把 3 和 5 的检查合并起来呢?

然后我灵机一动,如果我能从一个空字符串开始,如果它满足条件3,就附加单词“Fizz”,如果它满足条件5,就附加单词“Buzz”,会怎么样?我把这个想法画在了一张纸上。

  • i = 1 ==> 没有 Fizz '' ==> 没有 Buzz '' ==> 输出为 1
  • i = 3 ==> 是 'Fizz' ==> 否 Buzz '' ==> 输出为 'Fizz'
  • i = 5 ==> 没有 Fizz '' ==> 是 'Buzz' ==> 输出是 'Buzz'
  • i = 15 => 是 'Fizz' ==> 是 'Buzz' ==> 输出为 'FizzBu​​zz'

三元运算符将允许以非常简洁的方式在条件检查时分配一个值,在条件检查失败时分配一个替代值。

另一个显而易见的事实是,我们在循环i的值时,输出的要么是字符串,要么是数字。正如我们在上一节中看到的,空字符串是一个假值。那么,我们如何将这些智能转化为可运行的代码呢?

实现这一点的关键在于,输出的值要么是“Fizz”、“Buzz”、“FizzBu​​zz”等字符串之一,要么是假值。如果是假值,i 的值将直接传递。

所以最终重写并添加了更多评论

function fizzBuzz( start = 1, end = 100) {
    for( let i = start; i <= end; i++) {
        let output =  ( (i % 3) ? '' : 'Fizz' ); // output is assigned a value or empty
        output += ( (i % 5) ? '' : 'Buzz') ; // output concatenates the next value
        console.log(output || i); // || or operator if output is falsy will show i value
    }
}
fizzBuzz(1,15);

希望您遵循了所有这些:)这对我来说是一个非常令人满意的解决方案,因为我相信它易于阅读,解决了问题并且带有一些雄辩的 JavaScript。

最后的话

编码练习仅涵盖编码面试中发生的众多事情的一个方面。

正如我所提到的步骤和实现能力,无论问题的复杂程度如何,都需要大量的练习。

不要犹豫,使用模拟面试(我们很快会提供一些 Javascript 形式的模拟面试,但稍后会提供更多)来练习其中的对话方面。

我希望这篇文章对您有用,如果您愿意的话,请分享并发表评论:)

文章来源:https://dev.to/adyngom/how-to-approach-solving-a-challenge-during-a-coding-interview-1gf4
PREV
我希望几年前就知道的 Nginx 概念
NEXT
React Context 指南