测试面试准备

字节跳动面经

字节跳动游戏面经(一、二、三+boss(hr)面)

内推链接

面试+总结

总结


机器学习

Python基础

C++

计算机网络

操作系统

计算机组成

数据库

sql语句

Linux命令

git

数据结构

字符串

数组

链表

二叉树

矩阵

算法

测试实例

软件、产品知识

逻辑思维


机器学习

KNN 和 K-Means 算法区别

Python语言基础知识

1、深拷贝和浅拷贝

直接复制:所有元素都是地址拷贝
浅拷贝:可变对象是值拷贝,不可变对象是地址拷贝
深拷贝:所有元素都是值拷贝

可变对象,list,dict,set
不可变对象,tuple,int,float,str,long 等数据类型

a = [1,2,3,4,[5,6]]
b = a

# list自带拷贝,浅拷贝
c = a.copy()

# 浅拷贝
d = copy.copy(a)

#深拷贝
e = copy.deepcopy(a)

a.append(7)
a[4].append(8)
a[2] = 10

在这里插入图片描述

? 手写深拷贝

使用isinstance()进行类型判断
判断是否是可变类型,list,dict,set({ }开头 )
或是可包含多种其他类型的 tuple

def deepcopy(cls):
    if isinstance(cls,list):
        li =[]
        for item in cls:
            # 可变数据结构中的每一个元素,进行递归调用
            li.append(deepcopy(item))
        return li
    elif isinstance(cls,dict):
        dic = {}
        for k,v in cls.items():
            dic[k] = deepcopy(v)
        return dic
    elif isinstance(cls,tuple):
        li = []
        for item in cls:
            li.append(deepcopy(item))
        return li
    else:
        return cls

res = [1,4,3,[3,2,4],{1:'d',3:'t'},('j','a')]

ans = deepcopy(res)
print(ans)

2、python多线程

为什么python多线程不是真正意义上的多线程

GIL 全局解释器锁保证每次只有一个线程运行,虽然是4核,但是同一时间仍只能运行一个线程,不过是使用轮转

from threading import Thread

def loop():
	while(True):
		pass
# 创建三个线程
for i in range(3):
	t = Thread(target = loop)
	t.start()
	
while(True):
	pass

python 多进程可以利用多核

#coding=utf-8
from multiprocessing import Pool
from threading import Thread

from multiprocessing import Process


def loop():
    while True:
        pass

if __name__ == '__main__':

    for i in range(3):
        t = Process(target=loop)
        t.start()

    while True:
        pass

python不适合计算密集型任务,可以使用多进程实现多核任务,多个python拥有各自的GIL锁,互不影响

3、垃圾回收机制 参考

  • 引用计数 (reference counting)主要
    每个对象都有一个 ob_refcnt 最为引用计数。
    有新引用时,加1。
    引用它的对象被删除后,计数减1
    计数为0时,对象被回收

    sys.getrefcount(a)查看对象引用计数

    计数+1:
    创建、引用、作为参数
    计数-1:
    别名销毁 def a
    别名赋予新对象: a = 24
    离开作用域

    python对-5~256建立了整数常量池

    优缺点
    优点:简单、高效
    缺点:资源消耗大,循环引用问题(不在运行对象,相互调用,计数永远都是为1,垃圾回收无法进行回收)

  • 标记清除(mark - Sweep)
    为解决计数引用中的循环引用问题
    对象为节点,引用为边构成有向图

  1. 标记阶段
    从根对象(全局变量、调用栈)出发,遍历所有对象,可以到达的对象进行标记
  2. 回收阶段
    遍历结束,未到达对象为非活动对象,会被回收
  • 分代回收
    建立了三个代,代表不同的存活时间,分为年轻代(第0代),中年代(第二代),老年代(第三代),对应3个链表,越年轻的代代表越容易被回收。

当年轻代链表总数达到上限,垃圾回收出发,未被回收的对象就转到中年代。以此类推

建立在标记清除基础上

4、list、tuple、set、dict底层结构 参考1参考2

  • list 列表(可变长的数组)
    顺序表或链表实现 (动态顺序表,单链表、双链表)
    对其他对象的引用组成的连续数组

  • tuple 元组
    与列表类似,但元组不可变,一旦创建不能修改
    大数量集时,tuple和list速度差不多
    小数量集时,tuple会有对象池

  • dict 字典
    数据结构:散列表(哈希表)
    键:对
    只有可哈希对象才能作为键(不可变对象)
    可变对象:列表、字典、集合

    建立哈希表过程:
    1)数据添加
    将key利用哈希函数转化为整数数字,用整数数字对数组长度进行取余,结果作为下标。
    若该结果已有数据,产生哈希冲突,使用哈希冲突解决

    2) 数据查询
    用哈希函数将key转化为数组下标,定位查询

  • set 集合
    数据结构:散列表
    去除重复的值
    没有键的引用,只有值的引用。其他和字典相同

    去重实现:

    1. __hash__函数 :判断两个变量哈希值是否相同
    2. __eq__方法:两函数哈希值相同,判断变量是否是同一个,True去重,False不去重

5、面向对象的特征 参考

  • 封装
    对全局作用域隐藏内部实现
    __开头变为私有变量,只有内部可以访问
    **XXX**为特殊变量,可以访问

  • 继承
    父类、子类
    子类可以继承父类的属性(私有属性不会)、重写父类方法、重写object方法

继承属性

#父类
class Person():
    def __init__(self,name=None,age=None,sex=None):
        self.name=name
        self.age=age
        self.sex=sex
    def eat(self):
        print("%s正在吃饭"%self.name)

#学生子类:继承人类父类的属性
class Student(Person):
    #子类的初始化参数要和父类的一样,否则没有办法给父类传参,会报错
    def __init__(self,name=None,age=None,sex=None,score=None):
        # self.name=name
        # self.age=age
        # self.sex=sex
        #上面三行的代码等价于下面一行的代码,都是给父类的属性传参
        Person.__init__(self,name,age,sex)
        #还可以这样写
        #super().__init__(name,age,sex)
        self.score=score
    #这个可以是子类独有的方法,不会影响到父类
    def study(self):
        self.eat()
        print("%s在学习,考了%d分"%(self.name,self.score))
    #实例化一个学生对象,然后可以调用子类的方法,也可以直接调用父类的方法
    stu=Student("汤姆",20,"男",80)
    stu.study()
以上代码运行结果为:
汤姆正在吃饭
汤姆在学习,考了80分

子类对父类方法重写
对父类方法,object方法都可以重写

#父类
class Person():
    def __init__(self,name=None,age=None,sex=None):
        self.name=name
        self.age=age
        self.sex=sex
    def eat(self):
        print("%s正在吃饭"%self.name)

#学生子类:继承人类父类的属性
class Student(Person):
    def __init__(self,name=None,age=None,sex=None,score=None):
        # self.name=name
        # self.age=age
        # self.sex=sex
        #Person.__init__(self,name,age,sex)
        super().__init__(name,age,sex)
        self.score=score
    def study(self):
        self.eat()
        print("%s在学习,考了%d分"%(self.name,self.score))
    #方法的重写,调用的时候调用子类的方法
    #可以对自定义的方法进行重写
    def eat(self):
        print("%d的%s正在吃饭,他是%s的"%(self.age,self.name,self.sex))
    #也可以对自带的object类的方法进行重写。
    def __str__(self):
        return "姓名:{0},年龄:{1},性别:{2},成绩:{3}".format(self.name,self.age,self.sex,self.score)
    def __lt__(self,other):
        """ if isinstance(other,Student):
            return self.age<other.age
        else:
            return False """
        if self.name==other.name:
            return self.age<other.age
        else:
            return self.name<other.name
#实例化
stu=Student("汤姆",20,"男",80)
stu.study()
stu.eat()
list1=[]
stu1=Student("杰克",20,"男",90)
stu2=Student("杰森",21,"男",20)
stu3=Student("杰森",12,"女",50)
list1.append(stu)
list1.append(stu1)
list1.append(stu2)
list1.append(stu3)
for student in list1:
    print(student)
list1.sort()
for student in list1:
    print(student)

以上代码输出为:
20的汤姆正在吃饭,他是男的
汤姆在学习,考了80分
20的汤姆正在吃饭,他是男的
姓名:汤姆,年龄:20,性别:男,成绩:80
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰克,年龄:20,性别:男,成绩:90
姓名:杰森,年龄:12,性别:女,成绩:50
姓名:杰森,年龄:21,性别:男,成绩:20
姓名:汤姆,年龄:20,性别:男,成绩:80
如上,我们对自定义的eat()方法进行了重写,也对

多继承
一个子类继承多个父类
当继承多个父类时,默认调用前面一个父类

  • 多态
    子类,父类存在同样方法,子类会覆盖父类的方法
    运行时,会调用子类方法

简单工厂模式:
用户输入选择汉堡,但是不知道内部如何实现,只要得到一个汉堡的实例对象

不同类型汉堡都有相同的make()方法,但是选择不同的子类,制作不同
工厂类 --> 汉堡类 --> 汉堡子类 --> make()制作
不需要为不同汉堡设计不同make方法

#创建汉堡的父类,并根据父类创建几个子类
class Hamburger:
    def make(self):
        print("您没有正确选择要制作的汉堡,请重新输入")
class FishHamburger(Hamburger):
    def make(self):
        print("您的鱼肉汉堡已经制作好了")
class BeafHamburger(Hamburger):
    def make(self):
        print("您的牛肉汉堡已经制作好了")
class ChickenHamburger(Hamburger):
    def make(self):
        print("您的鸡肉汉堡已经制作好了")
        
#工厂类,用来判断用户输入的值并创建相应的对象
class HamburgerFactory:
    @classmethod
    def getinput(cls,temp):
        if temp=="1":
            ch=FishHamburger()
        elif temp=="2":
            ch=BeafHamburger()
        elif temp=="3":
            ch=ChickenHamburger()
        else:
            ch=Hamburger()
        return ch
#主方法,通过用户输入的值调用工厂的类方法
while True:
    temp=input("请输入您要制作汉堡的序号,1.鱼肉汉堡,2.牛肉汉堡,3.鸡肉汉堡")
    if temp=="1" or temp=="2" or temp=="3":
        ch=HamburgerFactory.getinput(temp)
        ch.make()
        break
    else:
        ch=Hamburger()
        ch.make()
        continue

6、a == b a is b

a == b 比较运算法,判断两对象 值是否相同

a is b 判断两对象物理地址是否相同

a is b 返回True

  • 小整数对象 [-5,256],相同数值的对象属于同意对象
  • 相同简单字符串,也属于同意对象
  • 直接复制

其他情况,a is b 返回False

7、多态的理解
问题 5

8、if(a1 && a2 && a==3) 什么情况下返回true
当 a 变量是一个 使用后自增的变量

9、内存模型、异常处理

不可变数据类型:一个固定区域存储。若数值发生变化,将内存会变化
可变数据类型:存储的数据的地址,真实存储区域大小是可以变化的

引用计数、标记清除、分代回收

10、面向对象特性
问题 5 面向对象特性

11、全局变量,局部变量 变量名是否可以一样
局部变量名和全局变量名 可以一样

  • 函数内可以使用 全局变量,但是无法直接修改(可变类型除外)
  • 函数内可以定义和全局变量相同的局部变量,他们作用域不同
  • 函数内想要修改全局变量,使用global关键字

12、GIL
问题 3 python垃圾回收机制

13、python多线程适合CPU密集型程序吗

问题 2 python 多线程
不适合,GIL限制一次只能有一个线程运行,不是真的多线程

14、python装饰器 参考
函数装饰器:封装一个函数
python函数可以像普通变量一样传递
装饰器 本质是python函数或类,使其他函数/类在不用修改情况下,增加额外功能

应用场景:插入日志、性能测试、事务处理、缓存、权限校验等场景

15、lambda函数 参考
python两种函数:def、lambda(匿名函数)

lambda argument_list : expression

argument_list : 参数列表

expression : 参数表达式

如:
map(lambda x : x**x, [y for y in range(10)])

lambda函数是程序更紧凑,但是只能有一条表达式组成

16、dict的key可以是list吗,为什么
不可以
dict的底层实现原理

可哈希的数据类型才可以当作键
可哈希:tuple、str、objects对象集
不可哈希:list、set、dict

set中的元素是可哈希的

17、哈希冲突怎么办,冲突点放单链表还是双链表

冲突点放单链表,双链表都是可以的。
但是若是删除操作频繁,可以选择双链表。删除效率为 1
删除时,是知道元素的具体地址的 ,双链表知道一个元素的地址后,可以快速的将该值删除
而单链表,只能从头开始遍历,找到删除节点的前驱节点,再删除 删除效率为 n

哈希表原理:

  • 哈希函数(散列函数):将键值通过哈希函数映射到一个0~M-1的索引中 (M为哈希表的长度)
    余数法(整数)
    乘数法(浮点数)

  • 哈希冲突处理:多个键值,对应相同的哈希函数值
    开放地址法

    • 线性探测法:
      fi = f(key + i ) % M i = 0 ~ M-1
      插入:若哈希函数值插入位置有元素存在,则插入该位置后续中第一个空余位置
      冲突解决:计算哈希函数,查看该位置键值是否等于查找键值,不相同查询后一个元素
    • 二次探测法
      fi = f(key + di ) % M di = 1^2 -1^2 2^2 -2^2 …
    • 伪随机探测法
      fi = f(key + m) % M m 为随机数

    链表法
    哈希表中的每个索引都对应着一个链表,链表中所有元素都是哈希值为索引的元素

    插入:哈希数组中的每个元素都指向一个链表,计算元素的哈希函数值,插入到对应的哈希数组位置的链表后

    冲突解决:计算哈希函数值,查询该哈希函数值对应的链表,查询是否链表中是否存在该键值,若有查询成功,没有查询失败

    再散列法
    有冲突,再次使用哈希函数进行哈希操作,直至没有冲突为止

python dict 数组 + 线性探测法 装载因子 2/3 关键字数/ 位桶数
Java hashmap 数组 + 链表(+ 红黑树 jdk1.8)

18、python 求数组中是否有两个数的相加,等于期望值,如果有,输出这两个数的索引 (s = s[::-1])

19、python数组和列表区别
python 中只有 list、tuple、set、dict
list是长度可变的数组或是链表&#

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值