SXF python

闭包

嵌套函数,第二层函数引用了第一层函数的变量,且返回的是第二层函数名。

装饰器

在不改变原函数情况下进行功能扩展。
这个不改变包括函数内部的逻辑,和函数的调用代码。
只需要在原来函数上方加一个魔术方法。

import time
def time_it(func):
def inner():
start = time.time()
func()
end = time.time()
print(‘用时:{}秒’.format(end-start))
return inner
@time_it
def func1():
time.sleep(2)
print(“Func1 is running.”)
if name == ‘main’:
func1()

dict实现原理

字典也被称为关联数组,还称为哈希数组等。也就是说,字典也是一个数组,但数组的索引是键经过哈希函数处理后得到的散列值。哈希函数的目的是使键均匀地分布在数组中,并且可以在内存中以O(1)的时间复杂度进行寻址,从而实现快速查找和修改。
通过键名查找对应的值会有很高的效率,时间复杂度在常数级别O(1)。
在Python2中,dict的底层是依靠哈希表(Hash Table)进行实现的,使用开放地址法解决冲突。所以其查找的时间复杂度会是O(1),下文会具体讲解哈希表的工作原理和解决冲突时的具体方法。哈希表是key-value类型的数据结构,通过关键码值直接进行访问。
哈希函数就是一个映射。

散列函数算法原理?

散列方法的主要思想是根据结点的关键码值来确定其存储地址:以关键码值K为自变量,通过一定的函数关系h(K)(称为散列函数),计算出对应的函数值来,把这个值解释为结点的存储地址,将结点存入到此存储单元中。

构造方法

直接寻址法
取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a•key + b,其中a和b为常数(这种散列函数叫做自身函数)

数字分析法
分析一组数据,比如一组员工的出生年月日,这时我们发现出生年月日的前几位数字大体相同,这样的话,出现冲突的几率就会很大,但是我们发现年月日的后几位表示月份和具体日期的数字差别很大,如果用后面的数字来构成散列地址,则冲突的几率会明显降低。因此数字分析法就是找出数字的规律,尽可能利用这些数据来构造冲突几率较低的散列地址

平方取中法
取关键字平方后的中间几位作为散列地址

折叠法
将关键字分割成位数相同的几部分,最后一部分位数可以不同,然后取这几部分的叠加和(去除进位)作为散列地址

随机数法
选择一随机函数,取关键字的随机值作为散列地址,通常用于关键字长度不同的场合

除留余数法
取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p, p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词

冲突的解决:
开放地址
开放地址的意思是除了哈希函数得出的地址可用,当出现冲突的时候其他的地址也一样可用,常见的开放地址思想的方法有线性探测再散列,二次探测再散列,这些方法都是在第一选择被占用的情况下的解决方法。
再哈希法
这个方法是按顺序规定多个哈希函数,每次查询的时候按顺序调用哈希函数,调用到第一个为空的时候返回不存在,调用到此键的时候返回其值。
链地址法
将所有关键字哈希值相同的记录都存在同一线性链表中,这样不需要占用其他的哈希地址,相同的哈希值在一条链表上,按顺序遍历就可以找到。

python基础数据类型 : 可变:(list, set, dict),不可变(int,float)

Python深拷贝和浅拷贝:

赋值是一个指向引用的操作,它指向的是同一个对象

深拷贝就是将一个对象拷贝到另一个对象中,这意味着如果你对一个对象的拷贝做出改变时,不会影响原对象。深拷贝在内存中占用两块内存空间。
b=copy.deepcopy(a)

浅拷贝则是将一个对象的引用(相当于c语言的指针)拷贝到另一个对象上,所以如果我们在拷贝中改动,也会影响到原对象。浅拷贝在内存中共用一块内存空间。
b=copy.copy(a)

python中的垃圾回收机制

Python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略。

1、引用计数
python中一切皆对象,所以python底层计数结构地就可以抽象为:
引用计数结构体{
引用计数;
引用的对象
}
什么情况下引用计数+1,什么情况下-1,当引用次数为0时,肯定就是需要进行回收的时刻。

引用计数+1的情况:
1、对象被创建时,例如 mark=“帅哥”
2、对象被copy引用时,例如 mark2=mark,此时mark引用计数+1
3、对象被作为参数,传入到一个函数中时
4、对象作为一个子元素,存储到容器中时,例如 list=[mark,mark2]

引用计数-1的情况:
1、对象别名被显式销毁,例如 del mark
2、对象引用被赋予新的对象,例如mark2=mark3,此时mark引用计数-1(对照引用计数+1的情况下的第二点来看)
3、一个函数离开他的作用域,例如函数执行完成,它的引用参数的引用计数-1
4、对象所在容器被销毁,或者从容器中删除。

查看引用计数
import sys
a = “mark 帅哥”
print(sys.getrefcount(a))

引用计数机制优点
1、简单、直观
2、实时性,只要没有了引用就释放资源。

引用计数机制缺点
1、维护引用计数需要消耗一定的资源
2、循环应用时,无法回收。也正是因为这个原因,才需要通过标记-清理和分代收集机制来辅助引用计数机制。

2、标记-清理
python标记删除时通过l两个容器来完成的:死亡容器、存活容器。

c=[5,6]#假设此时c的引用为1
d=[7,8]#假设此时d的引用为1
#循环引用
c.append(d)#c的引用+1=2
d.append©#d的引用+1=2

首先,我们先来分析情况2,删除c、d
删除后,c的引用为1,d的引用为1,根据引用计数,还无法删除
标记删除第一步:对执行删除操作后的每个引用-1,此时c的引用为0,d的引用为0,把他们都放到死亡容器内。把那些引用仍然大于0的放到存活容器内。
标记删除第二步:遍历存活容器,查看是否有的存活容器引用了死亡容器内的对象,如果有就把该对象(注意是对象,比如0x7f94bb602f80,不是对象的引用)从死亡容器内取出,放到存活容器内。
由于c、d都没有对象引用他们了,所以经过这一步骤,他们还是在死亡组。
标记删除第三部:将死亡组所有对象删除。
这样就完成了对从c、d的删除。

3、分代回收
分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。

Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

有三种情况会触发垃圾回收:
1.调用gc.collect(),
2.当gc模块的计数器达到阀值的时候。
3.程序退出的时候

gc模块的自动垃圾回收机制

必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。
这个机制的主要作用就是发现并处理不可达的垃圾对象。
垃圾回收=垃圾检查+垃圾回收
在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾检查中,该对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中。

gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。
例如(488,3,0),其中488是指距离上一次一代垃圾检查,Python分配内存的数目减去释放内存的数目,注意是内存分配,而不是引用计数的增加。

python 内存泄漏与内存溢出

内存泄漏:你使用malloc或new向 内存申请了一块内存空间,但没有用free以及delete对该块内存进行释放,造成程序失去了对该块内存的控制.
内存溢出:你申请了10个字节的内存,但写入了大于10个字节的数据.

内存泄漏解决办法:

资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉。
需要在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

内存溢出的解决方案:

第一步,修改JVM启动参数,直接增加内存。

内存溢出的检查
from guppy import hpy
h = hpy()
print (h.heap())

memory_profiler是利用python的装饰器工作的,所以我们需要在进行测试的函数上添加装饰器。
通过上述两种工具guppy与memory_profiler可以很好地来监控python代码运行时的内存占用问题。

什么是python元类:
class New_Hello2(metaclass=HelloMeta2)

GIL:又叫全局解释器锁,每个线程在执行的过程中都需要先获取GIL,保证同一时刻只有一个线程在运行,目的是解决多线程同时竞争程序中的全局变量而出现的线程安全问题。

python 进程

from multiprocessing import Process
def func(name):
print(‘hello’, name)
if name == “main”:
p = Process(target=func,args=(‘zhangyanlin’,))
p.start()
p.join() # 等待进程执行完毕
start():启动进程,并调用该子进程中的p.run()
join([timeout]):让主线程等待某一子进程结束,才继续执行主进程。timeout是可选的超时时间。超过一个时间主进程就不等待了。

python 线程

import threading
import time

def worker(num):
time.sleep(1)
print(“The num is %d” % num)
return

for i in range(20):
t = threading.Thread(target=worker,args=(i,),name=“t.%d” % i)
t.start()

进程池

#apply
from multiprocessing import Pool
import time

def f1(i):
time.sleep(0.5)
print(i)
return i + 100
if name == “main”:
pool = Pool(5)
for i in range(1,31):
pool.apply(func=f1,args=(i,))

#apply_async
def f1(i):
time.sleep(0.5)
print(i)
return i + 100
def f2(arg):
print(arg)
if name == “main”:
pool = Pool(5)
for i in range(1,31):
pool.apply_async(func=f1,args=(i,),callback=f2)
pool.close()
pool.join()

python迭代器

class MyNumbers:
def iter(self):
self.a = 1
return self

def next(self):
x = self.a
self.a += 1
return x

myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))

python生成器

在 Python 中,使用了 yield 的函数被称为生成器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
import sys

def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成

while True:
try:
print (next(f), end=" ")
except StopIteration:
sys.exit()

Python 中的__new__和__init__的区别


二者均是Python面向对象语言中的函数,__new__比较少用,__init__则用的比较多。


__new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是个静态方法。
__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常用在初始化一个类实例的时候。是一个实例方法。
也就是: __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。

Python 中的__new__和__init__的区别


二者均是Python面向对象语言中的函数,__new__比较少用,__init__则用的比较多。


__new__是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是个静态方法。
__init__是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常用在初始化一个类实例的时候。是一个实例方法。
也就是: __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。

什么时候用new

单例模式
new方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。

进程间通信

每个进程都各自拥有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

管道( pipe )
    管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。【是由内核管理的一个缓冲区,速度慢,容量有限】

命名管道 (named pipe)
    命名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

消息队列( message queue )
    消息队列是由消息组成的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    是用于两个进程之间的通讯,首先在一个进程中创建一个消息队列,然后再往消息队列中写数据,而另一个进程则从那个消息队列中取数据。需要注意的是,消息队列是用创建文件的方式建立的,如果一个进程向某个消息队列中写入了数据之后,另一个进程并没有取出数据,即使向消息队列中写数据的进程已经结束,保存在消息队列中的数据并没有消失,也就是说下次再从这个消息队列读数据的时候,就是上次的数据。

信号量( semophore )
    信号量是一个计数器,可以用来控制多个进程对共享资源的访问。
    它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。

信号 ( sinal )
    用于通知接收进程某个事件已经发生。

共享内存( shared memory )
    共享内存由一个进程创建,但多个进程都可以访问。【两个不同进程 A、B 共享内存的意思是:同一块物理内存被映射到进程 A、B 各自的进程地址空间。进程 A 可以即时看到进程 B 对共享内存中数据的更新,反之亦然】
    共享内存是最快的 IPC(进程间通信) 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。

套接字( socket )
    套接字也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

线程间的通信方式

锁机制:包括互斥锁、条件变量、读写锁
*互斥锁提供了以排他方式防止数据结构被并发修改的方法。
*读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
*条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
#信号机制(Signal):类似进程间的信号处理
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

进程 线程 协程的适用场景:

多进程适合在CPU 密集型操作(cpu 操作指令比较多,如科学计算,位数多的浮点运算)
多线程适合在IO 密集型操作(读写数据操作较多的,比如爬虫)

线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个进程中的所有线程共享资源。
进程:一个运行的程序或代码就是一个进程,一个没有运行的代码叫程序。进程是系统进行资源分配的最小单位,进程拥有自己的内存空间,所以进程间数据不共享,开销大。

线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程的存在而存在,一个进程至少有一个线程,叫主线程,多个线程共享内存(数据共享和全局变量),因此提升程序的运行效率。

协程:用户态的轻量级线程,调度有用户控制,拥有自己的寄存器上下文和栈,切换基本没有内核切换的开销,切换灵活。

进程切换比线程切换开销大是因为进程切换时要切页表,而且往往伴随着页调度,因为进程的数据段代码段要换出去,以便把将要执行的进程的内容换进来。本来进程的内容就是线程的超集。而且线程只需要保存线程的上下文(相关寄存器状态和栈的信息)就好了,动作很小

staticmethod()静态方法和classmethod类方法

一般来说,要使用某个类的方法,需要先实例化一个对象再调用方法。
而使用@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
classmethod类方法,第一个参数是cls,代表这个类(注意不是类实例),常用来调用类级方法(如系统方法跟静态方法,类方法等)来进行预处理,然后返回预处理后的类。
classmethod的返回值会传入__init__进行数值初始化,简单来说,就是在__init__先做预处理以处理其他情况。
实际使用时,能由__init__处理的就交给它,不行就先用classmethod先进行预处理,可以说classmethod是用来扩充__init__无法初始化而产生的。

类方法,第一个参数是cls,代表这个类(注意不是类实例),常用来调用类级方法(如系统方法跟静态方法,类方法等)来进行预处理,然后返回预处理后的类。
classmethod的返回值会传入__init__进行数值初始化,简单来说,就是在__init__先做预处理以处理其他情况。
实际使用时,能由__init__处理的就交给它,不行就先用classmethod先进行预处理,可以说classmethod是用来扩充__init__无法初始化而产生的。

静态方法staticmethod:让类里的方法直接被类调用,就像正常调用函数一样,要在静态方法里面调用self,就必须把self本身传进去。

那他们有什么区别呢,从它们的使用上来看:
实例方法,简而言之,实例方法就是类的实例能够使用的方法。
@staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。
@classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数。
如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.方法名。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等,避免硬编码。

mysql索引用了何种数据结构?用b树和b+树有什么特点?

一个m阶的B树具有如下几个特征:
1.根结点至少有两个子女。
2.每个中间节点都至少包含ceil(m / 2)个孩子,最多有m个孩子。
3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m。
4.所有的叶子结点都位于同一层。
5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。

平衡树的优点

采用平衡树的优点是:使树的结构较好,从而提高查找运算的速度。缺点是:是插入和删除运算变得复杂化,从而降低了他们的运算速度。

二叉树的实际应用

1.哈夫曼编码,来源于哈夫曼树(给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为赫夫曼树(Huffman tree)。即带权路径长度最短的树),在数据压缩上有重要应用,提高了传输的有效性,详见《信息论与编码》。
2.海量数据并发查询,二叉树复杂度是O(K+LgN)。二叉排序树就既有链表的好处,也有数组的好处, 在处理大批量的动态的数据是比较有用。
3.C++ STL中的set/multiset、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。查找最大(最小)的k个数,红黑树,红黑树中查找/删除/插入,都只需要O(logk)。
4.B-Tree,B±Tree在文件系统中的目录应用。
5.路由器中的路由搜索引擎。
红黑树:
普通的二叉查找树在极端情况下可退化成链表,此时的增删查O(n)效率都会比较低下。为了避免这种情况,就出现了一些自平衡的查找树,比如 AVL。
 ALV树是一种严格按照定义来实现的平衡二叉查找树,所以它查找的效率非常稳定,为O(log n),由于其严格按照左右子树高度差不大于1的规则,插入和删除操作中需要大量且复杂的操作来保持ALV树的平衡(左旋和右旋),因此ALV树适用于大量查询,少量插入和删除的场景中。
现在假设有这样一种场景:大量查询,插入和删除,使用ALV树就不太合适了,因为ALV树大量的插入和删除会非常耗时间

红黑树的五个特性

(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。

有了上面的几个性质作为限制,即可避免二叉查找树退化成单链表的情况。但是,仅仅避免这种情况还不够,这里还要考虑某个节点到其每个叶子节点路径长度的问题。如果某些路径长度过长,那么,在对这些路径上的及诶单进行增删查操作时,效率也会大大降低。这个时候性质4和性质5用途就凸显了,有了这两个性质作为约束,即可保证任意节点到其每个叶子节点路径最长不会超过最短路径的2倍。

二叉平衡树和红黑树的区别:

和红黑树相比,AVL树是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1)。通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍,因此,红黑树是一种弱平衡二叉树(由于是弱平衡,可以看到,在相同的节点情况下,AVL树的高度低于红黑树)。
AVL树适合用于插入与删除次数比较少,但查找多的情况;相对于要求严格的AVL树来说,红黑树的旋转次数少,所以对于搜索、插入、删除操作较多的情况下,我们就用红黑树。

只用二次握手有什么影响

假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。
由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据,但server却以为新的运输连接已经建立,并一直等待client发来数据。
这样,server的很多资源就白白浪费掉了。
采用“三次握手”的办法可以防止上述现象发生。

数据库主键和外键

关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键
外键是用于建立和加强两个表数据之间的链接的一列或多列。通过将保存表中主键值的一列或多列添加到另一个表中,可创建两个表之间的链接。这个列就成为第二个表的外键。
数据库索引就是数据库的数据结构!进一步说则是该数据结构中存储了一张表中某一列的所有值,也就是说索引是基于数据表中的某一列创建的。总而言之:一个索引是由表中某一列上的数据组成,并且这些数据存储在某个数据结构中。

聚集索引和非聚集索引的区别

聚集索引:索引中键值的逻辑顺序决定了表中相应行的物理顺序。
非聚集索引:索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同。
聚集索引一定是唯一索引。但唯一索引不一定是聚集索引。
聚集索引,在索引页里直接存放数据,而非聚集索引在索引页里存放的是索引,这些索引指向专门的数据页的数据。

什么是索引?

索引在MySQL中也叫做“键”或者"key"(primary key,unique key,还有一个index key),是存储引擎用于快速找到记录的一种数据结构。

主键(PRIMARY KEY)”的完整称呼是“主键约束”。MySQL主键约束是一个列或者列的组合,其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键,通过它可以强制表的实体完整性。

事务

事务只和DML(insert、update、delete)语句有关,或者说DML语句才有事务
事务四大特征(ACID)
原子性(A):事务是最小单位,不可再分
一致性©:事务要求所有的DML语句操作的时候,必须保证同时成功或者同时失败
隔离性(I):事务A和事务B之间具有隔离性
持久性(D):是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中)

start transaction
DML语句
commit

回滚操作(事务失败)
start transaction
DML语句
rollback

列表的数据结构

列表实现可以是数组和链表,在CPython中,列表被实现为长度可变的数组。

平衡二叉树

且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是频繁旋转会使插入和删除牺牲掉O(logN)左右的时间,不过相对二叉查找树来说,时间上稳定了很多。

二叉树的实际应用

1哈夫曼编码,哈夫曼树,在数据压缩上有重要应用,提高了传输的有效性。
2海量数据并发查询,二叉树复杂度是O(K+LgN)。二叉排序树就既有链表的好处,也有数组的好处,在处理大批量的动态的数据是比较有用。
3.C++ STL中的set/multiset、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。查找最大(最小)的k个数,红黑树,红黑树中查找/删除/插入,都只需要O(logk)。
4 B-Tree,B±Tree在文件系统中的目录应用。
5路由器中的路由搜索引擎。

如何在 1000 万个整数中快速查找某个整数?

我们的内存限制是100MB,每个数据大小是 8 字节,最简单的办法就是将数据存储在数组中,内存占用差不多是 80MB,符合内存的限制。我们可以先对这 1000 万数据从小到大排序,然后再利用二分查找算法,就可以快速地查找想要的数据了。

单例模式

我们可能希望某一个对象只会在程序中出现一次,之后所有的调用使用同一个对象。
直接实现 new 方法 ,同时定义一个类变量保存这个值,如果没有第一次 先生成好,赋值给类变量,之后发布会这个 类变量即可.
class SingleTon(object):
def new(cls, *args, **kwargs):
if not hasattr(cls,’_instance’):
cls._instance = object.new(cls,*args,**kwargs)
return cls._instance

class TestClass(SingleTon):
a = 1

test1 = TestClass()
test2 = TestClass()
print(test1.a)
print(test2.a)
print(id(test1))
print(id(test2))

出租车一分钟来的概率是p,三分钟来的概率是多少?30分钟呢?

1-(1-p)^3

稳定排序

插入排序,冒泡排序,二叉树排序,归并排序

不稳定排序
快速排序(快),希尔排序(些),选择排序(选),堆排序(一堆)。

Python的内存管理机制:引入计数、垃圾回收、内存池机制

mysql索引用了何种数据结构?用b树和b+树有什么特点?

mysql用b+树的原因:

  1. 支持范围查询(b+树叶子节点保存树中所有叶子节点的内容(值),叶子节点通过双向链表链接,可以通过叶子节点进行范围查询,而b树为传统搜索树,只能通过树的遍历方式对特定内容进行查找,不支持范围查找)
  2. 查询效率稳定(b树在查找过程中只要找到匹配元素即可,无论匹配元素处于中间节点还是叶子节点,而b+树在查找过程中是一定要查找到叶子节点上的,因此查找效率是稳定的)
  3. 减少IO次数(由于b+树在非叶子节点中并不直接存放实际内容,所以非叶子节点中能够存放更多的内容,因此查询时IO次数也更少)

数组和链表的区别

数组的特点
1.在内存中,数组是一块连续的区域。
2.数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间。
3.插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。删除数据时,这个数据后面的数据都要往前移动。
4.随机读取效率很高。因为数组是连续的,知道每一个数据的内存地址,可以直接找到给地址的数据。
5.并且不利于扩展,数组定义的空间不够时要重新定义数组。

链表的特点

1.在内存中可以存在任何地方,不要求连续。
2.每一个数据都保存了下一个数据的内存地址,通过这个地址找到下一个数据。
3.查找数据时效率低,因为不具有随机访问性,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推。
4.不指定大小,扩展方便。链表大小不用定义,数据随意增删。

数组的优点

随机访问性强
查找速度快

数组的缺点

插入和删除效率低
可能浪费内存
内存空间要求高,必须有足够的连续内存空间。
数组大小固定,不能动态拓展

链表的优点

插入删除速度快
内存利用率高,不会浪费内存
大小没有固定,拓展很灵活。

链表的缺点

不能随机查找,必须从第一个开始遍历,查找效率低

列表的数据结构:

列表实现可以是数组和链表。

HTTP协议和HTTPS协议区别如下:

1)HTTP协议是以明文的方式在网络中传输数据,而HTTPS协议传输的数据则是经过TLS加密后的,HTTPS具有更高的安全性
2)HTTPS在TCP三次握手阶段之后,还需要进行SSL 的handshake,协商加密使用的对称加密密钥
3)HTTPS协议需要服务端申请证书,浏览器端安装对应的根证书
4)HTTP协议端口是80,HTTPS协议端口是443

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Python中获取网络地址,你可以使用以下方法之一: 方法一:使用socket库的gethostname()函数和gethostbyname()函数来获取本地主机的IP地址。代码如下: ```python import socket # 获取本地主机名 hostname = socket.gethostname() # 获取本地主机的IP地址 ip_address = socket.gethostbyname(hostname) print(ip_address) ``` 这种方法适用于获取局域网IP地址。\[1\] 方法二:使用requests库和正则表达式来获取外部IP地址。代码如下: ```python import requests import re def getOuterIP(): ip = '' try: res = requests.get('https://myip.ipip.net', timeout=5).text ip = re.findall(r'(\d+\.\d+\.\d+\.\d+)', res) ip = ip\[0\] if ip else '' except: pass return ip print(getOuterIP()) ``` 这种方法通过访问一个获取外部IP地址的网站,并使用正则表达式提取IP地址。\[2\] 方法三:使用socket库的socket函数和getsockname()函数来获取本地主机的IP地址。代码如下: ```python import socket def extract_ip(): st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: st.connect(('10.255.255.255', 1)) IP = st.getsockname()\[0\] except Exception: IP = '127.0.0.1' finally: st.close() return IP print(extract_ip()) ``` 这种方法适用于获取所有接口的IP地址,包括公共、私有和外部IP地址。\[3\] 希望这些方法能够帮助到你! #### 引用[.reference_title] - *1* *2* *3* [Python获取本机IP地址的几种方式](https://blog.csdn.net/sxf1061700625/article/details/123526907)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值