50-python-interview-questions-and-answers
Roy Lv7

50 Python Interviwe Questions and Answers
mark

This post was written by Cameron Wilson, and was originally published at Educative.io 中文翻译由Roy在cx4首发,如需转载请注明出处。

这篇文章涵盖有关Python编程语言的50个面试问题。

基础部分

1. list和tuple的区别

List Tuple
列表由可变对象组成。 (创建后可以更改的对象) 元组由不可变的对象组成。 (创建后无法更改的对象)
列表占用很大的内存。 元组占用的内存很小。
列表存储在两个内存块中(一个是固定大小的,另一个是可变大小的,用于存储数据) 元组存储在单个内存块中。
创建列表的速度较慢,因为需要访问两个内存块。 创建元组比创建列表快。
列表中的元素可以删除或替换。 元组中的元素无法删除或替换。
列表中的数据存储在[]括号中。例如[1,2,3] 元组的数据存储在()括号中。例如(1,2,3)

只要用户知道在元组中插入了什么,就应该使用元组。假设一所大学将其学生的信息存储在一个数据结构中;为了使此信息保持不变,应将其存储在元组中。由于列表为用户提供了更方便的可访问性,因此在需要存储类似类型的对象时应使用它们。例如,如果杂货店需要将所有乳制品存储在一个字段中,则应使用一个列表。

2. 您如何将列表转换为元组?

1
2
a = [50,"hello","Ok"]
b = (a[0],a[1],a[2])

3. 数组和列表的区别是什么?

List Array
Python列表非常灵活,可以保存任意数据。 Python数组只是C数组的一个薄包装。
列表是Python语法的一部分,因此不需要首先声明它们。 首先需要从其他库(即numpy)中导入或声明数组。
列表还可以以省时的方式快速调整大小。这是因为Python在初始化时会初始化列表中的一些额外元素。 数组无法调整大小。相反,需要将一个数组的值复制到另一个更大的数组中。
列表可以保存异构数据。 数组只能存储同质数据。它们的值具有统一的数据类型。
数学函数不能直接应用于列表。相反,必须将它们分别应用于每个元素。 数组是专门为算术计算而优化的。
由于为列表分配了一些额外的元素,因此可以消耗更多的内存,从而可以更快地附加项目。 由于数组保持其首次初始化时的大小,因此它们是紧凑的。

4. 如何在Python中管理内存?

  1. python中的内存管理由Python专用堆空间管理。所有Python对象和数据结构都位于私有堆中。程序员无权访问此私有堆。 python解释器代替了它。
  2. Python对象的堆空间分配是由Python的内存管理器完成的。核心API允许访问一些工具,以便程序员进行编码。
  3. Python还具有一个内置的垃圾收集器,该垃圾收集器回收所有未使用的内存,并使其可用于堆空间。

    5. 如何在Python中实现多线程?

  4. Python有一个多线程程序包,但是如果您想使用多线程来加快代码速度,那么使用它通常不是一个好主意。
  5. Python具有称为全局解释器锁(GIL)的构造。 GIL确保您的“线程”只能在任何一次执行。线程获取GIL,做一些工作,然后将GIL传递到下一个线程。
  6. 这发生得非常快,因此在人眼看来,您的线程似乎是并行执行的,但实际上它们只是使用相同的CPU内核轮流执行。
  7. 所有这些GIL传递都会增加执行开销。这意味着,如果您想使代码运行更快,那么使用线程包通常不是一个好主意。

    6. 什么是猴子修补?

    在Python中,术语“monkey patch”仅指运行时对类或模块的动态修改。

    7. 什么是lambda函数?举例说明什么时候有用,什么时候没用。

    Lambda函数是一个小的匿名函数,它返回一个对象。
    lambda返回的对象通常被分配给变量或用作其他较大函数的一部分。
    lambda函数不是用于创建函数的常规def关键字,而是使用lambda关键字定义。
    lambda的结构如下所示:
    mark

    使用lambda的目的:

  • Lambda比完整功能更具可读性,因为它可以内联编写。因此,当函数表达式较小时,最好使用lambda。
  • lambda函数的优点在于它们返回函数对象。
  • 当与要求函数对象作为参数的map或filter等函数一起使用时,这使它们很有用。
  • 当表达式超过一行时,Lambda无效。

    8. 什么是pickling和unpickling?

    Pickle模块接受任何Python对象并将其转换为字符串表示形式,然后使用转储函数将其转储到文件中,此过程称为pickling。
    从存储的字符串表示形式检索原始Python对象的过程称为unpickling。

    9. 与(嵌套)Python列表相比,NumPy数组有什么优势?

  1. Python的列表是有效的通用容器。
    它们支持(相当)高效的插入,删除,附加和连接,并且Python的列表表达式使它们易于构造和操作。
  2. 它们有一定的局限性:它们不支持“向量化”操作,例如逐元素加法和乘法,并且它们可以包含不同类型的对象这一事实意味着Python必须存储每个元素的类型信息,并且在操作时必须执行类型调度代码在每个元素上。
  3. NumPy不仅效率更高,也更加方便。您可以免费获得许多矢量和矩阵运算,避免不必要的工作。
  4. NumPy数组更快,您可以使用NumPy,FFT,卷积,快速搜索,基本统计信息,线性代数,直方图等内置大量内容。

    10. 举例说明Python中的继承

    继承允许一个类获取另一类的所有成员(例如属性和方法)。
    继承提供了代码可重用性,使创建和维护应用程序变得更加容易。
    我们从中继承的类称为超类,而继承的类称为派生/子类。
  5. 单一继承:派生类获取超类的单个成员。
  6. 多级继承:从超类A继承的派生类A1,和从B类继承的B2
  7. 层次继承:从一个超类可以继承任意数量的子类。
  8. 多重继承:派生类继承自多个超类。

    11. Python中的多态是什么?

    多态是指采取多种形式的能力。
    因此,例如,如果父类具有一个名为ABC的方法,那么子类也可以具有一个具有相同名称和参数的ABC方法。
    Python允许多态。

12. 解释range()和xrange()之间的区别

在大多数情况下,xrange和range在功能方面完全相同。
它们都提供了一种生成整数列表供您使用的方法。
唯一的区别是range返回一个Python列表对象,而xrange返回一个xrange对象。
这意味着xrange实际上不会像range那样在运行时生成静态列表。
它通过一种称为yield的特殊技术根据需要创建值。
该技术与一种称为生成器的对象一起使用。

13. 解释Flask和Django之间的区别

Django是一个Python网络框架,提供了一个开放源代码的高级框架,“鼓励快速开发和简洁实用的设计”。
快速,安全且可扩展。Django提供了强大的社区支持和详细的文档。

该框架是一个包容性软件包,在您创建应用程序时,您将在其中获得管理面板,数据库界面和目录结构。
此外,它包括许多功能,因此您不必添加单独的库和依赖项。
它提供的一些功能包括用户身份验证,模板引擎,路由,数据库模式迁移等等。

Django框架非常灵活,您可以在其中与大型公司的MVP一起使用。
从某些角度来看,使用Django的一些最大的公司是Instagram,Dropbox,Pinterest和Spotify。


Flask被认为是一个微框架,是一个简约的Web框架。
它电量较低,这意味着它缺少Django等全栈框架提供的许多功能,例如网络模板引擎,帐户授权和身份验证。

Flask极简且轻巧,这意味着您可以在编写代码时添加所需的扩展和库,而不会被框架自动提供。
Flask背后的理念是,它仅提供构建应用程序所需的组件,因此您具有灵活性和控制力。
换句话说,它是不受限制的。
它提供的一些功能包括:一个内置int开发服务器,Restful请求分派,Http请求处理等等。

14. 什么是PYTHONPATH?

它是环境变量,在导入模块时使用。
每当导入模块时,都会查找PYTHONPATH以检查各个目录中是否存在导入的模块。
解释器使用它来确定要加载哪个模块。

15. 什么是PEP8?

PEP代表Python增强提案。
这是一组规则,用于指定如何格式化Python代码以实现最大的可读性。

16. 什么是Python装饰器?

装饰器是Python中的一种设计模式,允许用户在不修改其结构的情况下向现有对象添加新功能。
装饰器通常在要装饰的函数定义之前调用。

17. 什么是初始化init

_init__是Python中的方法或构造函数。
创建类的新对象/实例时,将自动调用此方法以分配内存。
所有类都具有init方法。

18. 什么是三元运算符?

三元运算符是用Python编写条件语句的一种方式。
顾名思义,此Python运算符由三个操作数组成。

注意:三元运算符可以看作是if-else语句的简化的单行版本,用于测试条件。

语法:

三元表达式包含三个操作式:

  1. 条件语句:一个布尔表达式,其值为true或false
  2. true_val:如果表达式的计算结果为true,则将分配一个值。
  3. false_val: 如果表达式的结果为false,则分配一个值。
    1
    var = true_val if condition else false_val

19. Python中的全局变量和局部变量是什么?

全局变量

在函数外部或全局空间中声明的变量称为全局变量。
程序中的任何函数都可以访问这些变量。

局部变量

在函数内部声明的任何变量都称为局部变量。
此变量存在于局部空间而不是全局空间中。

20. Python中的@property是什么?

@property是一个装饰器。
在Python中,装饰器使用户能够以相同的方式使用该类(而不管对其属性或方法所做的更改)。
@property装饰器允许像属性一样访问函数。

21. 在Python中如何使用try/except?

异常是程序执行时发生的错误。
发生错误时,程序将停止并生成异常,捕捉异常然后对其进行处理,以防止程序崩溃。

程序生成的异常在try块中捕获,并在except块中处理。

22. 解释Python 2和Python 3之间的区别

Python2 Python3
字符串编码
Python 2将它们存储为ASCII。
Unicode是ASCII的超集,因此可以编码更多字符,包括外来字符。
字符串编码
Python 3默认将字符串存储为Unicode。
Python 2除法将floor函数应用于十进制输出并返回整数。
因此,将5除以2将返回floor(2.5)= 2。
Python 3中的除法返回期望的输出,即使它是十进制的。
Python 2打印不需要括号。 Python3需要在要打印的内容周围加上括号。
许多较旧的库是专门为Python 2构建的,并不“向上兼容”。 一些较新的库是专门为Python 3构建的,不适用于Python 2。

23. python中的join方法是什么?

Python中的join方法采用可迭代数据结构的元素,并使用特定的字符串连接器值将它们连接在一起。

join是如何工作的?

Python中的join方法是一个字符串方法,它通过使用特定的字符串作为连接器来连接字符串可迭代结构的元素,该结构还包含字符串或字符(数组,列表等)。
mark

24. 什么是字典?

字典表达式是在Python中创建字典的一种方法。
它通过合并以列表或数组形式的两组数据来创建字典。
两个列表/数组之一的数据将作为字典的键,而第二个列表/数组的数据将作为值。
每个键充当每个值的唯一标识符,因此两个列表/数组的大小应相同。

1
NewDictionary = {key:value for (key,value) in iterable}

25. 您将如何在Python中进行深层复制?

深拷贝是指克隆对象。
当使用=运算符时,我们不克隆对象;
相反,我们将变量引用到相同的对象(也称为浅表副本)。

这意味着更改一个变量的值会影响另一个变量的值,因为它们引用(或指向)同一对象。
浅表副本与深表副本之间的区别仅适用于包含其他对象的对象,例如列表和类的实例。

方法

此函数将要克隆的对象作为唯一参数,并返回克隆。

语法

此函数将要克隆的对象作为唯一参数,并返回克隆。

1
a = copy.deepcopy(b)

mark

1
2
3
4
5
6
7
8
9
10
11
12
13
import copy
x = [1,2,3]
y = x
x[0] = 5
x[1] = 15
print("shallow copy:",y)
a = [10,20,30]
b = copy.deepcopy(a)
a[1] = 70
print("deep copy:",b)
Output:
shallow copy:[5,15,3]
deep copy:[10,20,30]

26. 如何检查Python字典中是否存在键?

一种安全的做法是在提取该键的值之前检查该键是否在字典中存在。
为此,Python提供了两个内置函数:
has_key()
如果字典中有给定的键,则has_key方法返回true;否则,返回true
否则,它返回false
if-in表达式
此方法使用if-in语句检查字典中是否存在给定键。

1
2
3
4
5
6
Fruits = ['a':"apple",'b':"banana",'c':"carrot"]
key_to_lookup = 'a'
if key_to_lookup in Fruits:
print("exists")
else:
print("does not exists")

27. 您将如何在Python中实现记忆化?

对于计算量比较大的代码:

1
2
3
4
5
6
7
#Fibonacci Numbers
def fib(num):
if num == 0:
return 0
elif num == 1:
return 1
return fib(num - 1) + fib(n-1)

记忆化可以通过Python装饰器实现
这是完整的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import timeit

def memoize_fib(func):
cache = {}
def inner(arg):
if arg not in cache:
cache[arg] = func(arg)
return cache[arg]
return inner


def fib(num):
if num == 0:
return 0
elif num == 1:
return 1
else:
return fib(num-1) + fib(num-2)

fib = memoize_fib(fib)


print(timeit.timeit('fib(30)', globals=globals(), number=1))
print(timeit.timeit('fib(30)', globals=globals(), number=1))
print(timeit.timeit('fib(30)', globals=globals(), number=1))

28. 您将如何在Python中对字典进行排序?

我们可以通过键或值对这种类型的数据进行排序,这可以通过使用sorted()函数来完成。
首先,我们需要知道如何从字典中检索数据以传递给该函数。

有两种从字典中获取数据的基本方法:

  • Dictionary.keys():仅以任意顺序返回键。
  • Dictionary.values():返回值列表。
  • Dictionary.items():返回所有数据作为键值对列表。

    Sorted()语法

    此方法采用一个强制性参数和两个可选参数:

数据(必填):要排序的数据。
我们将使用上述方法之一传递我们检索到的数据。
键(可选):我们要根据其对列表进行排序的功能(或条件)。
例如,条件可以是根据字符串的长度或任何其他任意函数对字符串进行排序。
此功能应用于列表的每个元素,并对结果数据进行排序。
将其保留为空将根据其原始值对列表进行排序。
反向(可选):将第三个参数设置为true将按降序对列表进行排序。
保留此空的升序排列。
keys()

1
2
3
4
5
6
7
8
9
10
11
dict = {}
dict['1'] = 'apple'
dict['3'] = 'orange'
dict['2'] = 'pango'

lst = dict.keys()

# Sorted by key
print("Sorted by key: ", sorted(lst))
```
`
values()`

dict = {}
dict[‘1’] = ‘apple’
dict[‘3’] = ‘orange’
dict[‘2’] = ‘pango’

lst = dict.values()

#Sorted by value
print(“Sorted by value: “, sorted(lst))

1
`items()`

dict = {}
dict[‘1’] = ‘apple’
dict[‘3’] = ‘orange’
dict[‘2’] = ‘strawberry’

lst = dict.items()

Sorted by key

print(“Sorted by key: “, sorted(lst, key = lambda x : x[0]))

#Sorted by value
print(“Sorted by value: “, sorted(lst, key = lambda x : x[1]))

1
2
3
4
5

### 29. <a name="29">您将如何以及何时使用any()和all()?</a>
#### 什么是`any()`?
`any()`是一个函数,它接受一个可迭代的函数(例如列表,元组,集合等),并且如果任何元素的值为真,则返回`True`,但如果所有元素的值为`False`,则返回`False`。
可以通过以下方式将`iterable`传递给`any()``,以检查是否有任何元素为`True`:

one_truth = [True, False, False]
three_lies = [0, ‘’, None]
print(any(one_truth))
print(any(three_lies))

1
2
3
#### 什么是`all()`?
`all()`是另一个Python函数,该函数具有一个可迭代的函数,如果所有元素的评估结果均为`True`,则返回`True`,否则返回`False`。
与`any()`类似,`all()`接受列表,元组,集合或任何可迭代的对象,如下所示:

all_true = [True, 1, ‘a’, object()]
one_true = [True, False, False, 0]
all_false = [None, ‘’, False, 0]
print(all(all_true))
print(all(one_true))
print(all(all_false))

1
2
3
4
5
6
7
8
9
10
### 30. <a name="30">什么是Python文档字符串?</a>
Python文档字符串提供了一种将文档与以下内容相关联的合适方法:
- Python模块
- Python函数
- Python类
这是书面代码的指定文件。
与常规代码注释不同,篡改应描述函数的功能,而不是功能的作用。
可以使用以下命令访问文档字符串
`__doc__`对象的方法
`help`函数

def Examplefunc(str): #function that outputs the str parameter
print(“The value is”, str)
#no return statement needed in this function

def Multiply(x,y): #function that computes the product of x and y
return x*y #returning the product of x and y

#Calling the functions
Examplefunc(9) #9 passed as the parameter)
answer = Multiply(4,2) #4 and 2 passed as the parameters
print(“The product of x and y is:”,answer)

1
2
3
4
5
6
7
8
9
10
11
12
### 32. <a name="32">解释Python中生成器和迭代器之间的区别。</a>
Python中的迭代器充当对象的持有者,以便可以对其进行迭代;
生成器有助于创建自定义迭代器。
![mark](http://imgs.yrzdm.com/imgs/20200619/kdc8sxg1Do1G.png?imageslim)
除了明显的语法差异外,还有一些值得注意的差异:
| Generator | Iterator |
| --- | --- |
| 使用函数实现 | 使用类实现 |
| 使用`yield`关键字 | 不使用`yield`关键字 |
| 代码简洁 | 代码不简洁 |
| 存储yield语句之前的所有局部变量。 | 没有使用局部变量 |
#### 迭代器的实现

Function to generate all the non-negative numbers

up to the given non-negative number.

class UpTo:
# giving the parameter a default value of 0
def init(self, max = 0):
self.max = max
def iter(self):
self.n = 0
return self
def next(self):
# The next method will throw an
# exception when the termination condition is reached.
if self.n > self.max:
raise StopIteration
else:
result = self.n
self.n += 1
return result
for number in UpTo(5):
print(number)

1
#### 生成器的实现

Function to generate all the non-negative numbers

up to the given non-negative number

def upto(n):
for i in range(n+1):
# The yield statement is what makes a function
# a generator
yield i
for number in upto(5):
print(number)

1
2
3
4
5
6
7
8
### 33. <a name="33">Python中的defaultdict是什么?</a>
Python字典dict包含单词和含义以及任何数据类型的键值对。
`defaultdict`是内置`dict`类的另一个细分。
`defaultdict`可以通过给出其声明来创建,该声明可以具有三个值。
列出,设置或诠释。
根据指定的数据类型,将创建字典,并且当添加或访问`defaultdict`中不存在的任何键时,将为其分配默认值,而不是给出`KeyError`。
举例:
下面的第一个代码段显示了一个简单的字典,以及如何访问dict中不存在的键时如何产生错误。

dict_demo = dict()
print(dict_demo[3])

1
现在,让我们介绍一个`defaultdict`,看看会发生什么。

from collections import defaultdict
defaultdict_demo = defaultdict(int)
print(defaultdict_demo[3])
Output: 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在这种情况下,我们将`int`作为数据类型传递给`defaultdict`。
因此,将为`defaultdict_demo`中不存在的任何键分配一个值0,除非为其定义了值。
>注意:您也可以将set或list作为参数

### 34. <a name="34">什么是Python模块?</a>
Python模块是一个Python文件,其中包含要在应用程序中使用的一组函数和变量。
变量可以是任何类型(数组,字典,对象等)
模块可以是:
- 内建
- 用户自定义
#### Python模块的好处
在Python中创建和使用模块有两个主要好处:
##### 结构化代码
- 通过将代码分组到一个Python文件中,逻辑上将代码组织起来,这使开发更加容易且不易出错。
代码更易于理解和使用。
##### 可重用性
- 单个模块中定义的功能可以被应用程序的其他部分轻松地重用。
这样就无需重新创建重复的代码。
### 35. <a nama="35">如何将列表转换为数组</a>

import numpy as np
my_list = [2,4,6,8,10]
my_array = np.array(my_list)
print(my_array)
print(type(my_array))
[2 4 6 8 10]

1
2
3
4
5
6
7
8
9
10
11
===

### 36. <a name="36">反转Python中的字符串</a>
`stringname[strlength::-1]`
`stringname[::-1]`
### 37. <a name="37">检查Python字符串是否包含另一个字符串</a>
有两种检查方法。
对于本文,我们将研究`find`方法。
`find`方法检查字符串是否包含子字符串。
如果是这样,则该方法返回字符串中子字符串的起始索引;否则,返回0
否则,返回-1

string.find(substring)
a_string=”Python Programming”
substring1=”Programming”
substring2=”Language”
print(“Check if “+a_string+” contains “+substring1+”:”)
print(a_string.find(substring1))
print(“Check if “+a_string+” contains “+substring2+”:”)
print(a_string.find(substring2))

1
2
3
4
检查字符串是否包含另一个字符串的其他两种值得注意的方法是在操作符中使用`in`或使用`count`方法。
### 38. <a name="38">在Python中实施广度优先搜索(BFS)</a>
考虑以下代码中实现的图:
![mark](http://imgs.yrzdm.com/imgs/20200619/ep9Ftu1SirWW.png?imageslim)

graph = {
‘A’ : [‘B’,’C’],
‘B’ : [‘D’, ‘E’],
‘C’ : [‘F’],
‘D’ : [],
‘E’ : [‘F’],
‘F’ : []
}

visited = [] # List to keep track of visited nodes.
queue = [] #Initialize a queue

def bfs(visited, graph, node):
visited.append(node)
queue.append(node)

while queue:
s = queue.pop(0)
print (s, end = “ “)

for neighbour in graph[s]:
  if neighbour not in visited:
    visited.append(neighbour)
    queue.append(neighbour)

Driver Code

bfs(visited, graph, ‘A’)
Output: A B C D E

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 第3-10行:图示的图表使用邻接表表示。  
在Python中执行此操作的一种简单方法是使用字典数据结构,其中每个顶点都有其相邻节点的存储列表。
- 第12行:受访者是用于跟踪受访节点的列表。
- 第13行:queue是一个列表,用于跟踪队列中当前的节点。
- 第29行:bfs函数的参数是访问列表,字典形式的图形和起始节点A。
- 第15-26行:bfs遵循上述算法:
它检查起始节点并将其附加到访问列表和队列中。
然后,当队列包含元素时,它将继续从队列中取出节点,如果未访问该节点的邻居,则将该节点的邻居添加到队列中,并将其标记为已访问。
这一直持续到队列为空。
##### 时间复杂度
由于访问了所有的节点和顶点,因此图上BFS的时间复杂度为O(V + E);
其中V是顶点数,E是边数。
### 39. <a name="39">在Python中实施深度优先搜索(DFS)</a>
考虑下面的代码,该代码在以下代码中实现:
![mark](http://imgs.yrzdm.com/imgs/20200619/ep9Ftu1SirWW.png?imageslim)

Using a Python dictionary to act as an adjacency list

graph = {
‘A’ : [‘B’,’C’],
‘B’ : [‘D’, ‘E’],
‘C’ : [‘F’],
‘D’ : [],
‘E’ : [‘F’],
‘F’ : []
}
visited = set() # Set to keep track of visited nodes.
def dfs(visited, graph, node):
if node not in visited:
print (node)
visited.add(node)
for neighbour in graph[node]:
dfs(visited, graph, neighbour)

Driver Code

dfs(visited, graph, ‘A’)
Output: A B D E F C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 第2-9行:使用邻接表表示图示的图形-在Python中执行此操作的一种简单方法是使用字典数据结构。
每个顶点都有一个存储其相邻节点的列表。
- 第11行:受访者集,用于跟踪受访节点。
- 第21行:调用dfs函数并将其传递给被访问的集合,字典形式的图形以及作为起始节点的A。
- 第13-18行:dfs遵循上述算法:
1. 它首先检查当前节点是否未访问-如果是,则将其附加到已访问集合中。
2. 然后,对于当前节点的每个邻居,再次调用dfs函数。
3. 当访问所有节点时,将调用基本情况。
然后函数返回。
##### 时间复杂度
由于访问了所有节点和顶点,因此图上DFS的平均时间复杂度为O(V + E),其中V是顶点数,E是边数。
对于树上的DFS,时间复杂度为O(V),其中V是节点数。
### 40. <a name="40">在Python中实现通配符</a>
在Python中,您可以使用regex(正则表达式)库实现通配符。
dot `.`字符代替问号`?`符号。
因此,要搜索与颜色模式匹配的所有单词,代码将如下所示。

Regular expression library

import re

Add or remove the words in this list to vary the results

wordlist = [“color”, “colour”, “work”, “working”,
“fox”, “worker”, “working”]

for word in wordlist:
# The . symbol is used in place of ? symbol
if re.search(‘col.r’, word) :
print (word)
Output:color

1
### 41. <a name="41">在Python中实施合并排序</a>

def mergeSort(myList):
if len(myList) > 1:
mid = len(myList) // 2
left = myList[:mid]
right = myList[mid:]

# Recursive call on each half
mergeSort(left)
mergeSort(right)

# Two iterators for traversing the two halves
i = 0
j = 0

# Iterator for the main list
k = 0

while i < len(left) and j < len(right):
    if left[i] < right[j]:
      # The value from the left half has been used
      myList[k] = left[i]
      # Move the iterator forward
      i += 1
    else:
        myList[k] = right[j]
        j += 1
    # Move to the next slot
    k += 1

# For all the remaining values
while i < len(left):
    myList[k] = left[i]
    i += 1
    k += 1

while j < len(right):
    myList[k]=right[j]
    j += 1
    k += 1

myList = [54,26,93,17,77,31,44,55,20]
mergeSort(myList)
print(myList)
Output: [17, 20, 26, 31, 44, 54, 55, 77, 93]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
##### 说明
这是用于实现合并排序的递归方法。
通过此方法获得排序数组所需的步骤可以在下面找到:
1. 在每个递归调用中,该列表分为左右两部分,直到获得两个相邻元素为止。
2. 现在开始排序过程。
i和j迭代器遍历每个调用中的两个部分。
k迭代器遍历整个列表并在此过程中进行更改。
3. 如果i处的值小于j处的值,则将left [i]分配给myList [k]插槽,并递增i。
如果不是,则选择right [j]。
4. 这样,通过k分配的值将全部排序。
5. 在此循环结束时,可能没有完全遍历一半。
它的值仅分配给列表中的其余插槽。
##### 时间复杂度
该算法在O(n.logn)中起作用。
这是因为列表在log(n)个调用中被拆分,并且合并过程在每个调用中花费线性时间。
### 42. <a name="42">在Python中实现Dijkstra的算法</a>
##### 基本算法
1. 从每个未访问的顶点中,选择距离最小的顶点并进行访问。
2. 更新已访问顶点的每个相邻顶点的距离,该顶点的当前距离大于其总和以及它们之间边缘的权重。
3. 重复步骤12,直到访问了所有顶点。

import sys

Function to find out which of the unvisited node

needs to be visited next

def to_be_visited():
global visited_and_distance
v = -10

Choosing the vertex with the minimum distance

for index in range(number_of_vertices):
if visited_and_distance[index][0] == 0
and (v < 0 or visited_and_distance[index][1] <=
visited_and_distance[v][1]):
v = index
return v

Creating the graph as an adjacency matrix

vertices = [[0, 1, 1, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]]
edges = [[0, 3, 4, 0],
[0, 0, 0.5, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]]

number_of_vertices = len(vertices[0])

The first element of the lists inside visited_and_distance

denotes if the vertex has been visited.

The second element of the lists inside the visited_and_distance

denotes the distance from the source.

visited_and_distance = [[0, 0]]
for i in range(number_of_vertices-1):
visited_and_distance.append([0, sys.maxsize])

for vertex in range(number_of_vertices):

Finding the next vertex to be visited.

to_visit = to_be_visited()
for neighbor_index in range(number_of_vertices):
# Calculating the new distance for all unvisited neighbours
# of the chosen vertex.
if vertices[to_visit][neighbor_index] == 1 and
visited_and_distance[neighbor_index][0] == 0:
new_distance = visited_and_distance[to_visit][1]
+ edges[to_visit][neighbor_index]
# Updating the distance of the neighbor if its current distance
# is greater than the distance that has just been calculated
if visited_and_distance[neighbor_index][1] > new_distance:
visited_and_distance[neighbor_index][1] = new_distance
# Visiting the vertex found earlier
visited_and_distance[to_visit][0] = 1

i = 0

Printing out the shortest distance from the source to each vertex

for distance in visited_and_distance:
print(“The shortest distance of “,chr(ord(‘a’) + i),
“ from the source vertex a is:”,distance[1])
i = i + 1

Output:
The shortest distance of a from the source vertex a is: 0
The shortest distance of b from the source vertex a is: 3
The shortest distance of c from the source vertex a is: 3.5
The shortest distance of d from the source vertex a is: 4.5

1
### 43. <a name="43">合并两个排序列表</a>

Merge list1 and list2 and return resulted list

def merge_lists(lst1, lst2):
index_arr1 = 0
index_arr2 = 0
index_result = 0
result = []

for i in range(len(lst1)+len(lst2)):
    result.append(i)
# Traverse Both lists and insert smaller value from arr1 or arr2
# into result list and then increment that lists index.
# If a list is completely traversed, while other one is left then just
# copy all the remaining elements into result list
while (index_arr1 < len(lst1)) and (index_arr2 < len(lst2)):
    if (lst1[index_arr1] < lst2[index_arr2]):
        result[index_result] = lst1[index_arr1]
        index_result += 1
        index_arr1 += 1
    else:
        result[index_result] = lst2[index_arr2]
        index_result += 1
        index_arr2 += 1
while (index_arr1 < len(lst1)):
    result[index_result] = lst1[index_arr1]
    index_result += 1
    index_arr1 += 1
while (index_arr2 < len(lst2)):
    result[index_result] = lst2[index_arr2]
    index_result += 1
    index_arr2 += 1
return result

print(merge_lists([4, 5, 6], [-2, -1, 0, 7]))
Output: [-2, -1, 0, 4, 5, 6, 7]

1
2
3
4
5
6
7
8
9
10
11
上面的解决方案是解决此问题的更直观的方法。
1. 首先创建一个新的空列表。
该列表将按排序顺序填充两个列表的所有元素,然后返回。
2. 然后将三个变量初始化为零以存储每个列表的当前索引。
3. 然后在每个列表的当前索引处比较两个给定列表的元素,将较小的列表追加到新列表,然后将该列表的索引增加1
4. 重复直到到达其中一个列表的末尾,然后将另一个列表追加到合并列表中。
##### 时间复杂度
此算法的时间复杂度为O(n + m)O(n + m),其中nn和mm是列表的长度。
这是因为两个列表都至少迭代了一次。
请注意,也可以通过就地合并解决此问题。
### 44. <a name="44">查找两个加起来为“ k”的数字</a>

def binarySearch(a, item, curr):
first = 0
last = len(a) - 1
found = False
index = -1
while first <= last and not found:
mid = (first + last) // 2
if a[mid] == item:
index = mid
found = True
else:
if item < a[mid]:
last = mid - 1
else:
first = mid + 1
if found:
return index
else:
return -1

def findSum(lst, k):
lst.sort()
for j in range(len(lst)):
# find the difference in list through binary search
# return the only if we find an index
index = binarySearch(lst, k -lst[j], j)
if index is not -1 and index is not j:
return [lst[j], k -lst[j]]

print(findSum([1, 5, 3], 2))
print(findSum([1, 2, 3, 4], 5))
Output:
None
[1,4]

1
2
3
4
5
6
7
8
9
10
11
12
13
您可以通过首先对列表进行排序来解决此问题。
然后,对于列表中的每个元素,使用二进制搜索来查找该元素与预期总和之间的差异。
换句话说,如果预期总和为`k`,而排序列表的第一个元素为`a0`
我们将进行二进制搜索`a0`
重复搜索直到找到一个。
您可以根据需要以递归或迭代的方式实现`binarySearch()`函数。
##### 时间复杂度
由于大多数基于比较的最佳排序函数均采用O(nlogn),因此我们假设Python .sort()函数采用相同的排序方式。
此外,由于二进制搜索要花费O(logn)时间来查找单个元素,因此对所有n个元素进行二进制搜索都将花费O(nlogn)时间。


### 45. <a name="45">查找列表中的第一个非重复整数</a>
在这里,您可以使用Python字典来记录重复次数。

[9,2,3,2,6,6]
def findFirstUnique(lst):
counts = {} # Creating a dictionary
# Initializing dictionary with pairs like (lst[i],(count,order))
counts = counts.fromkeys(lst, (0,len(lst)))
order = 0
for ele in lst:
# counts[ele][0] += 1 # Incrementing for every repitition
# counts[ele][1] = order
counts[ele] = (counts[ele][0]+1 , order)
order += 1 # increment order
answer = None
answer_key = None
# filter non-repeating with least order
for ele in lst:
if (counts[ele][0] is 1) and (answer is None):
answer = counts[ele]
answer_key = ele
elif answer is None:
continue
elif (counts[ele][0] is 1) and (counts[ele][1] < answer[1]):
answer = counts[ele]
answer_key = ele
return answer_key
print(findFirstUnique([1, 1, 1, 2]))
Output: 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
counts字典中的键是给定列表的元素,值是每个元素出现在列表中的次数。
我们返回在第23行的列表中最多出现一次的元素。我们需要保持元组值中每个键的更新顺序。
##### 时间复杂度
由于该列表仅被迭代两次,并且使用线性时间复杂度来初始化计数字典,因此该解决方案的时间复杂度是线性的,即O(n)。

### 46. <a name="46">查找链接列表的中间值</a>
在这里,您可以使用两个可以同时工作的指针。
这样想:
- 快速指针一次移动两步,直到列表结尾
- 慢速指针一次移动一步
- 当快速指针到达末尾时,慢速指针将在中间
使用此算法,由于长度和遍历直到中间的遍历的计算是并行进行的,因此可以使过程更快。
##### 时间复杂度
您以两倍的速度遍历链表,因此它肯定更快。
但是,瓶颈复杂度仍然是O(n)。
### 47. <a name="47">反转队列的第一个“ k”元素</a>
##### 说明
检查输入是否无效,即队列是否为空,`k`是否大于队列,以及在队列`k`是否为负
如果输入有效,则从创建堆栈开始。
可用的堆栈功能有:
- 构造函数:myStack()
- 推送元素:push(int)将元素添加到堆栈中。
- 弹出元素:pop()从堆栈中删除或弹出顶部元素。
- 检查是否为空:如果堆栈为空,则isEmpty()返回true,否则返回false。
- 返回:back()返回在末尾添加的元素,而不将其从堆栈中删除。
- 返回front:front()返回顶部元素(已在开头添加),而不将其从堆栈中删除。
我们的函数`reverseK(queue,k)`将queue作为输入参数。
`k`表示我们要反转的元素数。
可用的队列功能是:
- 构造函数:myQueue(size)size应该是一个整数,指定队列的大小。
- 入队:enqueue(int)
- 出队:dequeue()
- 检查是否为空:isEmpty()
- 检查尺寸:size()
现在,继续进行实际的逻辑,从队列的前面使前`k`个元素出队,并使用第8行中的`stack.push(queue.dequeue())`将它们推入我们先前创建的堆栈中。
将所有`k`个值都压入堆栈后,开始弹出它们并将它们按顺序排入队列的后面。
我们将在第12行中使用`queue.enqueue(stack.pop())`进行此操作。在此步骤的最后,我们将得到一个空堆栈,并将`k`个反向元素添加到队列的后面。
现在我们需要将这些反向元素移到队列的最前面。
为此,我们在第16行中使用了`queue.enqueue(queue.dequeue())`。每个元素都首先从背面出队。
### 48. <a name="48">查找二叉搜索树(BST)的高度</a>
在这里,您可以使用递归来找到左侧和右侧子树的高度。
##### 说明
在这里,如果给定节点为None,则返回-1
然后,我们在左右子树上调用findHeight()函数,并返回具有更大值加1的子树。如果给定节点为None,则不返回0,因为叶节点的高度为0
时间复杂度
代码的时间复杂度为O(n)O(n),因为必须遍历整个树的所有节点。
### 49. <a name="49">将最大堆转换为最小堆</a>

def minHeapify(heap, index):
left = index * 2 + 1
right = (index * 2) + 2
smallest = index
# check if left child exists and is less than smallest
if len(heap) > left and heap[smallest] > heap[left]:
smallest = left
# check if right child exists and is less than smallest
if len(heap) > right and heap[smallest] > heap[right]:
smallest = right
# check if current index is not the smallest
if smallest != index:
# swap current index value with smallest
tmp = heap[smallest]
heap[smallest] = heap[index]
heap[index] = tmp
# minHeapify the new node
minHeapify(heap, smallest)
return heap

def convertMax(maxHeap):
# iterate from middle to first element
# middle to first indices contain all parent nodes
for i in range((len(maxHeap))//2, -1, -1):
# call minHeapify on all parent nodes
maxHeap = minHeapify(maxHeap, i)
return maxHeap

maxHeap = [9, 4, 7, 1, -2, 6, 5]
print(convertMax(maxHeap))
Output: [-2, 1, 5, 9, 4, 6, 7]

```
说明
请记住,我们可以将给定的maxHeap视为元素的常规列表,并对其进行重新排序,以使其准确表示最小堆。
我们在此解决方案中正是这样做的。
convertMax()函数通过在每个父节点上调用minHeapify()函数来从最低父节点还原所有节点上的堆属性。
时间复杂度
minHeapify()函数被调用堆中一半的节点。
minHeapify()函数花费O(log(n))时间,并被调用

50. 检测链表中的循环

说明
遍历整个链表,并将每个访问的节点添加到visit_nodes集合中。
在每个节点上,我们检查它是否已被访问。
原则上,如果重新访问节点,则存在一个循环!

时间复杂度

我们对列表进行一次迭代。
平均而言,集合中的查找需要O(1)时间。
因此,该算法的平均运行时间为O(n)。
但是,在最坏的情况下,查找可能会增加到O(n),这将导致算法在

  • 本文标题:50-python-interview-questions-and-answers
  • 本文作者:Roy
  • 创建时间:2020-06-16 13:31:34
  • 本文链接:https://www.yrzdm.com/2020/06/16/50-python-interview-questions-and-answers/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!