目录
一、TCP三次握手
1.过程
1)首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。
2)A 向 B 发送连接请求报文段,SYN=1,ACK=0,选择一个初始的序号 x。
3)B 收到连接请求报文段,如果同意建立连接,则向 A 发送连接确认报文段,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。
4)A 收到 B 的连接确认报文段后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。
5)B 收到 A 的确认后,连接建立。
2.原因
第三次握手是为了防止服务器错误打开失效的链接请求。
客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。
二、TCP四次挥手
1.过程
1)A 发送连接释放报文段,FIN=1。
2)B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。
3)当 B 不再需要连接时,发送连接释放请求报文段,FIN=1。
4)A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。
5)B 收到 A 的确认后释放连接。
2.原因
服务器收到客户端发送的FIN 连接释放报文之后,就进入了 CLOSE-WAIT 状态,发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。
3.TIME_WAIT
客户端接收到服务器端的 FIN 报文后进入此状态,还需要等待2MSL。有两个理由:
- 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送FIN连接释放报文段,Time-wait就是为了处理这种情况。
- 为了让本连接持续时间内所产生的所有报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段
三、UDP vs. TCP
- 用户数据报协议 UDP:是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。
- 传输控制协议 TCP:是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。
四、进程 vs. 线程
- 进程:资源分配的基本单位;
- 线程:独立调度的基本单位。一个进程中可以有多个线程,共享进程资源
- 区别:1)资源:进程是资源分配的基本单位,线程不拥有资源,访问隶属进程的资源
2)调度:线程是独立调度的基本单位。同一进程中的线程切换不会引起进程切换,不同进程中的线程切换会引起进程切换
3)系统开销:创建/撤销/切换进程的开销远大于线程
4)通信:进程间通信需要进程同步或互斥的辅助,线程间可直接读/写进程中的数据来通信
五、进程状态
创建,就绪,运行,阻塞,终止
就绪:进程已获得除CPU以外的所有资源,等待分配CPU
运行:占用CPU资源运行,处于此状态的进程数小于等于CPU数
阻塞:进程等待某种条件,在条件满足之前无法执行
六、进程间通信
主要包括管道, 系统IPC (包括信号,消息队列,共享内存,信号量), 套接字(SOCKET)
- 管道:可用于具有亲缘关系进程间的通信
- 系统IPC:
- 信号:用于通知接受进程有某种事件发生
- 消息队列:消息链表,存放在内核中并由消息队列标识符标识。
- 共享内存:多个进程可访问同一内存空间,最快的IPC形式,通常与其他机制结合使用
- 信号量:计数器,控制多个进程对共享资源的访问,锁机制
- 套接字:可用于不同机器间的进程间通信
七、死锁,产生条件
- 死锁:多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放其他资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲就是多个进程无限期的阻塞、相互等待的一种状态。
- 条件(有一个条件不成立,则不会产生死锁):
- 互斥:一个资源一次只能被一个进程使用
- 请求与保持:一个进程因请求资源而阻塞时,对已获得资源保持不放
- 非抢占条件:进程获得的资源,在未完全使用完之前,不能强行抢占
- 循环等待:若干进程之间形成一种头尾相接的环形等待资源关系
八、python里面字典底层怎么实现的
- 用哈希表实现。两个list,一个放key,一个放value,用哈希函数将key和数组下标进行映射,使其均匀地分布在数组中。
- 哈希函数:只要能将所有数据均映射到list中即可
- 折叠法:将每个元素分为相等的几部分后相加后再取模
- 取中法:平方后取中间的值再取模
- 冲突处理:
- 开放寻址法
1)线性探测再散列:H_i=(hash(key)+i)%m ,i=0,1,2,…,m-1
2)二次探测再散列/平方探查法:H_i=(hash(key)+i^2 )%m ,i=0,1,2,…,m-1 - 再哈希:换另一个哈希函数
- 链地址法:将所有关键字哈希值相同的记录都存在同一线性链表中
- 开放寻址法
- 装载因子 = ,越大,冲突的可能性越大,一般0.75比较合适
九、动态规划 vs. 贪心算法
- 都是用来求“最优化问题”,且都必须有“最优子结构”(一个问题的最优解包含其子问题的最优解)。贪心可以解决的问题,动态规划都能解决,可以说,贪心是动态规划的一个特例
- 动态规划:自底向上,通过组合子问题的解来求解原问题
- 关键是状态转移方程,即如何由已求出的局部最优解来推导全局最优解,全局最优解一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解
- 边界条件:最简单的,可以直接得出的局部最优解
- 贪心:自顶向下,进行一次次选择,将给定问题变得更小
从问题的某一个初始解出发逐步逼近给定的目标,总是做出当时看来最佳的选择,由上一步的最优解推导下一步的最优解,因此只需要记录上一步的最优解
十、堆栈区别
- 栈存储的是局部变量而堆存储的是实体
- 栈的更新速度要快于堆内存,因为局部变量的生命周期很短,变量只在作用域内有效,一旦离开就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收
十一、排序复杂度
排序算法 | 比较次数 | 移动次数 | 辅助空间 | 稳定性 | |||
最好时间 | 平均时间 | 最坏时间 | 最好情形 | 最坏情形 | |||
直接插入排序 | O(n) | O(n2) | O(n2) | 0 | O(n2) | O(1) | 稳定 |
希尔排序 | O(n1.3) | 0 | O(n2) | O(1) | 不稳定 | ||
直接选择排序 | O(n2) | O(n2) | O(n2) | 0 | O(n2) | O(1) | 不稳定 |
堆排 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(log2n) | O(n2) | O(1) | 不稳定 |
冒泡排序 | O(n) | O(n2) | O(n2) | 0 | O(n2) | O(1) | 稳定 |
快排 | O(nlog2n) | O(nlog2n) | O(n2) | O(nlog2n) | O(log2n) | 不稳定 | |
归并排序 | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(nlog2n) | O(n) | 稳定 | |
基数排序(链式、顺序) | O(dn) | O(dn) | O(dn) | O(n) | 稳定 |