面试技巧篇 – 编码风格 (Coding Style)

Coding Style 在面试中有什么影响?

算法面试已经是绝大多数high-tech公司的敲门砖,这也导致了求职者花费大量时间准备算法刷题,编码风格作为代码的脸面却往往被忽略。 在网上也经常有人吐槽,为什么算法写出来了面试还是没过?这种情况大概率是你的编码风格出卖了你。

对于一个有多年经验的程序员,代码风格是刻在骨子里的。就如同写字一般,写的一首好字的人会更容易给别人留下较好印象。相应的,好的代码风格会让你的面试效果事半功倍,不健康的代码风格却也可能会导致你的面试功亏一篑。要知道,面试官是在寻找自己未来的同事,如果一个人算法能力再强,但写出的代码可读性很差让人头疼,你会愿意让他成为你未来的同事吗?答案是显而易见的。

Coding Style 实例

很多开源的 Coding Style 文档可以在网上直接查找, 比如Google的开源项目风格指南。但对于准备面试,这些文档显得过于冗长,缺少针对性。在一个45分钟的面试中,我们能被问到的问题大多可以在300行代码之内解决,工程量小。

本文将帮助你在10分钟内掌握面试相关的Coding Style技巧。

面试中需不需要写代码注释?

pics

答案是不需要,面试的代码量很小,通常是一眼就可以看懂,面试官不会将注释做为考察代码能力的点。适当添加注释来辅助自己完成算法是被允许的。

命名规则

在所有的编码规则中,命名规则是面试中一定逃不掉且也是对代码可读性影响最大的。只要牢记以下几点命名规则,瞬间就可以让你的形象从代码小白变成老程序员。

使用驼峰式命名法(Camel Case)

下面以leet code上的高频面试题“Longest Substring Without Repeating Characters”的Python代码为例说明不同命名风格之间的差距:

代码小白↓

def substring(s):  # 函数名应遵循 Upper Camel Case 命名规则
  n = len(s)  # 意义不明确的变量命名
  ans = 0  # 不建议的缩写
  mp = {} # 不建议的缩写
  
  i = 0  # 意义不明确的变量命名
  for j in range(n): # 循环中的i, j是约定俗成的方式,是被允许的
    if s[j] in mp:
      i = max(mp[s[j]], i)
      
    ans = max(ans, j - i + 1)
    mp[s[j]] = j + 1

  return ans

潜在的同事↓

def LengthOfLongestSubstring(input_string: str) -> int:  # 函数名遵循 Upper Camel Case 命名规则
  longest_length = 0  # 变量名遵循 Lower Camel Case 命名规则
  hash_map = {}  # 意义明确的变量名
  
  index_left = 0 # 意义明确的变量名
  for index_right in input_string:
    character = input_string[i]
    if character in hash_map:
      index = max(character, index_right)
    
    longest_length = max(longest_length, index_right - index_left + 1)
    hash_map[character] = index_right + 1
    
  return longest_length

从以上两段代码的比较中我们可以看出,在算法完全相同的前提下,良好的命名规则会大大的提升代码的可读性,也是编程经验的最直接的表现。

代码模块化

下面是以经典题目“Letter Combinations of a Phone Number”为例说明如何代码模块化。

代码小白↓

def letterCombinations(digits: str) -> List[str]:
  # 代码格式可读性差
  mapper = {'2' : ['a','b','c'],'3' : ['d','e','f'],'4' : ['g','h','i'],'5' : ['j','k','l'],'6' : ['m','n','o'],'7' : ['p','q','r','s'],'8' : ['t','u','v'],'9' : ['w','x','y','z']}
  l = len(digits)  # 意义不明确的变量命名
  if l == 0:  # 应尽量减少嵌套
    return []
  elif l == 1:
    return mapper[digits]
  else:
    idx = 1
    array = []
    
    # 可读性差
    while idx < l:
      newarray = []  # 变量命名未遵循Lower Camel Case命名规则。
      for combo1 in array:
        newarray.append(combo1)
        for combo2 in mapper[digits[idx]]:
          newarray.append(combo1 + combo2)
      array = copy.deepcopy(newarray)
    idx += 1
    
    return newarray

潜在的同事↓

def LetterCombinations(digits: str) -> List[str]:
  # 代码格式可读性好
  mapper = {'2' : ['a','b','c'],
            '3' : ['d','e','f'],
            '4' : ['g','h','i'],
            '5' : ['j','k','l'],
            '6' : ['m','n','o'],
            '7' : ['p','q','r','s'],
            '8' : ['t','u','v'],
            '9' : ['w','x','y','z']}

  length = len(digits)
  # Corner case test.
  if not length:
    return []
  if length == 1:
    return mapper[digits]
    
  return SearchAlgorithm(digits, hash_map)

# 子函数使代码逻辑更简洁
def SearchAlgorithm(digits: str, hash_map: Dict[str, List[str]])-> List[str]:
  if not digits:
    return []
  if len(digits) == 1:
    return hash_map[digits[0]]

  pivot = int(len(digits) / 2)
  left = SearchAlgorithm(digit_lst[:pivot])
  right = SearchAlgorithm(digit_lst[pivot:])

  result = []
  for i in left:
    for j in right:
      result.append(i+j)

  return result 

从以上两段代码可以看出,合理的运用子函数,会大大提升代码可读性。同时,子函数的使用会更方便递归调用算法的实现。

只要在日常的算法练习中留心命名规则算法模块化这两点,会让你在面试中的表现事半功倍,成为面试官的首选。

主页

下一篇: 面试过程中的高频算法