AnBento作者陈丹校对陈超翻译

在Python编程面试前需要学会的10个算法(附代码)

本文为大家介绍了最近在Python编程面试中反复出现的10个基础算法问题,并且给出了相应的解答过程。

Photo by Headway on Unsplash为什么练习算法是关键?

如果你是Python新手,并且打算面试顶尖公司(FAANG),听着,你需要从现在开始就好好练习算法。

不要像我第一次练习算法时那么天真。尽管我认为从早到晚死磕算法很有趣,但是我从来没有花过太多时间练习,甚至更少花时间去使用快捷、高效的解决方法。在我看来,我认为花一天的时间解决算法问题有点太傻了,而且在实际工作环境中很不适用,而且长期来看这也不会给我带来多大的收益。

“知道如何解决算法问题将会成为你在找工作过程中极有竞争力的优势”

好吧……我错了(至少在某种程度上来说):我仍然认为花费太多时间在算法上而不注重其他技能远远不能让你找到理想的工作,但是我知道作为一个程序员,复杂的问题总会自己出现在日常的工作当中,因此大公司不得不找到一个标准化的流程来收集应聘者在问题解决和细节技能关注的见解。这意味着知道如何解决算法问题将会成为在找工作的你的一个竞争优势,甚至不那么出名的公司也倾向于采纳这样的评估方法。

那里有一整个世界

在我开始更专注地解决算法问题之后不久,我发现有很多资源可供练习、学习最有效的策略以及为面试做好充足的心理准备,比如以下几个例子:

HackerRank:

https://www.hackerrank.com/interview/interview-preparation-kit

LeetCode :

https://leetcode.com/explore/interview/card/top-interview-questions-easy/

CodingBat :

https://codingbat.com/python

GeeksForGeeks:

https://www.geeksforgeeks.org/python-programming-language/?ref=leftbar

练习顶级的面试问题,这些网站通常会按照公司对算法问题进行分组,并且把人们分享详细的面试经验总结的活跃博客嵌入进去,有时也会提供模拟面试问题作为优选计划(premium plans)的一部分。

例如,LeetCode可以通过特定的公司以及频率筛选顶尖的面试问题。你也可以选择自己觉得合适的试题难度(简单、中等、困难):

来源:https://leetcode.com/problemset/all/

那里有上百道不同的算法问题,这意味着,要做到能够识别出常见的模式并在10分钟以内得出有效的解决方法需要大量的时间和投入。

 “如果你一开始感觉到解决这些算法问题很困难千万不要灰心丧气,这是很正常的事。”

如果你一开始感觉到解决这些算法问题很困难千万不要灰心丧气,这是很正常的事。即使有经验的Python程序员在没有充分的训练之前,也会感觉到有很多算法题很难解。

如果你的面试不如预期并且你才刚开始刷题,也不要沮丧。有很多人会刷好几个月的算法题,并且做有规律地复习才能最终拿下一场面试。

为了在你的练习过程中帮到你,我精选了10个在电话面试过程中反复出现的算法(主要是关于字符串操作和数组)。这些问题的难度大都比较容易,所以这会是一个很好的开始。

请注意我给出的每个问题的解答仅仅是许多潜在解决方法的其中之一,通常是一个蛮力解法(“Brute Force”)。因此请自主选择你自己的解法,尝试在运行时间和所用内存之间找到适当的平衡。

字符串处理

1. 整数逆位输出

# Given an integer, return the integer with reversed digits.
# Note: The integer could be either positive or negative.


def solution(x):
    string = str(x)
    if string[0] == '-':
        return int('-'+string[:0:-1])
    else:
        return int(string[::-1])


print(solution(-231))
print(solution(345))

Output:
-132
543

这是一个预热算法,将帮助您练习切片技巧。实际上,唯一棘手的问题是确保您考虑整数为负数的情况。我已经看到此问题以许多不同的方式呈现,但这通常有更复杂的要求。

2. 平均单词长度

# For a given sentence, return the average word length. 
# Note: Remember to remove punctuation first.


sentence1 = "Hi all, my name is Tom...I am originally from Australia."
sentence2 = "I need to work very hard to learn more about algorithms in Python!"


def solution(sentence):
    for p in "!?',;.":
        sentence = sentence.replace(p, '')
    words = sentence.split()
    return round(sum(len(word) for word in words)/len(words),2)


print(solution(sentence1))
print(solution(sentence2))

Output:

4.2

4.08

要求使用字符串来进行一些简单计算的算法非常常见,因此你需要对.replace()和.split()这些方法非常熟悉,这样才能帮助你删除不想要的字符并且创建单词长度容易计算和求和的单词表。

3. 添加字符串

# Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and num2.
# You must not use any built-in BigInteger library or convert the inputs to integer directly.


#Notes:
#Both num1 and num2 contains only digits 0-9.
#Both num1 and num2 does not contain any leading zero.


num1 = '364'
num2 = '1836'


# Approach 1: 
def solution(num1,num2):
    eval(num1) + eval(num2)
    return str(eval(num1) + eval(num2))
            
print(solution(num1,num2))


#Approach2 
#Given a string of length one, the ord() function returns an integer representing the Unicode code point of the character 
#when the argument is a unicode object, or the value of the byte when the argument is an 8-bit string.


def solution(num1, num2):
    n1, n2 = 0, 0
    m1, m2 = 10**(len(num1)-1), 10**(len(num2)-1)


    for i in num1:
        n1 += (ord(i) - ord("0")) * m1 
        m1 = m1//10        


    for i in num2:
        n2 += (ord(i) - ord("0")) * m2
        m2 = m2//10


    return str(n1 + n2)
print(solution(num1, num2))

Output:

2200

2200

我发现两种方法同样好用:第一种胜在简洁和直观地使用eval()方法对基于字符串的输入进行动态评估,而第二种胜在ord()功能的巧妙使用,来通过其字符的Unicode编码将两个字符串重构成实际的数字。如果你真的要选择其中的一种,我倾向于选择第二种,因为它第一眼看上去更复杂,但是通常在解决需要更高级的字符串处理和计算的“中等”和“困难”算法问题当中非常好用。

4. 第一个不同的字母

# Given a string, find the first non-repeating character in it and return its index. 
# If it doesn't exist, return -1. # Note: all the input strings are already lowercase.


#Approach 1
def solution(s):
    frequency = {}
    for i in s:
        if i not in frequency:
            frequency[i] = 1
        else:
            frequency[i] +=1
    for i in range(len(s)):
        if frequency[s[i]] == 1:
            return i
    return -1


print(solution('alphabet'))
print(solution('barbados'))
print(solution('crunchy'))


print('###')


#Approach 2
import collections


def solution(s):
    # build hash map : character and how often it appears
    count = collections.Counter(s) # <-- gives back a dictionary with words occurrence count 
                                         #Counter({'l': 1, 'e': 3, 't': 1, 'c': 1, 'o': 1, 'd': 1})
    # find the index
    for idx, ch in enumerate(s):
        if count[ch] == 1:
            return idx     
    return -1


print(solution('alphabet'))
print(solution('barbados'))
print(solution('crunchy'))

Output:

1

2

1

###

1

2

1

在这种情况下,也是有两种潜在的解决方法,我猜测如果你是算法小白,第一种看起来更熟悉,因为它是从空字典开始构建的简单计数器。

然而理解第二种方法将会从长期来看帮助你更多,这是因为在这个算法当中我简单地使用了collection.Counter(s)代替创建字符计数器,并且用enumerate(s)代替了range(len(s)),enumerate(s)是一个可以帮助你更好地识别索引地址的函数。

5. 有效回文

# Given a non-empty string s, you may delete at most one character. Judge whether you can make it a palindrome.
# The string will only contain lowercase characters a-z.


s = 'radkar'
def solution(s):
    for i in range(len(s)):
        t = s[:i] + s[i+1:]
        if t == t[::-1]: return True


    return s == s[::-1]
  
solution(s)

Output:

True

“回文数列”问题是一个经典问题,你可能会在很多不同场景都见到它。任务是检查通过移除最多一个字符之后,字符串是否与它的逆向字符串相匹配。当s=’radkar’时,函数返回True,因为除去’k’之后,我们获得单词’radar’是一个回文序列。

数组

6. 单调数组

# Given an array of integers, determine whether the array is monotonic or not.
A = [6, 5, 4, 4] 
B = [1,1,1,3,3,4,3,2,4,2]
C = [1,1,2,3,7]


def solution(nums): 
    return (all(nums[i] <= nums[i + 1] for i in range(len(nums) - 1)) or 
            all(nums[i] >= nums[i + 1] for i in range(len(nums) - 1))) 
  
print(solution(A)) 
print(solution(B)) 
print(solution(C))

Output:

True

False

True

这是另外一个常见的问题,以上提供的解决方法也是非常漂亮的,因为可以用一行解决。当且仅当某一数组单调递增或单调递减时才被称为单调数组,为了评估它,以上算法利用了all()函数,当所有可迭代项为真,则返回True,否则返回FALSE。如果迭代对象是空,all()函数也会返回True。

7. 移动零

#Given an array nums, write a function to move all zeroes to the end of it while maintaining the relative order of 
#the non-zero elements.


array1 = [0,1,0,3,12]
array2 = [1,7,0,0,8,0,10,12,0,4]


def solution(nums):
    for i in nums:
        if 0 in nums:
            nums.remove(0)
            nums.append(0)
    return nums


solution(array1)
solution(array2)

Output:

[1, 3, 12, 0, 0]

[1, 7, 8, 10, 12, 4, 0, 0, 0, 0]

当你在处理数组的时候,.remove()和.append()的方法是“黄金组合”。在这个问题当中,我用他们首先将属于原始数组的零移除,然后把移出的零填到同一个数组的末尾。

8. 填空

# Given an array containing None values fill in the None values with most recent 
# non None value in the array 
array1 = [1,None,2,3,None,None,5,None]


def solution(array):
    valid = 0            
    res = []                 
    for i in nums: 
        if i is not None:    
            res.append(i)
            valid = i
        else:
            res.append(valid)
    return res


solution(array1)

Output:

[1, 1, 2, 3, 3, 3, 5, 5]

在真实面试过程中,我有两次都被问到这个问题。这两次都需要包括边界情况(我在这里为了简化省略了)。在论文当中,这是一个易于创建的算法,但是你需要在脑海中有一个清晰的概念,你到底希望通过这个for循环和if语句实现什么,并且可以轻松地使用None值。

9. 匹配和失匹配单词

#Given two sentences, return an array that has the words that appear in one sentence and not
#the other and an array with the words in common. 


sentence1 = 'We are really pleased to meet you in our city'
sentence2 = 'The city was hit by a really heavy storm'


def solution(sentence1, sentence2):
    set1 = set(sentence1.split())
    set2 = set(sentence2.split())
    
    return sorted(list(set1^set2)), sorted(list(set1&set2)) # ^ A.symmetric_difference(B), & A.intersection(B)


print(solution(sentence1, sentence2))

Output:

(['The','We','a','are','by','heavy','hit','in','meet','our','pleased','storm','to','was','you'],['city', 'really'])

这个问题非常直观,但是该算法利用了一些非常常见的set操作,例如set(), intersection() or &以及symmetric_difference() or ^这些方法都会让你的解题过程更漂亮。如果你是第一次见到它们,请查看一下这个网址:https://www.programiz.com/python-programming/set

10. 质数数据

# Given k numbers which are less than n, return the set of prime number among them
# Note: The task is to write a program to print all Prime numbers in an Interval.
# Definition: A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. 


n = 35
def solution(n):
    prime_nums = []
    for num in range(n):
        if num > 1: # all prime numbers are greater than 1
            for i in range(2, num):
                if (num % i) == 0: # if the modulus == 0 is means that the number can be divided by a number preceding it
                    break
            else:
                prime_nums.append(num)
    return prime_nums
solution(n)

Output:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31] 

我想用另外一个经典问题来结束这一部分。如果你熟悉质数的定义和模运算,就可以轻而易举地找到遍历range(n)的解法。

结论

本文当中我分享了10个在编程面试当中常被问到的Python算法。如果你正在准备一家知名技术公司的面试,这篇文章对你熟悉常见算法模式并且循序渐进到更复杂问题来说,是一个好的开始。顺便请注意本文当中的练习(及其解决方案)只是针对Leetcode和GeekforGeeks上存在的问题稍微做了重新解释。在这个领域我还远非达得到一个专家的水平,因此我呈现的解决方法仅仅是指示性的。

原文标题:

10 Algorithms To Solve Before your Python Coding Interview

原文链接:

https://towardsdatascience.com/10-algorithms-to-solve-before-your-python-coding-interview-feb74fb9bc27

编辑:黄继彦

校对:龚力

THU数据派
THU数据派

THU数据派"基于清华,放眼世界",以扎实的理工功底闯荡“数据江湖”。发布全球大数据资讯,定期组织线下活动,分享前沿产业动态。了解清华大数据,敬请关注姐妹号“数据派THU”。

理论Python
2
相关数据
重构技术

代码重构(英语:Code refactoring)指对软件代码做任何更动以增加可读性或者简化结构而不影响输出结果。 软件重构需要借助工具完成,重构工具能够修改代码同时修改所有引用该代码的地方。在极限编程的方法学中,重构需要单元测试来支持。

暂无评论
暂无评论~