Python初中级面试题

版权声明:欢迎读阅 https://blog.csdn.net/weixin_44266137/article/details/90261045

这里的题有些是简单的话述,所以有可能需要你自己整理,自己整理出来的会加深你的记忆,有些我会附上文章,有些就简单而过,直接上干货了,希望对您有帮助。

1.深拷贝和浅拷贝之间的区别是什么?

深拷贝可理解为递归性质的拷贝,其开辟新的内存空间地址,在修改新的拷贝内容不会影响到旧的内容,其完全拷贝原内容的所以。

import copy
b=copy.deepcopy(a)

而浅拷贝则是将一个对象的引用拷贝到另一个对象上,所以如果我们在拷贝中改动,会影响到原对象。我们使用函数copy()执行浅拷贝

b=copy.copy(a)

2.列表和元组之间的区别是?

二者的主要区别是列表是可变的,而元组是不可变的

不同点一:不可变 VS 可变

两种类型除了字面上的区别(括号与方括号)之外,最重要的一点是tuple是不可变类型,大小固定,而 list 是可变类型、数据可以动态变化,这种差异使得两者提供的方法、应用场景、性能上都有很大的区别。

不同点二:同构 VS 异构

tuple 用于存储异构(heterogeneous)数据,当做没有字段名的记录来用,比如用 tuple 来记录一个人的身高、体重、年龄。

person = (“zhangsan”, 20, 180, 80) 比如记录坐标上的某个点

point = (x, y) 而列表一般用于存储同构数据(homogenous),同构数据就是具有相同意义的数据,比如下面的都是字符串类型

[“zhangsan”, “Lisi”, “wangwu”] 再比如 list 存放的多条用户记录

[(“zhangsan”, 20, 180, 80), (“wangwu”, 20, 180, 80)] 数据库操作中查询出来的记录就是由元组构成的列表结构。

因为 tuple 作为没有名字的记录来使用在某些场景有一定的局限性,所以又有了一个 namedtuple 类型的存在,namedtuple 可以指定字段名,用来当做一种轻量级的类来使用。

tuple在性能上也要快于list,就迭代来说,同样大小的数据,tuple占用内存更少

异构:多个不同类型的事物或物体来参与完成密谋一件事,具有数据完整性,安全性。
同构:多个相同类型的事物或物体来参与完成某一件事

简单插入异步与同步

异步:已经做好(完)一件事,去做下一件事,中间不需要等待反馈
同步:已经做好(完)一件事,要等待反馈之后继续走一下步

构:可由同种或不同种的事物构成
步:步调,保持一样或不同的步调

3.Python2和Python3的区别
觉得这个有些鸡肋,版本迭代肯定会带来区别,但必须要知道,问不问就看面试官的了

print语句被python3废弃,只能使用print函数

Python3中字符串是Unicode (utf-8)编码,支持中文做标识符。

python2中是ASCII编码,需要更改字符集才能正常支持中文,所以在.py文件中会看到

#-- coding: UTF-8 --
# -*- encoding: utf-8 -*-

异常处理 Python2中try:…except Exception, e:…,
在Python3中改为了try:…except Exception as e:…

Python3中不再使用xrange方法,只有range方法。
(这里可以说说他俩的区别。一个是range()函数。另一个就是xrange的生成器)

range在Python2中返回列表,而在Python3中返回range可迭代对象。

在Python2中有两个不等运算符!=和<>,在Python3中去掉了<>,只有!=符号表示不等

在Python2中双反引号可以替代repr函数,在Python3中去掉了双反引号的表是方法,只能用repr方法。

StringIO模块现在被合并到新的io模组内。new, md5, gopherlib等模块被删除。

httplib, BaseHTTPServer, CGIHTTPServer, SimpleHTTPServer, Cookie, cookielib被合并到http包内。

取消了exec语句,只剩下exec()函数。

在Python2中long是比int取值范围更大的整数,Python3中取消了long类型,int的取值范围扩大到之前的long类型范围。

列表推导 不再支持[n for n in a,b]语法,改为[n for n in (a,b)]或[n for n in [a,b]]

python 2 中通过input输入的类型是int,只有通过raw_input()输入的类型才是str。

python 3中通过input输入的类型都是str,去掉了row_input()方法。

4.解释一下Python中的三元运算

[on true] if [expression] else [on false]

如果表达式为True,就执行[on true]中的语句。否则,就执行[on false]中的语句

a,b=2,3
min=a if a<b else b
min

判定条件/为真的结果/为假的结果
为真时的结果/判定条件/为假时的结果
先输出结果,在判定条件

5.在Python中如何实现多线程?
一个线程就是一个轻量级进程,多线程能让我们一次执行多个线程。我们都知道,Python是多线程语言,其内置有多线程工具包。

Python中的GIL(全局解释器锁)确保一次执行单个线程。一个线程保存GIL并在将其传递给下个线程之前执行一些操作,这会让我们产生并行运行的错觉。但实际上,只是线程在CPU上轮流运行。当然,所有的传递会增加程序执行的内存压力。

解决多线程之间数据的完整性和状态同步的锁机制
含有GIL的解释器有 CPython、Pypy、Psyco
没有GIL的解释器有JPython、IronPython

单线程锁,确保不会恶意争抢资源,造成死锁
Look,一个一个线程执行

线程,进程的关系
俩个功能在同一个进程中执行任务

子线程,主线程,由于没有守护线程,所以,当主线程结束后,子线程也会跟着结束
join() 等待子线程执行结束,再执行主线程。

6.解释一下Python中的继承
当一个类继承自另一个类,它就被称为一个子类/派生类,继承自父类/基类/超类。它会继承/获取所有类成员(属性和方法)。

继承能让我们重新使用代码,也能更容易的创建和维护应用。Python支持如下种类的继承:

单继承:一个类继承自单个基类
多继承:一个类继承自多个基类
多级继承:一个类继承自单个基类,后者则继承自另一个基类
分层继承:多个类继承自单个基类
混合继承:两种或多种类型继承的混合

继承,是Python类的重要特性之一,现在Python类的特征应该是四大特性:封装、继承、多态、抽象(因为Class类本身就是抽象的)。

父与子的关系,子类会继承父类的所有公有属性及方法。(公有、私有)。
好处说的最多的就是实现了代码的重用,避免代码臃肿,多余

需要注意的是super()方法.
我个人的话述是,如果子类继承父类大于俩个以上,super()方法只继承以mro魔法方法的底层c3算法,由底部向上继承。(说的有点low,您可在仔细查阅总结)

7.在Python中是如何管理内存的
Python有一个私有堆空间来保存所有的对象和数据结构。
作为开发者,我们无法访问它,是解释器在管理它。
但是有了核心API后,我们可以访问一些工具。
Python内存管理器控制内存分配。

另外,内置垃圾回收器会回收使用所有的未使用内存,所以使其适用于堆空间

一、垃圾回收:python不像C++,Java等语言一样,他们可以不用事先声明变量类型而直接对变量进行赋值。对Python语言来讲,对象的类型和内存都是在运行时确定的。这也是为什么我们称Python语言为动态类型的原因(这里我们把动态类型可以简单的归结为对变量内存地址的分配是在运行时自动判断变量类型并对变量进行赋值)。

二、引用计数:Python采用了类似Windows内核对象一样的方式来对内存进行管理。每一个对象,都维护这一个对象指向该对象的引用的计数。当变量被绑定在一个对象上的时候,该变量的引用计数就是1,(还有另外一些情况也会导致变量引用计数的增加),系统会自动维护这些标签,并定时扫描,当某标签的引用计数变为0的时候,该对象就会被回收。

python的内存在底层也是由malloc和free的方式来分配和释放,只是它代替程序员决定什么时候分配什么时候释放,同时也提供接口让用户手动释放,因此它有自己的一套内存管理体系,主要通过两种机制来实现,一个是引用计数,一个是垃圾回收。前者负责确定当前变量是否需要释放,后者解决前者解决不了的循环引用问题以及提供手动释放的接口

垃圾回收(Garbage Collection)python提供了del方法来删除某个变量,它的作用是让某个对象引用数减少1。
当某个对象引用数变为0时并不是直接将它从内存空间中清除掉,而是采用垃圾回收机制gc模块,当这些引用数为0的变量规模达到一定规模,就自动启动垃圾回收,将那些引用数为0的对象所占的内存空间释放。
这里gc模块采用了分代回收方法,将对象根据存活的时间分为三“代”,所有新建的对象都是0代,当0代对象经过一次自动垃圾回收,没有被释放的对象会被归入1代,同理1代归入2代。
每次当0代对象中引用数为0的对象超过700个时,启动一次0代对象扫描垃圾回收,经过10次的0代回收,就进行一次0代和1代回收,1代回收次数超过10次,就会进行一次0代、1代和2代回收。
而这里的几个值是通过查询get_threshold()返回(700,10,10)得到的。此外,gc模块还提供了手动回收的函数,即gc.collect()。

这里可以看看这篇文章,讲的很详细,我上边也有用到大牛的话述,[Python]内存管理

8.解释Python中的help()和dir()函数
help() 函数是一个内置函数,用于查看函数或模块用途的详细说明:
help() 和一些 -help 我觉得大体类似。都有查看帮助的作用。
代码中有一些文档,可查看帮助,让你快速入门级使用

import copy
help(copy.copy)

dir()函数也是Python内置函数,dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。

dir() 可查看内置方法 比如 List中的append等等。
那么查询模块中的方法,要先导入模块

import sys
dir(sys)
# 返回查询对象的所有属性名称的列表

9.什么是猴子补丁?
在运行期间动态修改一个类或模块。

class A:
    def func(self):
        print("Hi")
def monkey(self):
    print("Hi, monkey")
m.A.func = monkey
a = m.A()
a.func()

猴子补丁
猴子补丁是一种让程序行为在运行时拓展或变更的方法
猴子补丁仅指在运行时动态改变类或模块,为的是将第三方代码打补丁在不按预期运行的bug或者feature上
当我们需要对处理某个函数、类的函数进行测试时,被处理的函数和类可能需要精细的构造、长时间的运行,才能出现一些罕见的行为,为此我们通过猴子补丁直接得到这些情况导致的结果,从而测试外部的函数,当然猴子补丁并不只这一种用法

常见问题

当猴子补丁的假设由于代码更新不再为真时,这可能导致一些问题,因此猴子补丁常常在条件下使用
如果两个模块同时打了猴子补丁,只有后打的补丁才有效
源代码和其行为在不知道有猴子补丁的人的视角下会出现矛盾

可参考这篇文章来理解和总结,python中的猴子补丁

10.请解释使用args和*kwargs的含义
当我们不知道向函数传递多少参数时,比如我们向传递一个列表或元组,我们就使用*args。

>>> def func(*args):
    for i in args:
        print(i)  
>>> func(3,2,1,4,7)

在我们不知道该传递多少关键字参数时,使用**kwargs来收集关键字参数。

>>> def func(**kwargs):
    for i in kwargs:
        print(i,kwargs[i])
>>> func(a=1,b=2,c=7)

也可以查看文章,团子的小窝->理解 Python 中的 *args 和 **kwargs

11.什么是负索引?
负索引和正索引不同,它是从右边开始检索。

它也能用于列表中的切片:

mylist=[0,1,2,3,4,5,6,7,8]
mylist[-3]
mylist[-6:-1]

12.解释Python中的join()和split()函数
字符串的常见操作(一般搜索都有,就不一一列举了。自己找要比看记忆深刻)

Join()能让我们将指定字符添加至字符串中。

Split()能让我们用指定字符分割字符串。

13.怎么移除一个字符串中的前导空格?
字符串中的前导空格就是出现在字符串中第一个非空格字符前的空格。我们使用方法Istrip()可以将它从字符串中移除。

'   Ayushi '.lstrip()

可以看到,该字符串既有前导字符,也有后缀字符,调用Istrip()去除了前导空格。如果我们想去除后缀空格,就用rstrip()方法。

'   Ayushi '.rstrip()

14.Python中的pass语句是什么?
在用Python写代码时,有时可能还没想好函数怎么写,只写了函数声明,但为了保证语法正确,必须输入一些东西,在这种情况下,我们会使用pass语句。

def func(*args):
    pass

同样,break语句能让我们跳出循环。

for i in range(7):
    if i==3: break

最后,continue语句能让我们跳到下个循环。

for i in range(7):
    if i==3: continue
    print(i)

可以找一些应用场景,简单聊一聊

15.Python中的闭包是什么?

当一个嵌套函数在其外部区域引用了一个值时,该嵌套函数就是一个闭包。其意义就是会记录这个值。
和装饰器差不多,外函数返回内函数的值

def A(x):
    def B():
        print(x)
    return B

16.解释一下Python中的逻辑运算符

Python中有3个逻辑运算符:and,or,not

个人感觉面试官主要看的还是你的逻辑思维,这么简单的问题,就是 “与、或、非”。结合场景叙述一下最好

17.Python支持什么数据类型?

这里主要列举一些常用的

Numbers(数字)——用于保存数值

Strings(字符串)——字符串是一个字符序列。我们用单引号或双引号来声明字符串。

Lists(列表)——列表就是一些值的有序集合,我们用方括号声明列表。

Tuples(元组)——元组和列表一样,也是一些值的有序集合,区别是元组是不可变的,意味着我们无法改变元组内的值。

Dictionary(字典)——字典是一种数据结构,含有键值对。我们用大括号声明字典

18.什么是切片?
切片是Python中的一种方法,能让我们只检索列表、元素或字符串的一部分。
在切片时,我们使用切片操作符 [ ]。

(1,2,3,4,5)[2:4]

19.Python中的不可变集合(frozenset)是什么?

首先,我们讨论一下什么是集合。集合就是一系列数据项的合集,不存在任何副本。
另外,集合是无序的。

曾经为了这个,我反复试验了无数遍,最后只有字符串显示无序效果最佳

这就意味着我们无法索引它。

不过,集合是可变的。
而不可变集合却不可变,这意味着我们无法改变它的值,从而也使其无法作为字典的键值。

myset=frozenset([1,3,2,2])
myset

点这里看看,不可变集合frozenset,也有很多Python相关的知识点。

20.解释lambda表达式,什么时候会用到它?

如果我们需要一个只有单一表达式的函数,我们可以匿名定义它。
lambda表达式通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。

(lambda a,b:a if a>b else b)(3,3.5)

可配合高阶函数一起使用,在来一篇函数式编程 讲的也很详细。

21.什么是递归?

在调用一个函数的过程中,直接或间接地调用了函数本身这个就叫递归。但为了避免出现死循环,必须要有一个结束条件

一般最大限制次数是998,也有说1000的。但个人认为避免进入1000。主要的是用了之后怎么退出递归。

def facto(n):
    if n==1: return 1
    return n*facto(n-1)
facto(4)

借用一个博主的文章来简单了解一下递归函数

21.什么是生成器?
生成器会生成一系列的值用于迭代,这样看它又是一种可迭代对象。它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。
22.什么是迭代器?
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。我们使用inter()函数创建迭代器。

一般出现生成器,迭代器。不用想都要说他们的区别项,及优缺点。还可以谈到Python2中俩者的区别。下边紧接着来

23.请说说生成器和迭代器之间的区别?

在使用生成器时,我们创建一个函数;
在使用迭代器时,我们使用内置函数iter()和next()。
在生成器中,我们使用关键字‘yield’来每次生成/返回一个对象。
生成器中有多少‘yield’语句,你可以自定义。 每次‘yield’暂停循环时,生成器会保存本地变量的状态。
而迭代器并不会使用局部变量,它只需要一个可迭代对象进行迭代。
使用类可以实现你自己的迭代器,但无法实现生成器。
生成器运行速度快,语法简洁,更简单。
迭代器更能节约内存。

其实这俩点还可以说很多,就涉及到的内容也可以长谈,这里就不多介绍了。再来说yield

24.Python中的yield用法
yield简单说来就是一个生成器,这样函数它记住上次返 回时在函数体中的位置。

也可以说,一个函数中有yield关键字,就是生成器。

[x for x in list ] 和(x for x in list)这俩个的区别也可以简单聊聊。但别说错

25.解释Python的参数传递机制

Python使用按引用传递(pass-by-reference)将参数传递到函数中。如果你改变一个函数内的参数,会影响到函数的调用。这是Python的默认操作。
不过,如果我们传递字面参数,比如字符串、数字或元组,它们是按值传递,这是因为它们是不可变的。

这里有篇文章介绍了Python中函数默认参数与参数传递机制以及最后在stack overflow中查看的js中的参数传递。

26.如何在Python中创建自己的包?

Python中创建包是比较方便的,只需要在当前目录建立一个文件夹,文件夹中包含一个init.py文件和若干个模块文件,其中init.py可以是一个空文件,但还是建议将包中所有需要导出的变量放到all中,这样可以确保包的接口清晰明了,易于使用。

这里我理解为自己创建的工具包,或者叫工具类。

python 创建自己的包
咱们可以把步骤总结成自己的话述。

27.谈一谈Python的装饰器(decorator)
装饰器本质上是一个Python函数,它可以让其它函数在不作任何变动的情况下增加额外功能,装饰器的返回值也是一个函数对象。
它经常用于有切面需求的场景。
比如:插入日志、性能测试、事务处理、缓存、权限校验等。
有了装饰器我们就可以抽离出大量的与函数功能无关的雷同代码进行重用。

装饰器在大多场景下经常使用,所以这也是一道高频面试题。也是基础,理解之后,就可以转化为自己的话述和面试关进行交谈了
赋文章 理解 Python 装饰器看这一篇就够了

我个人理解:就是当多个装饰器同时装饰一个函数时,它的执行顺序我觉得是最为主要的,所以增加功能也要多多注意。

28.冒泡排序

一般是手写,或者说原理,或者是画图。

def bubble_sort(li):
   for i in range(len(li)-1): # i表示第几趟
       for j in range(len(li)-i-1): # j表示图中的箭头
           if li[j] > li[j+1]:
               li[j], li[j+1] = li[j+1], li[j]

29.快排

快速排序,又称划分交换排序,从无序队列中挑取一个元素,把无序队列分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

def quick_sort(li, start, end):
    # 分治 一分为二
    # start=end ,证明要处理的数据只有一个
    # start>end ,证明右边没有数据
    if start >= end:
        return
    # 定义两个游标,分别指向0和末尾位置
    left = start
    right = end
    # 把0位置的数据,认为是中间值
    mid = li[left]
    while left < right:
        # 让右边游标往左移动,目的是找到小于mid的值,放到left游标位置
        while left < right and li[right] >= mid:
            right -= 1
        li[left] = li[right]
        # 让左边游标往右移动,目的是找到大于mid的值,放到right游标位置
        while left < right and li[left] < mid:
            left += 1
        li[right] = li[left]
    # while结束后,把mid放到中间位置,left=right
    li[left] = mid
    # 递归处理左边的数据
    quick_sort(li, start, left-1)
    # 递归处理右边的数据
    quick_sort(li, left+1, end)

if __name__ == '__main__':
    l = [6,5,4,3,2,1]
    # l = 3 [2,1,5,6,5,4]
    # [2, 1, 5, 6, 5, 4]
    quick_sort(l,0,len(l)-1)
    print(l)
    # 稳定性:不稳定
    # 最优时间复杂度:O(nlogn)
    # 最坏时间复杂度:O(n^2)

30.折半查找

二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

def binary_chop(alist, data):
    """
    非递归解决二分查找
    :param alist:
    :return:
    """
    n = len(alist)
    first = 0
    last = n - 1
    while first <= last:
        mid = (last + first) // 2
        if alist[mid] > data:
            last = mid - 1
        elif alist[mid] < data:
            first = mid + 1
        else:
            return True
    return False

涉及到算法,就需要你做足准备了,也就是看你面试的职位和公司对算法的要求,以及这篇文章最后提到的数据结构。

31.关于数据结构

著名的公式

程序 = 数据结构 + 算法

1.什么是数据结构?

简单地说,数据结构是以某种特定的布局方式存储数据的容器。这种“布局方式”决定了数据结构对于某些操作是高效的,而对于其他操作则是低效的。首先我们需要理解各种数据结构,才能在处理实际问题时选取最合适的数据结构。

2.为什么我们需要数据结构?

数据是计算机科学当中最关键的实体,而数据结构则可以将数据以某种组织形式存储,因此,数据结构的价值不言而喻。

无论你以何种方式解决何种问题,你都需要处理数据——无论是涉及员工薪水、股票价格、购物清单,还是只是简单的电话簿问题。

数据需要根据不同的场景,按照特定的格式进行存储。有很多数据结构能够满足以不同格式存储数据的需求。

常见的数据结构

首先列出一些最常见的数据结构,我们将逐一说明:

数组、栈、队列、链表、树、图、字典树(这是一种高效的树形结构,但值得单独说明) 散列表(哈希表)

数组

数组是最简单、也是使用最广泛的数据结构。栈、队列等其他数据结构均由数组演变而来。下图是一个包含元素(1,2,3和4)的简单数组,数组长度为4。

每个数据元素都关联一个正数值,我们称之为索引,它表明数组中每个元素所在的位置。大部分语言将初始索引定义为零

数组的基本操作

Insert——在指定索引位置插入一个元素
Get——返回指定索引位置的元素
Delete——删除指定索引位置的元素
Size——得到数组所有元素的数量

面试中关于数组的常见问题

寻找数组中第二小的元素
找到数组中第一个不重复出现的整数
合并两个有序数组
重新排列数组中的正值和负值

著名的撤销操作几乎遍布任意一个应用。但你有没有思考过它是如何工作的呢?这个问题的解决思路是按照将最后的状态排列在先的顺序,在内存中存储历史工作状态(当然,它会受限于一定的数量)。

这没办法用数组实现。但有了栈,这就变得非常方便了。

可以把栈想象成一列垂直堆放的书。为了拿到中间的书,你需要移除放置在这上面的所有书。这就是LIFO(后进先出)的工作原理。

栈的基本操作

Push——在顶部插入一个元素
Pop——返回并移除栈顶元素
isEmpty——如果栈为空,则返回
true Top——返回顶部元素,但并不移除它

面试中关于栈的常见问题

使用栈计算后缀表达式 对栈的元素进行排序 判断表达式是否括号平衡

队列

与栈相似,队列是另一种顺序存储元素的线性数据结构。栈与队列的最大差别在于栈是LIFO(后进先出),而队列是FIFO,即先进先出。

一个完美的队列现实例子:售票亭排队队伍。如果有新人加入,他需要到队尾去排队,而非队首——排在前面的人会先拿到票,然后离开队伍。

移除先入队的元素、插入新元素

队列的基本操作

Enqueue()——在队列尾部插入元素
Dequeue()——移除队列头部的元素
isEmpty()——如果队列为空,则返回
true Top()——返回队列的第一个元素

面试中关于队列的常见问题

使用队列表示栈
对队列的前k个元素倒序
使用队列生成从1到n的二进制数

链表

链表是另一个重要的线性数据结构,乍一看可能有点像数组,但在内存分配、内部结构以及数据插入和删除的基本操作方面均有所不同。
链表就像一个节点链,其中每个节点包含着数据和指向后续节点的指针。
链表还包含一个头指针,它指向链表的第一个元素,但当列表为空时,它指向null或无具体内容。
链表一般用于实现文件系统、哈希表和邻接表。

链表包括以下类型:

单链表(单向) 双向链表(双向)

链表的基本操作:

InsertAtEnd - 在链表的末尾插入指定元素
InsertAtHead - 在链接列表的开头/头部插入指定元素
Delete - 从链接列表中删除指定元素
DeleteAtHead - 删除链接列表的第一个元素
Search - 从链表中返回指定元素
isEmpty - 如果链表为空,则返回true

面试中关于链表的常见问题

反转链表
检测链表中的循环
返回链表倒数第N个节点
删除链表中的重复项

图是一组以网络形式相互连接的节点。节点也称为顶点。 一对节点(x,y)称为边(edge),表示顶点x连接到顶点y。边可以包含权重/成本,显示从顶点x到y所需的成本。

图的类型
无向图 有向图

在程序语言中,图可以用两种形式表示:
邻接矩阵 邻接表

常见图遍历算法

广度优先搜索 深度优先搜索
(广度优先遍历、深度优先遍历)

面试中关于图的常见问题

实现广度和深度优先搜索
检查图是否为树
计算图的边数
找到两个顶点之间的最短路径

树形结构是一种层级式的数据结构,由顶点(节点)和连接它们的边组成。 树类似于图,但区分树和图的重要特征是树中不存在环路。

树形结构被广泛应用于人工智能和复杂算法,它可以提供解决问题的有效存储机制。

树数据结构中使用的基本术语:
Root - 根节点
Parent - 父节点
Child - 子节点
Leaf - 叶子节点
Sibling - 兄弟节点

以下是树形结构的主要类型:

N元树 平衡树 二叉树 二叉搜索树 AVL树 红黑树 2-3树

其中,二叉树和二叉搜索树是最常用的树。

面试中关于树结构的常见问题:

求二叉树的高度
在二叉搜索树中查找第k个最大值
查找与根节点距离k的节点
在二叉树中查找给定节点的祖先节点

字典树(Trie)

字典树,也称为“前缀树”,是一种特殊的树状数据结构,对于解决字符串相关问题非常有效。它能够提供快速检索,主要用于搜索字典中的单词,在搜索引擎中自动提供建议,甚至被用于IP的路由

这些单词以顶部到底部的方式存储,其中绿色节点“p”,“s”和“r”分别表示“top”,“thus”和“theirs”的底部。

面试中关于字典树的常见问题

计算字典树中的总单词数
打印存储在字典树中的所有单词
使用字典树对数组的元素进行排序
使用字典树从字典中形成单词
构建T9字典(字典树+ DFS )

哈希表

哈希法(Hashing)是一个用于唯一标识对象并将每个对象存储在一些预先计算的唯一索引(称为“键(key)”)中的过程。因此,对象以键值对的形式存储,这些键值对的集合被称为“字典”。可以使用键搜索每个对象。基于哈希法有很多不同的数据结构,但最常用的数据结构是哈希表。

哈希表通常使用数组实现。

欢迎采阅!

猜你喜欢

转载自blog.csdn.net/weixin_44266137/article/details/90261045