目录
- 线性代数
- 概率论
- 高等数学
- 信号与系统
- 离散数学
- 操作系统
- 计算机网络
- 计算机组成
- 数据结构
- 算法
- 编译原理
- C++
- 杂项
线性代数
-
怎么求逆矩阵
逆矩阵: A A − 1 = E AA^{-1}=E AA−1=E,伴随矩阵: A A ∗ = A ∗ A = ∣ A ∣ E AA^{*}=A^{*}A=|A|E AA∗=A∗A=∣A∣E, A ∗ = ∣ A ∣ A − 1 A^{*}=|A|A^{-1} A∗=∣A∣A−1
可用方法:待定系数法、伴随矩阵+行列式的值、初等行变换(增广矩阵)
-
什么是矩阵的秩,几何含义,应用
定义:将矩阵进行初等行变换化为行阶梯型后非0行的个数,这是行秩。所有的矩阵,行秩=列秩=秩
含义:矩阵秩代表矩阵所张开空间的维度;矩阵秩代表矩阵列(行)向量的线性无关的个数
应用:通过秩可以剔除掉线性相关的向量,直指问题的核心;矩阵秩可以探究到其所对应的线性方程解的情况,是有唯一解还是有无穷多解还是无解
-
什么是线性相关、线性无关
定义:若干同维向量 a 1 , a 2 … a m a_1,a_2…a_m a1,a2…am,如果存在不全为0的实数 k 1 , k 2 … k m k_1,k_2…k_m k1,k2…km 使得 k 1 a 1 + k 2 a 2 + … + k m a m = 0 k_1a_1+k_2a_2+…+k_ma_m=0 k1a1+k2a2+…+kmam=0,则称向量是线性相关的;若干同维向量 a 1 , a 2 … a m a_1,a_2…a_m a1,a2…am,如果只存在全为0的实数 k 1 , k 2 … k m k_1,k_2…k_m k1,k2…km 使得 k 1 a 1 + k 2 a 2 + … + k m a m = 0 k_1a_1+k_2a_2+…+k_ma_m=0 k1a1+k2a2+…+kmam=0,则称向量是线性无关的
从性质看:两个向量线性相关就对应成比例; m m m 个向量线性相关,其组成的行列式为0或者其矩阵的秩小于 m m m
-
什么是矩阵的特征值和特征向量,几何含义,应用
定义:设 A A A 是 n n n 阶方阵,若数 λ \lambda λ 和 n n n 维非零列向量 x x x 使得 A x = λ x Ax=\lambda x Ax=λx 成立,则称 λ \lambda λ 是方阵 A A A 的一个特征值, x x x 为方阵 A A A 的对应于特征值 λ \lambda λ 的一个特征向量。
几何含义:矩阵乘法对应了一个仿射变换,是把任意一个向量变成另一个新向量,它们的方向和长度可能改变。在这个变换的过程中,原向量主要发生旋转、伸缩的变换。如果矩阵对某一个向量或某些向量只发生伸缩变换,不对这些向量产生旋转的结果,那么这些向量就称为这个矩阵的特征向量,伸缩的比例就是特征值。
-
什么是矩阵的等价、合同、相似?三者关系是什么
等价:对同型矩阵 A 、 B A、B A、B,存在可逆阵 P P P 和 Q Q Q,使得 B = P A Q B=PAQ B=PAQ。充要条件是 A 、 B A、B A、B 秩相等。
合同:对同型矩阵 A 、 B A、B A、B,存在可逆阵 P P P ,使得 B = P T A P B=P^TAP B=PTAP。
相似:对同型矩阵 A 、 B A、B A、B,存在可逆阵 P P P ,使得 B = P − 1 T A P B=P^{-1}TAP B=P−1TAP。
三者关系:等价(只有秩相等) → \rightarrow → 合同(秩和正负惯性指数相同:正负特征值的个数) → \rightarrow → 相似(秩,正负惯性指数、特征值均相同),矩阵亲密关系一步步深化
-
什么是对角化
定义:对 n n n 阶矩阵 A A A,存在可逆矩阵 P P P,使得 P − 1 A P = 对角阵 P^{-1}AP=对角阵 P−1AP=对角阵 ,就称为把方阵 A A A 对角化
充要条件: n n n 阶矩阵有 n n n 个线性无关的特征向量
性质:对角阵的主对角元素为 A A A 的特征值;可逆矩阵 P P P 由 A A A 的 n n n 个线性无关的特征向量作列向量构成
-
什么是正交矩阵、对称矩阵,对称矩阵的性质
正交矩阵:矩阵的转置等于矩阵的逆、矩阵的行列式为±1、矩阵的行(或列)向量之间点积为0、行(或列)向量与自身的点积为1
对称矩阵:以主对角线为对称轴,各元素对应相等的矩阵
性质:方阵、矩阵等于矩阵的转置、能相似对角化,能正交对角化
-
什么是二次型、什么是二次型矩阵?什么是正定矩阵、半正定矩阵?
二次型:关于一些变量的二次齐次多项式,表达式为 f ( x ) = x T A x f(x)=x^TAx f(x)=xTAx
二次型矩阵:表达式中 A A A 是一个 n n n 阶对称矩阵,即二次型矩阵。主对角元素为平方项系数,其他元素为交叉项系数的一半。
正定矩阵:设 A A A 为实对称矩阵,若对于每个非零实向量 X X X 都有 X T A X > 0 X^TAX>0 XTAX>0,则称 A A A 为正定矩阵,称 X T A X X^TAX XTAX 为正定二次型。
- 特征值全都大于零
- 各阶顺序主子式大于0
半正定矩阵:设 A A A 为实对称矩阵,若对于每个非零实向量 X X X 都有 X T A X ≥ 0 X^TAX\ge 0 XTAX≥0,则称 A A A 为正定矩阵,称 X T A X X^TAX XTAX 为正定二次型。
- 特征值均为非负
- 各阶顺序主子式均非负
概率论
-
解释下全概率公式和贝叶斯公式,并说明贝叶斯公式的意义
条件概率: P ( B ∣ A ) = P ( A B ) P ( A ) P(B|A)=\frac{P(AB)}{P(A)} P(B∣A)=P(A)P(AB)
乘法公式: P ( A B ) = P ( A ) P ( B ∣ A ) P(AB)=P(A)P(B|A) P(AB)=P(A)P(B∣A)
全概率公式: P ( A ) = P ( B 1 ) P ( A ∣ B 1 ) + P ( B 2 ) P ( A ∣ B 2 ) + … + P ( B n ) P ( A ∣ B n ) = ∑ i = 1 n P ( B i ) P ( A ∣ B i ) P(A)=P(B_1)P(A|B_1)+P(B_2)P(A|B_2)+…+P(B_n)P(A|B_n)=\sum^n_{i=1}P(B_i)P(A|B_i) P(A)=P(B1)P(A∣B1)+P(B2)P(A∣B2)+…+P(Bn)P(A∣Bn)=∑i=1nP(Bi)P(A∣Bi)
意义:全概率公式反映的是由因到果的过程
贝叶斯公式: P ( B i ∣ A ) = P ( A B i ) P ( A ) = P ( B i ) P ( A ∣ B i ) ∑ j = 1 n P ( B j ) P ( A ∣ B j ) , i = 1 , 2 … n P(B_i|A)=\frac{P(AB_i)}{P(A)}=\frac{P(B_i)P(A|B_i)}{\sum^n_{j=1}P(B_j)P(A|B_j)},i=1,2…n P(Bi∣A)=P(A)P(ABi)=∑j=1nP(Bj)P(A∣Bj)P(Bi)P(A∣Bi),i=1,2…n
意义:贝叶斯公式是乘法公式除以全概率公式。是由果导因的过程,在事件 A A A 已经发生的条件下,贝叶斯公式可用来寻找导致 A A A 发生的各种原因 B i B_i Bi 的概率。
-
大数定理说明了什么?什么是伯努利大数定理?什么是中心极限定理?
大数定理:描述了大量重复试验的结果,即结果的平均值应接近预期值,并随着试验次数的增加,结果将越趋于预期值。大数定理讨论的是在什么条件下,随机变量的算术平均依概率收敛于其期望。
伯努利大数定理:设 f A f_A fA 为 n n n 重伯努利试验中事件 A A A 发生的次数, p p p 为每次试验中 A A A 出现的概率,则对 ∀ ε > 0 \forall\varepsilon>0 ∀ε>0,有 l i m P { ∣ f A n − p ∣ < ε } = 1 limP\{|\frac{f_A}{n}-p|< \varepsilon\}=1 limP{∣nfA−p∣<ε}=1。试验次数足够多时,频率依概率收敛于概率。
辛钦大数定理:设 X n { X_n } Xn 为一独立同分布的随机变量序列,若 X i X_i Xi 的数学期望存在,则 X n {X_n} Xn 服从大数定律,即对 ∀ ε > 0 \forall \varepsilon>0 ∀ε>0,有 l i m P { ∣ 1 n ∑ k = 1 n X k − μ ∣ < ε } = 1 limP\{|\frac{1}{n}\sum^n_{k=1}X_k-\mu|< \varepsilon\}=1 limP{∣n1∑k=1nXk−μ∣<ε}=1。独立同分布随机变量的算术平依概率收敛于期望。
中心极限定理:中心极限定理讨论了在怎样的条件下,独立随机变量之和 Y n = ∑ i = 1 n X i Y_n=\sum\limits_{i=1}^n X_i Yn=i=1∑nXi 的极限分布为正态分布。
-
什么是点估计?什么是区间估计?什么是矩估计?
点估计:用一个数值对对总体参数给出估计
区间估计:在点估计基础上,给定一个具体的估计范围
矩估计:样本的 k k k 阶矩依概率收敛于总体的 k k k 阶矩
-
如何判断一个点估计好不好?
- 无偏性:用样本估计的参数值的期望,等于真实值
- 有效性:用样本估计的参数值的方差,如果越小,就越有效
- 一致性:当样本量越来越大的时候,估计值和真实值的距离越来越小
-
什么是极大似然估计?
利用已知的样本结果信息,反推出最有可能(最大概率)导致这些样本结果出现的模型参数值
例子:比如箱子中有100个球,共两种颜色白和黑。已知白球和黑球的比例是1:99(但不知道谁是1)。目标是估计箱子中什么颜色是99个。随机抽取一个球,发现是白球。那么从直观上讲,是不是大概率箱子中是99个白球?当然也有可能箱子中是99个黑球,正好有1个白球还正好被抽到了。但是明显这种情况概率较小。
-
各个分布
-
三大抽样分布
-
一对夫妇生了两个孩子,已知第一个孩子是女孩,请问第二个孩子是男孩的概率是?
条件概率:答案为2/3,如果问的是女孩就是1/3
高等数学
-
连续、可导、可微
连续:左极限等于右极限等于函数值,即 lim x → x 0 f ( x ) = f ( x 0 ) \lim\limits_{x\rightarrow x_0}f(x)=f(x_0) x→x0limf(x)=f(x0)。
可导: lim Δ x → 0 f ( x 0 + Δ x ) − f ( x 0 ) Δ x \lim\limits_{\Delta x\rightarrow 0}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x} Δx→0limΔxf(x0+Δx)−f(x0) 存在,则 y = f ( x ) y=f(x) y=f(x) 在 x 0 x_0 x0 处可导。
-
什么是连续,什么是一致连续,有什么区别?
连续:设函数 y = f ( x ) y=f(x) y=f(x) 在点 x 0 x_0 x0的某一邻域内有定义,如果
则称函数 y = f ( x ) y=f(x) y=f(x) 在点 x 0 x_0 x0 连续。即:控制 x x x 与 x 0 x_0 x0 的距离,就可以使相应的函数值 f ( x ) f(x) f(x) 离 f ( x 0 ) f(x_0) f(x0) 就可以要多近有多近。一致连续:
即:不论在区间 I I I 的任何部分,只要自变量的两个数值接近到一定程度,相应的函数值就可以达到指定的接近程度。这个一定 程度对全体区间上的点都奏效。
区别:
- 范围不同:连续是局部性质,一般只对单点,而一致连续是整体性质,要对定义域上的某个子集。
- 连续性不同:如果一个函数具有一致连续性则一定具有连续性,而函数具有连续性并不一定具有一致连续性。
-
什么是函数的极限?
在自变量的某个变化过程中,若对应的函数值无限接近于一个确定的常数,那这个确定的常数就叫做这一变化的过程中函数的极限。
注:函数的极限表示函数的变化趋势,并不表示某个值。
-
什么是上界,什么是上确界,有什么区别?
对一个函数 f ( x ) f(x) f(x) 来说,上界是存在实数 M M M,使得 f ( x ) < M f(x)<M f(x)<M恒成立,则 M M M 为该函数的上界。上确界是最小的上界。
比如, [ 1 , 2 ] [1,2] [1,2]的上界可以是 3 , 4 , 5... 3,4,5... 3,4,5...,但是上确界是 2 2 2
-
一阶导数、二阶导数的几何含义,物理含义
一阶导数:一阶求导是求函数各点的斜率,体现的是函数的增减规律
二阶导数:二阶导数是求函数导数的增减规律,体现函数的凹凸性
-
函数的凹凸性
驻点:一阶导数为 0 的点;拐点:二阶导数为 0 的点
凸函数: f ( x 1 + x 2 2 ) ≥ f ( x 1 ) + f ( x 2 ) 2 f(\frac{x_1+x_2}{2})\ge \frac{f(x_1)+f(x_2)}{2} f(2x1+x2)≥2f(x1)+f(x2) , x 1 、 x 2 x_1、x_2 x1、x2 为区间上任意两点,开区间上满足二阶导数恒小于 0。
凹函数:跟凸函数相反。
-
介绍梯度和散度,其作用是什么
梯度:一个向量。在微积分里面,对多元函数的参数求偏导,把求得的各个参数的偏导数以向量的形式写出来,就是梯度。比如 g r a d f = ( ∂ f ∂ x , ∂ f ∂ y ) gradf=(\frac{\partial f}{\partial x},\frac{\partial f}{\partial y}) gradf=(∂x∂f,∂y∂f) 称为函数 f ( x , y ) f(x,y) f(x,y) 的梯度。
梯度向量的几何意义:函数变化增加最快的地方。沿着梯度向量的方向,更容易找到函数的最大值。反过来说,沿着梯度向量相反的方向,更容易找到函数的最小值。
散度:一个数,可用于表征空间各点矢量场发散的强弱程度。
物理意义:表示场的有源性。当 d i v F > 0 div F>0 divF>0,表示该点有散发通量的正源(发散源);当 d i v F < 0 div F<0 divF<0表示该点有吸收通量的负源(洞或汇);当 d i v F = 0 div F=0 divF=0,表示该点无源。
-
高斯公式
向量场穿过曲面的通量,等于散度在曲面围起来的体积上的积分,沟通了曲面积分与三重积分之间的联系,将曲面积分化为三重积分求解。
信号与系统
-
傅里叶、反傅里叶变换公式,有何作用?
傅里叶变换:从时域变换到频域
反傅里叶变换:从频域变换到时域
-
什么是频谱
频谱:频率的分布曲线,复杂振荡分解为振幅不同和频率不同的谐振荡,这些谐振荡的幅值按频率排列的的图形叫做频谱。
作用:求得动态信号中的各个频率成分和频率分布范围,求出各个频率成分的幅值分布和能量分布,从而得到主要幅度和能量分布的频率值。
-
常用的处理信号的方法?
傅里叶变换、反傅里叶变换
最小二乘法:利用已知参数去拟合出一个最优的线性函数,再利用这个函数方程去预测一个未知的目标值。而这个最优就体现在实际值与预测值的最小误差平方上。为什么是最小误差的平方而不是最小误差呢?
- 最小误差的平方是随时可导的,而最小误差不是,这在深度学习神经网络领域中有重大意义
- 平方可放大两两之间的误差,对于择优问题有提升精度的作用
卷积
拉普拉斯变换:拉普拉斯变换是对连续时域序列转换到复频域上的一种数学变换。
-
描述傅里叶变换和拉普拉斯变换的区别
拉普拉斯变换是傅里叶变换的延申,而傅里叶变换是拉普拉斯变换的一个特例。
拉普拉斯变换是傅立叶变换的推广,傅立叶变换适用的前提是信号收敛,而拉氏变换相当于是带有一个指数收敛因子的傅立叶变换,把频域推广到复频域,能分析的信号更广。
离散数学
-
二元关系、逆关系、关系矩阵、关系图
A 、 B A、B A、B 为两个非空集合,则 A × B A\times B A×B 的任何子集 R R R 为从 A A A 到 B B B 的二元关系,简称关系。如果 A = B A=B A=B,则称 R R R 为 A A A 上的二元关系。
-
关系的性质、闭包的求解
-
等价关系和等价类、偏序关系、拟序关系
偏序关系跟等价关系差不多,但是关系有自反性,非对称性和传递性
拟序关系跟偏序关系差不多,但是关系有反自反性和传递性
-
单射、满射、双射
-
有穷集、无穷集
有穷集(有限集合):集合元素是有限的;无穷集(无限集合):集合元素是无限的
等势: A 、 B A、B A、B 集合,如果存在 A A A 到 B B B 的双射函数,那么 A A A 和 B B B 等势
有穷集/无穷集:一个集合是有穷集的当且仅当它与某个自然数等势,否则为无穷集
-
关于图
无向图:边没有方向
有向图:边有方向
简单图:不存在顶点到其自身的边,且同一条边不重复出现
无向完全图:任意两点间都有边相连
有向完全图:任意两点间都有两条方向相反的有向边相连
悬挂节点:度数为1的结点。以悬挂节点为端点的边为悬挂边
-
通路、回路、连通
通路:给定起点和终点,以结点和边交替出现的序列称为一条路通
基本通路:结点和边都无重复
简单通路:结点可重复,边无重复
回路:给定起点和终点(两者一样),以结点和边交替出现的序列称为一条路通
基本回路:回路且结点和边都无重复
简单回路:回路且结点可重复,边无重复
若 D D D 是一个有向图
连通图:略去 D D D 中各边方向后所得无向图是连通图(任意两点都是连通的),则称 D D D 是弱连通图,简称连通图。连通是自反,对称,传递的,所以是等价关系。
单向连通:若 D D D 中任意两顶点至少一个可达另一个,则称 D D D 是单向连通图。
强连通:若 D D D 中任何一对顶点都是相互可达的,则称 D D D 是强连通图。
强连通图一定是单向连通图,单向连通图一定是弱连通图。反之不成立。
-
欧拉图和哈密顿图
欧拉通路:图中所有边一次且仅一次的通路
欧拉回路:图中所有边一次且仅一次的回路
欧拉图:具有欧拉回路的图
图类型 无向图 有向图 条件1 图是连通的,不能有孤立的点存在 图是连通的,不能有孤立的点存在 条件2 所有顶点的度数均为偶数 每个顶点的入度要等于出度 半欧拉图:具有欧拉通路但是无欧拉回路的图
图类型 无向图 有向图 条件1 图是连通的,不能有孤立的点存在 图是连通的,不能有孤立的点存在 条件2 奇度数结点个数为0或2 存在两个顶点,其入度不等于出度 哈密顿通路:图中所有结点一次且仅一次的通路
哈密顿回路:图中所有结点一次且仅一次的回路
哈密顿图:具有哈密顿回路的图
半哈密顿图:具有哈密顿通路但是无哈密顿回路的图
旅行商问题:一个售货员须访问 n n n 个城市,恰好访问每个城市一次,并最终回到出发城市。其实就是求图的最短哈密尔顿回路问题。
操作系统
-
操作系统的特征?
并发、共享、虚拟、异步
-
什么是系统调用?
计算机的各种硬件资源是有限的,为了更好的管理这些资源,用户进程是不允许直接操作的,所有对这些资源的访问都必须由操作系统控制。为此操作系统为用户态运行的进程与硬件设备之间进行交互提供了一组接口,这组接口就是所谓的系统调用。即系统调用是用户进程进入内核的接口层。
系统调用实质上就是函数调用,只不过调用的是系统函数,处于内核态而已。 用户在调用系统调用时会向内核传递一个系统调用号,然后系统调用处理程序通过此号从系统调用表中找到相应的内核函数执行,最后返回。
-
什么是进程?什么是线程?有什么区别?
进程:正在运行的应用程序的实例。包含进程控制块、程序段和数据段
线程:进程中运行的实体,是进程的一条执行路径
区别:(1)一个进程可以有多个线程,线程依赖于进程而存在
(2)进程拥有独立的内存单元,而进程内的多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的 栈段和寄存器组。
(3)进程是资源分配的最小单元,线程是CPU调度的最小单元
(4)进程切换开销远大于线程切换开销
(5)一个线程挂掉,对应的进程挂掉;一个进程挂掉,不会影响其他进程
具体实例:例如一个浏览器是一个进程,那么有http请求线程、事件响应线程、渲染线程等
-
为什么有了进程还要有线程?
- 进程频繁进行上下文切换将引起额外开销,从而严重影响系统的性能。为了减少这种开销,就用更小粒度的执行单元放入进程来实现并发执行,这就是线程。
- 线程比进程更轻量级,故比进程更容易创建和撤销。
- 进程间信息难以共享,但多个线程由于共享进程的内存使得线程间进行信息交换十分方便。
-
进程通信的方式有哪些?分别详细说明,并比较它们之间的不同
进程间通信主要包括管道、系统IPC(包括消息队列、信号量、信号、共享内存)、套接字socket。
-
管道:包括无名管道和命名管道,无名管道半双工,只能用于具有亲缘关系的进程直接的通信(父子进程或者兄弟进程),可以看作一种特殊的文件;命名管道可以允许无亲缘关系进程间的通信。
-
系统IPC
消息队列:消息的链接表,放在内核中。消息队列独立于发送与接收进程,进程终止时,消息队列及其内容并不会被删除;消息队列可以实现消息的随机查询,可以按照消息的类型读取。
信号量semaphore:是一个计数器,可以用来控制多个进程对共享资源的访问,使得资源在一个时刻只有一个进程独享。信号量用于实现进程间的互斥与同步。
信号:用于通知接收进程某个事件的发生。
内存共享:使多个进程访问同一块内存空间。
-
套接字socket:用于不同主机直接的通信。
进程同步:信号量,管道,消息队列
-
-
什么是进程同步、互斥?
进程同步:并发性带来了异步性,有时需要通过进程同步解决这种异步问题。有的进程之间需要相互配合地完成工作,各进程的工作推进需要遵循一定地先后顺序。
进程互斥:对临界资源的访问,需要互斥地进行。即同一时间段内只允许一个进程访问该资源。
-
说说进程有多少种状态?
进程有五种状态:创建、就绪、执行、阻塞、终止。一个进程创建后,被放入队列处于就绪状态,等待操作系统调度执行,执行过程中可能切换到阻塞状态(并发),任务完成后,进程销毁终止。
-
进程通信中的管道实现原理是什么?
操作系统在内核中开辟一块缓冲区(称为管道)用于通信。管道是一种两个进程间进行单向通信的机制。因为这种单向性,管道又称为半双工管道,所以其使用是有一定的局限性的。半双工是指数据只能由一个进程流向另一个进程(一个管道负责读,一个管道负责写);如果是全双工通信,需要建立两个管道。管道分为无名管道和命名管道,无名管道只能用于具有亲缘关系的进程直接的通信(父子进程或者兄弟进程),可以看作一种特殊的文件,管道本质是一种文件;命名管道可以允许无亲缘关系进程间的通信。
-
进程调度算法有哪些?
- 先来先服务调度算法:每次调度都是从后备作业(进程)队列中选择一个或多个最先进入该队列的作业(进程),将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。
- 短作业(进程)优先调度算法:短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业(进程),将它们调入内存运行。
- 高优先级优先调度算法:当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程
- 时间片轮转法:每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。
- 多级反馈队列调度算法:综合前面多种调度算法。
调度算法的衡量标准:CPU利用率、系统吞吐量、周转时间、等待时间、响应时间
-
页面替换算法有哪些?
- 最佳置换算法(OPT):每次选择淘汰的页面将是以后永不使用,或者在最长时间内不再被访问的页面
- 先进先出算法(FIFO):每次选择淘汰的页面是最早进入内存的页面
- 最近最久未使用置换算法(LRU):每次淘汰的页面是最近最久未使用的页面
- 时钟置换算法(CLOCK)
- 线程间通信的方式有哪些?
线程间的通信方式包括临界区、互斥量、信号量、条件变量、读写锁:
- 临界区:每个线程中访问临界资源的那段代码称为临界区(Critical Section)(临界资源是一次仅允许一个线程使用的共享资源)。每次只准许一个线程进入临界区,进入后不允许其他线程进入。不论是硬件临界资源,还是软件临界资源,多个线程必须互斥地对它进行访问。
- 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才可以访问。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。
- 信号量:计数器,允许多个线程同时访问同一个资源。
- 条件变量:通过条件变量通知操作的方式来保持多线程同步。
- 读写锁:读写锁与互斥量类似。但互斥量要么是锁住状态,要么就是不加锁状态。读写锁一次只允许一个线程写,但允许一次多个线程读,这样效率就比互斥锁要高。
-
互斥锁与自旋锁的区别?互斥锁与读写锁的区别?
互斥锁:mutex,用于保证在任何时候都只有一个线程访问临界资源。当获取锁操作失败后,线程会进入睡眠,不再使用CPU,等待 锁被释放时唤醒。
自旋锁:spin-lock,需要资源的进程轮询资源上的锁直到得到它,也称为忙等待。进程将在循环中忙碌,其间一直使用CPU,直到获 得资源。
读写锁:rwlock,分为读锁和写锁。允许多个线程同时获得读操作,但同一时刻只有一个线程可以获得写锁,并且写锁会阻塞其他读 写锁。
-
什么是死锁,产生的条件,如何解决?
死锁: 是指多个进程在执行过程中,因争夺资源而造成了互相等待。此时系统产生了死锁。比如两只羊过独木桥,若两只羊互不相让,争着过桥,就产生死锁。
死锁发生有四个必要条件:
(1)互斥条件:进程对所分配到的资源不允许其他进程访问,若其他进程访问,只能等待,直到进程使用完成后释放该资源;
(2)请求保持条件:进程获得一定资源后,又对其他资源发出请求,但该资源被其他进程占有,此时请求阻塞,而且该进程不会释 放自己已经占有的资源;
(3)不可剥夺条件:进程已获得的资源,只能自己释放,不可剥夺;
(4)环路等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
如何解决:
(1)资源一次性分配,从而解决请求保持的问题
(2)可剥夺资源:当进程新的资源未得到满足时,释放已有的资源;
(3)资源有序分配:系统为资源赋予序号,进程按编号递增请求资源,释放则相反。从而破坏环路等待。
-
内存管理
分页存储管理:把进程分页,各个页面离散地放到各个内存块中。页表记录进程页面和实际存放的内存块之间的对应关系。一个进程对应一张页表,进程的每一项对应一个页表项,每个页表项由页号和块号组成。
如何实现地址转换:通过计算出逻辑地址对应的页号,在页表中找到其对应的块号,再用块起始地址+页内偏移量
优点:页长固定,因而便于构造页表、易于管理,且不存在外碎片,内存利用率高,磁盘换出的消耗小
缺点:页长与程序的逻辑大小不相关
分段存储管理:将地址空间按照程序自身的逻辑关系划分为若干段。段表记录逻辑段到实际存储地址的映射关系。每个段对应一个段表项,各段表项长度相同,每个段表项由段号、段长、基址组成。
如何实现地址转换:根据逻辑地址的段号找到段表中的段长和基址,判断段内地址是否越界,再用基址+段内地址
优点:段的逻辑独立性使其易于编译、管理、修改和保护;段长可以根据需要动态改变
缺点:容易在段间留下许多碎片,造成存储空间利用率降低
-
什么是虚拟内存?虚拟内存是怎么实现的?
虚拟内存:虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。电脑中所运行的程序均需经由内存执行,若执行的程序占用内存很大或很多,则会导致内存消耗殆尽。为解决该问题,Windows中运用了虚拟内存技术,即匀出一部分硬盘空间来充当内存使用。当内存耗尽时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。
如何实现:
- 请求分页存储管理
- 请求分段存储管理
- 请求段页式存储管理
无论哪种实现方式,都需要满足如下条件
-
一定容量的内存和外存:程序执行时,只需要将程序的一部分装入内存,就可以运行了
-
缺页中断:若需要执行的程序未在内存中(即“缺页/段”),则处理器会通知操作系统将相应的页/段调入到内存中,与此同时也会将不常用的页/段调出到外外存中。
缺页中断的概念:当软件试图访问已映射在虚拟地址空间中,但是并未被加载在物理内存中的一个分页时,由中央处理器的内存管理单元所发出的中断
-
虚拟地址空间:逻辑地址转换为物理地址
-
大端、小端
数据的读取是从地位到高位
大端:高位放在低地址
小端:低位放在低地址
例:32位宽的数 0x12345678,在不同的模式下,存储方式为:
地址 0x4000 0x4001 0x4002 0x4003 小端模式 0x78 0x56 0x34 0x12 大端模式 0x12 0x34 0x56 0x78 -
计算机启动过程
- 开机上电:电流稳定之后,主板进行CPU初始化复位工作。复位完成后,CPU执行指令跳转到BIOS(基本输入输出系统),执行系统自检。
- 系统自检:由存放在主板BIOS芯片中的POST程序实现,进行计算机核心部件自检,如CPU,主板,内存,显卡,电源等。
- 运行主引导记录:系统自检结束后,POST程序会检测硬盘中的MBR(主引导记录,系统扇区的一部分),然后读取并执行硬盘MBR中的程序代码。
- 装载操作系统
- 运行操作系统
- 搜索计算机硬件设备,并将硬件设备信息写进操作系统的(注册表文件)中
- 读入操作系统注册表文件,确定应当加载哪些(驱动程序)
- 装载并执行硬件设备驱动程序
- 创建系统坏境,加载操作系统中的各个核心子系统模块,并执行这些子系统模块
-
一个机器上跑的进程如果要转移到另一个机器上需要做哪些工作?
在进行进程迁移时,应将源系统中的已迁移的进程撤消,在目标系统中建立一个相同的新进程。因此,进程映像至少包括进程控制块是必须需要迁移的,另外这个进程与其他进程间的任何链接也必须更新。
计算机网络
-
解释下计算机网络各个层的主要功能,以及给一个人发送微信这其中经历了什么过程?
OSI各层功能:
- 物理层physical layer:通过介质传输位,提供机械和电气规范
- 数据链路层data link layer:将位组成的帧,提供节点到节点的传递
- 网络层network layer:负责将分组从源地址传送到目的地址,可能会通过多个网络
- 传输层transport layer:提供端到端(进程到进程)的可靠报文传递和差错纠正
- 会话层session layer:建立、管理和终止会话
- 表示层presentation layer:翻译、加密和压缩数据
- 应用层application layer:负责向用户提供服务
TCP/IP协议簇:跟OSI差不多,将会话层,表示层,应用层合并成应用层
微信发出去以后,数据都是0和1组成的比特序列;将比特组合成字节进而组合成帧;找到目的手机的地址将数据包发送到指定手机;将数据包发送到指定手机上的指定APP也就是微信;手机需要对这个数据连接会话进行管理,比如收到一段数据以后就进行通知;数据格式的转化,或者加密解密、加压解压;转换完成以后再用微信特有一种"解析处理方式"(不同类型的APP可能有自己特有的解析处理方式) 呈现给用户。
-
TCP/IP协议簇
-
简述 TCP 三次握手和四次挥手的过程
三次挥手:
- 第一次握手:建立连接时,客户端向服务器发送SYN包(seq=x),请求建立连接,等待确认
- 第二次握手:服务端收到客户端的SYN包,回一个ACK包(ACK=x+1)确认收到,同时发送一个SYN包(seq=y)给客户端
- 第三次握手:客户端收到SYN+ACK包,再回一个ACK包(ACK=y+1)告诉服务端已经收到
- 三次握手完成,成功建立连接,开始传输数据
四次挥手
- 客户端发送FIN包(FIN=1)给服务端,告诉它自己的数据已经发送完毕,请求终止连接,此时客户端不发送数据,但能接收数据
- 服务端收到FIN包,回一个ACK包给客户端告诉它已经收到包了,此时还没有断开socket连接,而是等待剩下的数据传输完毕
- 服务端等待数据传输完毕后,向客户端发送FIN包,表明可以断开连接
- 客户端收到后,回一个ACK包表明确认收到,等待一段时间,确保服务端不再有数据发过来,然后彻底断开连接
-
说说 TCP 2次握手行不行?为什么要3次
TCP是全双工通信,两次握手只能确定单向数据链路是可以通信的,并不能保证反向的通信正常
(单工:单方向通信;半双工:双方均能发送和接收,但不能同时进行;全双工:双方能同时发送和接收)
-
什么是SYN泛洪攻击?
攻击者目的是耗尽服务器的CPU和内存资源。攻击者客户端伪造大量客户端ip向服务端发出SYN请求,却直接丢弃掉服务端发送过来的SYN-ACK包,不会回复服务端。那这个连接就处在了一个挂起的状态,也就是半连接。服务器收不到由客户端发来的ACK包,还会继续重复发送SYN-ACK包给客户端,浪费服务器的资源。攻击者对服务器发送非法大量的这种TCP连接,由于每一个都没法完成握手的机制,所以它就会消耗服务器的内存最后可能导致服务器死机,就无法正常工作了。
-
简述TCP和UDP的区别
- TCP协议是有连接的(三次握手)。而UDP是无连接的
- TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到。
- TCP协议所需资源多,TCP首部需20个字节(不含可选项),UDP首部字段只需8个字节。
- TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
- TCP是一对一的连接,而UDP则可以支持一对一,多对多,一对多的通信。
- TCP面向的是字节流的服务,UDP面向的是报文的服务。
-
说说 TCP 可靠性保证
TCP主要提供了检验和、序列号/确认应答、超时重传、最大消息长度、滑动窗口机制(流量控制)等方法实现了可靠性传输。
-
TCP拥塞控制
-
说说浏览器从输入URL到展现页面的全过程
- 输入URL地址
- 浏览器查找域名的 IP 地址
- 浏览器向 web 服务器发送一个 HTTP 请求
- 服务器处理请求
- 服务器返回一个 HTTP 响应
- 浏览器显示 HTML
- 解释流量控制、拥塞控制,两者区别?
流量控制:如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。流量控制根本目的是防止分组丢失,它是构成TCP可靠性的一方面。TCP的流量控制是利用滑动窗口机制实现的,滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。
拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
区别:拥塞控制是作用于网络的,它是防止过多的数据注入到网络中,避免出现网络负载过大的情况;流量控制是作用于接收者的,它是控制发送者的发送速度从而使接收者来得及接收,防止分组丢失的。
-
有哪些路由协议?
RIP基于UDP,是应用层协议;OSPF基于IP,是传输层协议;BGP基于TCP,是应用层协议(有不同说法)
RIP 路由协议:路由信息选择协议。RIP采用距离向量算法,即路由器根据距离选择路由,所以也称为距离向量协议。 路由器收集所有可到达目的地的不同路径,并且保存有关到达每个目的地的最少站点数的路径信息。
OSPF 路由协议:开放最短路径优先协议。OSPF 是一种基于链路状态的路由协议,需要每个路由器向其同一管理域的所有其它路由器发送链路状态广播信息。在OSPF的链路状态广播中包括所有接口信息、所有的量度和其它一些变量。利用OSPF的路由器首先必须收集有关的链路状态信息,并根据一定的算法计算出到每个节点的最短路径。
(精简版)
域内网关协议
- 路由信息协议(RIP)
- 仅和相邻的路由器交换信息
- 一条路径最多只能包含15个路由器
- 开放最短路径优先协议(OSPF)
- 洪泛法向本自治系统中的所有路由器发送信息
域间网关协议
- 边界网关协议(BGP)
- 路由信息协议(RIP)
-
一个主机将两个端口接到网络上能否提升吞吐量?为什么?
如果两个端口均单独使用,那就要看每个端口的吞吐量是否平衡。 如果某个端口吞吐量很小,那就没什么太大区别。
如果端口做了绑定,那么两个物理端口可以当一个逻辑端口用。 这个逻辑端口的最大吞吐量是物理端口的近似2倍。
-
网络中两台主机通信的过程
如果两台主机在同一个网段中,直接通过交换机进行交换(二层交换)
如果两台主机不在同一个网段中,要通过路由器进行交换(三层交换)
计算机组成
-
冯诺依曼计算机体系结构核心思想
- 程序、数据的最终形态都是二进制编码,程序和数据都是以二进制方式存储在存储器中的,二进制编码也是计算机能够所识别和执行的编码。(可执行二进制文件:.bin文件)
- 程序、数据和指令序列,都是事先存在主(内)存储器中,以便于计算机在工作时能够高速地从存储器中提取指令并加以分析和执行。
- 确定了计算机的五个基本组成部分:运算器、控制器、存储器、输入设备、输出设备
-
四种编码方式
正数:原码=反码=补码
负数:反码为原码数值位取反,补码为反码在末尾加1
二者移码都是在补码的基础上符号位取反
-
缓存的原理,在什么情况下失效,如何用硬件实现?
广义缓存:数据高速交换的存储介质,用于数据的更快速访问
狭义缓存:加速CPU交换的存储器
缓存存在的意义就是通过开辟一个新的数据交换缓冲区,来解决原始数据获取代价太大的问题,让数据得到更快的访问
缓存失效:
- 缓存穿透:用户不断发请求访问缓存和数据库中都没有的数据
- 缓存雪崩:缓存在某一时刻同时失效,请求全部转发带数据库,数据库压力过重导致雪崩(即指不同数据同时过期,很多的数据都查不到而查询数据库)
- 缓存击穿:指并发查询同一条数据,缓存中不存在但数据库中存在的数据。此时由于并发用户特别多,同时没能从缓存中读取到数据,而去数据库中去取,引起数据库压力瞬间增大,造成过大压力
硬件实现:将cache放置在CPU和主存之间,作为主存数据的缓存,将当前正在执行的程序何正在访问的数据放在其中。在程序运行时,不需要从慢速的主存中取指令和数据,而是直接访问高速的存储器,从而可以提高CPU程序执行的速度。
-
计算机的存储介质有哪些?
存储介质是指存储数据的载体,由软盘、光盘、U盘、硬盘、SD卡等等
-
五级流水cpu的各阶段
- 取指:将指令从存储器中读取出来
- 译码:将存储器中取出的指令进行翻译
- 执行:对指令进行真正运算的过程
- 访存:将数据从存储器中读出,或者写入存储器
- 写回:将指令执行的结果写回通用寄存器组
-
执行单条指令时单周期CPU和五级流水CPU谁更快?为什么?
单周期CPU:单周期处理器在一个时钟周期内完成整个指令的执行。
五级流水线CPU:在五级流水线CPU中,一条指令的执行被划分为5个阶段:取指(IF),译码(ID),执行(EX),访存(MEM)和写回(WB)。这意味着,一条指令的执行时间是5个时钟周期。
在理想情况下,五级流水线CPU的时钟周期会比单周期CPU的时钟周期短。然而,在执行单条指令时,五级流水线CPU需要5个时钟周期,而单周期CPU仅需要1个时钟周期。所以,对于执行单条指令,单周期CPU可能更快。而在处理大量指令时,五级流水线CPU通常会比单周期CPU更快。
-
总线是什么?
连接在两个以上数字系统元器件的信息通路
-
什么是DMA?
DMA(Direct Memory Access),即直接存储器存取,是一种快速传送数据的机制。是一种完全由硬件执行I/O交换的工作方式,DMA控制器从CPU完全接管对总线的控制。数据交换不经过CPU,而直接在内存和I/O设备之间进行。DMA控制器采用以下三种方式:
- CPU停止工作:CPU放弃对总线的控制权,放弃对主存的访问转而由DMA控制
- 周期挪用:当I/O设备没有 DMA请求时,CPU按程序要求访问内存;一旦 I/O设备有DMA请求,则I/O设备挪用一个或几个周期
- DMA与CPU交替分时工作:把时间分为两个时间片,一片分给CPU,一片分给DMA,使CPU和MAC交替地访问主存
-
中断是什么?中断的分类
中断:CPU停下当前的工作任务,去处理其他事情,处理完后回来继续执行刚才的任务(CPU从用户态切换到核心态)
关中断:不允许中断。作用是实现原子操作。标志位时IF:Interrupt Flag,存在于PSW中,值为0时代表关中断。
中断分类:
- 外部中断:与当前执行的指令无关,中断信号来源于CPU外部
- 可屏蔽中断:通过INTR线向CPU请求的中断,主要来自外部设备如硬盘,打印机,网卡等。此类中断并不会影响系统运行,可随时处理,甚至不处理,所以名为可屏蔽中断。关中断时不会被响应。
- 不可屏蔽中断:通过NMI线向CPU请求的中断,如电源掉电,硬件线路故障等。这里不可屏蔽的意思是问题太大,屏蔽不了,不能屏蔽的意思。关中断时也会被响应。
- 内部中断(软中断、异常):与当前执行的指令有关,中断信号来源于CPU内部
- 陷阱:是一种有意的,预先安排的异常事件,一般是在编写程序时故意设下的陷阱指令,而后执行到陷阱指令后,CPU将会调用特定程序进行相应的处理,处理结束后返回到陷阱指令的下一条指令。如系统调用,程序调试功能等。例如printf函数,最底层的实现中会有一条int 0x80指令,这就是一条陷阱指令,使用0x80号中断进行系统调用。
- 故障:由错误条件引起的,可能被内核程序修复。内核程序修复故障后会把CPU使用权还给应用程序。常见的故障为缺页,当CPU引用的虚拟地址对应的物理页不存在时就会发生故障。缺页异常是能够修正的,有着专门的缺页处理程序,它会将缺失的物理页从磁盘中重新调进主存。而后再次执行引起故障的指令时便能够顺利执行了。
- 终止:执行指令的过程中发生了致命错误,不可修复,程序无法继续运行,只能终止,通常会是一些硬件的错误。终止处理程序不会将控制返回给原程序,而是直接终止原程序。
- 外部中断:与当前执行的指令无关,中断信号来源于CPU外部
-
中断控制器
每个独立运行的外设都可以是一个中断源,能够向CPU发送中断请求,为了方便管理和减少引脚数目,设立了中断控制器,让所有的可屏蔽中断都通过INTR信号线与CPU进行交流。如x86系统中的8259
-
中断过程
当前进程正在CPU上运行,CPU正常进行取指令,执行指令(PC+1)。在每个指令执行周期的末尾,CPU例行检查是否有中断信号需要进行处理
- 中断请求:中断源向CPU发出中断请求信号
- 中断响应:中断隐指令(中断周期)
- 关中断:在中断服务程序中,为了保护中断现场期间不被新的中断所打断,必须关中断,从而保证被中断的程序在中断服务程序执行完毕后能接着正确的执行下去
- 保存断点:为了保证在中断服务程序执行完毕后能正确的返回原来的程序,必须将原来程序的断点(即PC的内容)保存起来。可以存入堆栈。
- 引出中断服务程序:引出中断服务程序的实质就是取出中断服务程序的入口地址(中断向量)并传送给程序计数器(PC)
- 中断处理:中断服务程序
- 保护现场:保存通用寄存器和状态寄存器的内容(即保存中断前的寄存器内容),以便返回原程序后可以恢复CPU环境。可使用堆栈(跟保存断点一样)。
- 中断服务:不同的中断请求需要不同的中断服务程序
- 恢复现场:通过出栈指令把之前保存的信息送回寄存器中
- 开中断,中断返回
数据结构
-
排序(重要,牢记)
-
堆排序详解(以大顶堆为例)
维护堆的性质时间复杂度 O ( l g n ) O(lgn) O(lgn)
从根节点,左右子节点找到最大的值使之成为根节点,并交换其与根节点的位置。它的子树可能违背大顶堆,继续调整。
建堆时间复杂度 O ( n ) O(n) O(n)
找到序号为 i / 2 i/2 i/2 的节点,维护堆的性质,按照层次遍历的顺序往前依次调整。
-
快排过程
快排在每次递归调用时划分数组划分的很不均匀,即一边有0个元素,一边有 n − 1 n-1 n−1 个元素有最坏的时间复杂度
-
判断一个链表有没有环?如何判断环的起点?
快慢指针:首先创建两个指针1和2同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。(跑步套圈)
在确认有环之后,只要让任意一个节点回到起点,然后同时移动两个节点,当节点再次相遇时,所在的位置就是入环的位置。
-
栈的基本操作
pop、push、empty、size、top
-
堆和栈的区别
堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。
栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。还用于保存函数调用栈,即递归压栈的操作。
区别:
- 堆是由低地址向高地址扩展;栈是由高地址向低地址扩展
- 堆中的内存需要手动申请和手动释放;栈中内存是由OS自动申请和自动释放,存放着参数、局部变量等内存
- 堆中频繁调用malloc和free,会产生内存碎片,降低程序效率;而栈由于其先进后出的特性,不会产生内存碎片
- 堆的分配效率较低,而栈的分配效率较高
-
平衡二叉树和二叉排序树
二叉排序树或者是空树,或者具有如下性质:
-
若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
-
若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
-
左、右子树也分别为二叉排序树;
二叉排序树的查找:最坏情况需要的查找时间取决于树的深度
- 当二叉排序树接近于满二叉树时,其深度为 l o g 2 n log_2n log2n ,因此最坏情况下的查找时间为 O ( l o g 2 n ) O(log_2n) O(log2n),与折半查找是同数量级的
- 当二叉树形成单枝树时,其深度为 n n n,最坏情况下查找时间为 O ( n ) O(n) O(n),与顺序查找属于同一数量级
为了保证二叉排序树查找有较高的查找速度,希望该二叉树接近于满二叉树,或者二叉树的每一个节点的左、右子树深度尽量相等
平衡二叉树或者是空树,或者具有如下性质
- 是二叉排序树
- 左右的子树高度之差绝对值不超过1
- 左右子树都是平衡二叉排树
-
-
二叉树类别
- 满二叉树:除了最后一层的节点没有任何子节点外,每层上的所有节点都有两个节点的二叉树
- 完全二叉树:一颗二叉树中,只有最下面两层节点的度可以小于2,并且最下层的叶节点集中在靠左的若干位置上。
- 二叉排序(搜索)树和平衡二叉树如上
- 红黑树:弱平衡的二叉搜索树
- 每个结点要么是红的,要么是黑的
- 根节点是黑的
- 每个叶节点都是黑的
- 如果一个结点是红色的,那么它的两个子节点都是黑的
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
- 堆:完全二叉树,分为大顶堆和小顶堆。是优先队列的实现基础
-
说一说map和unordered_map的底层实现
map底层是基于红黑树实现的,因此map内部元素排列是有序的。而unordered_map底层则是基于哈希表实现的,因此其元素的排列顺序是杂乱无序的。
-
图和树的遍历
DFS:一条路径走到底后需要返回上一步,搜索第二条路径。在树的遍历中,首先访问到最深的节点,然后回溯到它的父节点,遍历另一条路径,直到遍历完所有节点。图也类似,如果某个节点的邻居节点都已遍历,回溯到上一个节点。重点在于递归,使用栈数据结构。
BFS:先访问完当前顶点的所有邻接点,然后再访问下一层的所有节点,该算法适用于解决最短、最小路径等问题。广度搜索是同时搜索所有路径,相当于一层一层地搜索,就好比波浪的扩展一样。此搜索方法跟树的层次遍历类似,因此一般都用队列存储结构。
-
拓扑排序
拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面 两个条件:
1. 每个顶点出现且只出现一次。2. 若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
如何写出拓扑排序:
1. 从 DAG 图中选择一个 没有前驱(即入度为0)的顶点并输出。2. 从图中删除该顶点和所有以它为起点的有向边。3. 重复 1 和 2 直到当前的 DAG 图为空或**当前图中不存在无前驱的顶点为止**。后一种情况说明**有向图中必然存在环**(可以用来判断有向图中是否有环)。
还有没有其他能判断图中是否有环的方法?
DFS,如果一条深度遍历路线中如果有结点被第二次访问到,那么有环。
算法
-
能解释下递归和循环的区别与联系吗?
联系:递归和循环的本质都是代码复用、递归和循环在理论上具有相同的计算能力、递归是一种特殊的循环
区别:
- 递归由程序和系统共同完成,需要系统维护一个工作栈;循环由程序单独完成,需要程序设定好循环条件。
- 递归的复用单位为函数;循环的复用单位为语句
- 递归通常是自顶向下;循环既可以是自顶向下,也可以是自底向上
- 递归代码简洁清晰,易于理解,但是运行效率低,需要考虑栈溢出问题;循环运行效率高,对存储空间占用少,但多重循环可读性差。
-
分治:归并排序以及上面的快速排序
-
分治和DP的联系与区别?
联系:二者都要求原问题具有最优子结构性质(最优子结构:原问题的最优解包含子问题的最优解),都是将原问题分而治之,分解成若干个规模较小(小到很容易解决的程序)的子问题,然后将子问题的解合并形成原问题的解
区别:分治法划分后的子问题是相互独立的,通过用递归(递归的每一步产生全新的子问题)来做;动态规划将分解后的子问题理解为相互间有联系,有重叠部分(重叠子问题:不同的子问题有公共的子子问题),需要记忆,通常用迭代来做。
-
DP与贪心的联系与区别?
联系:都具有最优子结构性质
区别:贪心是每一步的最优解一定包含上一步的最优解,上一步之前的最优解则不作保留;动态规划是全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有的局部最优解,本质是穷举法。贪心是一种最简单的动态规划,时间复杂度较低。
-
DP的两种等价实现方法?
- 带备忘录的自顶向下法(递归+备忘)。此方法按自然的递归形式编写过程,但会保存每个子问题的解(通常保存在一个数组或者散列表中)。当需要一个子问题的解时,过程首先检查是否已经保存过此解。如果是,则直接返回保存的值,节省计算时间;否则,计算子问题并记录。
- 自底向上法。这种方法一般需要恰当定义子问题规模的概念,使得任何子问题的求解都只依赖于更小的子问题的求解。因而我们可以将子问题按规模排序,按由小至大的顺序进行求解。当求解某个子问题时,它所依赖的那些更小的子问题都已求解完毕,结果已经保存。
-
DP的四个步骤
- 刻画一个最优解的结构特征
- 递归地定义最优解的值
- 计算最优解的值,通常采用自底向上的方法
- 利用计算出的信息构造一个最优解
-
哈夫曼编码过程
哈夫曼编码是哈夫曼树的一种应用,广泛用于数据文件压缩。哈夫曼编码基于贪心:每次都从待排序列里选出最小的两个数进行组合生成一棵树,根节点为两数之和,将根节点值加入待排序列重复此操作,直到待排序列空。得到一颗哈夫曼树,从根节点开始往下走,左节点为0右节点为1。
-
Top(K)问题:以第几小值为例
这其实也有两种,一种是第K个值,一种是前K个值,但是基本想法都一样
- 直接sort排序,然后取第K个值,时间复杂度为 O ( n l g n ) O(nlgn) O(nlgn)
- 快速选择:这种方法类似于快速排序。首先选择一个划分元,将比这个划分元大的元素放到它的后面,比划分元小的元素放到它的前面,此时完成了一趟排序。如果此时这个划分元的序号index刚好等于K,那么这个划分元以及它左边的数,刚好就是前K个最小的元素;如果index > K,那么前K小的数据在index的左边,那么就继续递归的从index-1个数中进行一趟排序;如果index < K,那么再从划分元的右边继续进行排序,直到找到序号index刚好等于K为止。再将前K个数进行排序后,返回Top K个元素。这种方法就避免了对除了Top K个元素以外的数据进行排序所带来的不必要的开销。时间复杂度最优为 O ( n ) O(n) O(n),最差情况跟快排一样,退化为 O ( n 2 ) O(n^2) O(n2)
- 堆排序:手写堆排,创建大顶堆(小顶堆),再调整堆的过程中找到第K个值。创建堆时间复杂度 O ( n ) O(n) O(n); k k k次调整堆 O ( k l o g n ) O(klogn) O(klogn)
- 优先队列:调用现有API,其实也就是堆排序
-
最小生成树算法
生成树:一个连通树的生成树是一个极小的连通子图,包含图中全部的 n n n 个节点,但只有构成一棵树的 n − 1 n-1 n−1 条边(对于包含 n n n 个顶点的无向完全图最多包含 n n − 2 n^{n-2} nn−2 颗生成树。)
最小生成树:带权图的最小生成树就是边的权值最小的生成树
最小生成树算法:克鲁斯卡尔算法(Kruskal)和普利姆算法(Prim)
两种算法都是基于贪心:克鲁斯卡尔是不断选择权值最小的边,判断加入会不会使生成树有环(终点数组),不会生成环就加入,否则看下一条边,直到所有顶点加入。而普利姆算法是随机选择一个点,找与此点间边权值最小的点,加入,再找点,直到所有点加入。
克鲁斯卡尔的时间复杂度为 O ( E l o g E ) O(ElogE) O(ElogE) 或者 O ( E l o g V ) O(ElogV) O(ElogV),其中 E E E 代表图中的边的数目, V V V 代表图中的顶点数目。(对图中的边按照非降序排列需要 O ( E l o g E ) O(ElogE) O(ElogE) 的时间。)Prim算法的复杂度是 O ( n 2 ) O(n^2) O(n2) 级别的。
为什么最小生成树的算法(这是克鲁斯卡尔算法的证明)是基于贪心的,但是得到的结果是全局最优解而不是局部最优解?
(突发奇想:迪杰斯特拉算法跟普利姆算法有什么区别?)
-
单源最短路径算法
-
Dijkstra算法:广度优先+松弛。适用于带权重的有向图,权重大于等于0。基于贪心
- 所有点分成两个集合S和T,S最开始只包含源点s,剩余点都在T。S集合表示已经计算出最短路径点的集合,T表示尚未计算出最短路径点的集合。
- 每次从集合T中选出一个与集合S距离最短的点v,将v加入集合S。对所有从v出发的边松弛,更新T中点到源点的最短距离。
- 重复上述步骤知道T集合中无法找到与集合S相邻的点。
算法时间复杂度取决于距离最短点v的选取,即最小优先队列的实现,设 n n n 为节点数, m m m 为边数
- 遍历, O ( n 2 ) O(n^2) O(n2)
- 二叉堆, O ( ( n + m ) l g n ) O((n+m)lgn) O((n+m)lgn)
- 斐波那契堆, O ( n l g n + m ) O(nlgn+m) O(nlgn+m)
-
bellman-ford算法:适用于带权重的有向图,权重可为负。但如果图中存在可以从源点到达的权重为负的环路,算法返回false
- 对所有的边进行 n − 1 n-1 n−1 轮松弛
- 即第一轮在所有边进行松弛后,得到的是源点最多经过一条边达到其他顶点的最短距离;第二轮在所有边进行松弛后,得到的是源点最多经过两条边达到其他顶点的最短距离…
算法时间复杂度 O ( V E ) O(VE) O(VE)
-
所有结点对的最短路径
Floyd算法:基于动态规划,时间复杂度为 O ( n 3 ) O(n^3) O(n3)
// 执行Floyd算法
for(i = 0; i < V; i++) { //i为中转点for (j = 0; j < V; j++) { //j为起点for (k = 0; k < V; k++) { //k为终点//循环比较dist[j][k]和dist[j][i] + dist[i][k]最小值//如果dist[j][i] + dist[i][k]为更小值//则把dist[j][i] + dist[i][k]覆盖保存在dist[j][k]中。if (dist[j][i] + dist[i][k] < dist[j][k]) {dist[j][k] = dist[j][i] + dist[i][k];}} }
}
编译原理
-
GCC的全称,GCC的原理(编译链接过程)?
GNU编译器(GCC, GNU Compiler Collection)
- 预处理(Pre-Processing):将所有的#include头文件以及宏定义替换成其真正的内容,生成
.i
文件 - 编译(Compilation):将经过预处理之后的程序转换成特定汇编代码(assembly code),生成
.s
文件 - 汇编(Assembling):调用汇编器(AS)将上一步的汇编代码转换成机器码(machine code),生成
.o
文件 - 链接(Linking):调用链接器(LD)将多个目标文件以及所需的库文件链接成最终的可执行文件(executable file)
- 预处理(Pre-Processing):将所有的#include头文件以及宏定义替换成其真正的内容,生成
-
什么是静态链接?什么是动态链接?
静态链接,是在链接的时候就已经把要调用的函数或者过程链接到了生成的可执行文件中,就算你在去把静态库删除也不会影响可执行程序的执行;生成的静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。
动态链接,是在链接的时候没有把调用的函数代码链接进去,而是在执行的过程中,再去找要链接的函数,生成的可执行文件中没有函数代码,只包含函数的重定位信息,所以当你删除动态库时,可执行程序就不能运行。生成的动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。
-
编译器工作阶段
- 词法分析:将源代码的字符序列分割成一系列的记号。
- 语法分析:对记号进行语法分析,产生语法树。
- 语义分析:判断表达式是否有意义。
- 中间代码生成
- 代码优化
- 目标代码生成
- 符号表管理器和出错处理器贯穿始终
-
编译型和解释型语言的区别
编译型:源程序经过编译器成目标程序,输入数据进目标程序后输出
优点:因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。
缺点:编译之后如果需要修改就需要整个模块重新编译。不同的操作系统之间难以移植,需要根据运行的操作系统环境编译不同的可执行文件
解释型:源程序和输入数据一同经过解释器后输出
优点:有良好的平台兼容性;修改代码的时候直接修改就可以,可以快速部署,不用停机维护
缺点:每次运行的时候都要解释一遍,性能上不如编译型语言
主要区别:运行目标程序时的控制权在解释器而不是在目标程序
-
.exe文件在Linux系统下为什么不能运行,如果想运行该怎么做?
在编译,链接过程中两者用到的共享库不一样:Linux下称作shared object,Windows下简称DLL,一个可执行文件可能需要很多共享库才能运行
在运行过程中两者的运行时系统不一样
怎么解决:使用sudo命令去获取,安装,检测,更新模拟器软件
-
什么是递归下降分析?
递归下降分析法是一种自顶向下的分析方法,文法的每个非终结符对应一个递归过程(函数)。
分析过程就是从文法开始符触发执行一组递归过程(函数),这样向下推到直到推出句子;或者说从根节点出发,自顶向下为输入串寻找一个最左匹配序列,建立一颗语法树。
递归下降分析要求文法是 LL(1)的,所以需要对二义文法进行改造,先改写为非二义的文法,再消除左递归和公共左因子。
C++
-
C语言和C++的区别
- C语言是C++的子集,C++可以很好兼容C语言。但是C++又有很多新特性,如引用、智能指针、auto变量等。
- C++是面对对象的编程语言;C语言是面对过程的编程语言。
- C语言有一些不安全的语言特性,如指针使用的潜在危险、强制转换的不确定性、内存泄露等。而C++对此增加了不少新特性来改善安全性,如const常量、引用、cast转换、智能指针、try—catch等等;
- C++可复用性高,C++引入了模板的概念,后面在此基础上,实现了方便开发的标准模板库STL。C++的STL库相对于C语言的函数库更灵活、更通用。
-
面向对象的概念
面向对象是一种编程思想,把一切东西看成是一个个对象,比如人、耳机、鼠标、水杯等,他们各自都有属性,比如:耳机是白色的,鼠标是黑色的,水杯是圆柱形的等等,把这些对象拥有的属性变量和操作这些属性变量的函数打包成一个类来表示
面向过程和面向对象的区别:
面向过程:根据业务逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
-
面向对象三大特征
面向对象的三大特征是封装、继承、多态。
- 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
- 继承:可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
- 多态:用父类引用或指针指向子类对象并调用子类重写的方法(通俗的说,就是当不同的对象去完成某个行为时会产生出不同的状态)。实现多态有二种方式:重写,重载。
-
什么是 C++ 的重载和重写
重写:是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同,派生类对象调用时会调用派生类的重写函数,不会调用被重写函数。重写的基类中被重写的函数必须有virtual修饰。
重载:函数重载是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
-
什么是虚函数?什么是虚表?
C++中的虚函数的作用主要是实现了多态的机制。关于多态,就是父类引用或指针指向子类对象并调用子类重写的方法。虚函数必须是基类的非静态成员函数。虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数。
C++中每个有虚函数的类,编译器都会为它生成一个虚表,表中的每一个元素都指向一个虚函数的地址。虚表是从属类的。编译器会为包含虚函数的类加上一个成员变量,是一个指向该虚函数表的指针,每一个由此类别派生出来的类,都有这么一个虚表指针。虚表指针是从属于对象的。也就是说,如果一个类含有虚表,则该类的所有对象都会含有一个虚表指针,并且该虚表指针指向同一个虚表。
-
数组和指针的区别
概念:
- 数组:数组是用于储存多个相同类型数据的集合。 数组名是首元素的地址。
- 指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。 指针名指向了内存的首地址。
区别:
-
赋值:同类型指针变量可以相互赋值;数组不行,只能一个一个元素的赋值或拷贝
-
存储方式:
- 数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,数组的存储空间,不是在静态区就是在栈上。
- 指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。
-
求sizeof:
- 数组所占存储空间的内存大小:sizeof(数组名)/sizeof(数据类型)
- 在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。
-
说说什么是函数指针,如何定义函数指针,有什么使用场景
概念:函数指针就是指向函数的指针变量。每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。
定义:
*f
是函数指针,指向的函数返回值是int
,参数列表为int
。而func
正是这样的函数,故f
可以指向它,45行两种方式均可int func(int a); int (*f)(int a); f = func; f = &func;
作用:
-
函数调用:能直接使用指向函数的指针调用该函数,无需解引用指针
int a = f(1); int b = (*f)(1); int c = func(1); //三种方式相同
-
作为函数的参数:虽然不能定义函数类型的参数,但是形参可以是指向函数的指针。
-
-
STL
s.substr(start,end)
reverse(a.begin(),a.end())
杂项
-
字符型和整型的大小是多少?
char:1个字节;short:2个字节;int:4个字节;long:4个字节
32位系统指针占4字节,64位占8字节
-
麦克斯韦方程组是作用是什么?
- 高斯定律:描述电荷如何产生电场
- 高斯磁定律:论述磁单极子不存在
- 麦克斯韦-安培定律:描述传导电流和位移电流怎样产生磁场
- 法拉第感应定律:描述时变磁场如何产生电场
-
IEEE和ACM的中英文全称
电气电子工程师学会:the Institute of Electrical and Electronics Engineers(IEEE)
美国计算机协会:Association for Computing Machinery(ACM)
-
etc. et al.是啥
etc. :它的完整写法是 et cetera,意为”等等”
et al. :是用得最多的,一般在文中引用学者成果或者是参考文献时,罗列作者时的省略
e.g. :全称是 exampli gratia,意为“例如”
i.e. :的全称是 id est,意为“也就是”
-
C语言中二进制文件和文本文件的区别是什么?
两者的区别不是物理上的,而是逻辑上的。是看待数据的方式不同。
文本文件:以文本的ASCII码形式存储在计算机中。它是以"行"为基本结构的一种信息组织和存储方式。
二进制文件:以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们,只有通过相应的软件才能将其显示出来。
-
如何拟合一条曲线?如何拟合一条直线?
曲线:多项式拟合 y = a n x n a n − 1 x n − 1 . . . a 1 x a 0 y = a_nx^na_{n-1}x^{n-1}...a_1xa_0 y=anxnan−1xn−1...a1xa0
直线:线性回归、最小二乘法
-
斐波那契数列递归,问时间复杂度和空间复杂度
#include <iostream> using namespace std;int fib(int n){if(n == 1 || n == 2)return 1;return fib(n - 1) + fib(n - 2); }int main(){int a = fib(30);cout << a << endl;return 0; }
时间复杂度: O ( 2 n ) O(2^n) O(2n),空间复杂度 O ( n ) O(n) O(n)
如果用动态规划实现:时间复杂度 O ( n ) O(n) O(n)
-
sql的几种子句是什么?顺序是什么?
-
随机算法有哪些?
- 数值随机算法(时间复杂度 O ( N ) O(N) O(N))
- 拉斯维加斯算法
算法获得的解一定是问题的正确解,但算法也可能不能获得问题的解,获得正确解的概率为 p p p ,其中 0 < p < 1 0<p<1 0<p<1 。这种随机算法叫做 p p p -正确的拉斯维加斯算法。(要么获得正确解,要么没有解)
- 舍伍德算法
像这种在算法中引入随机性来消除或减少算法时间复杂度在问题好坏实例之间的差别,这种随机算法叫做舍伍德算法,它总能得到问题的一个解,且所求得的解总是正确的!(就是随机的在序列里挑一个数啊为什么那老师说不对zzz)
随机快排期望的时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
-
蒙特卡洛算法
算法并不总能获得问题的正确解;算法以较高的概率获得正确解;不存在有效过程判定算法输出的解是否为问题的正确解,这种随机算法叫做蒙特卡洛算法。(可以随着尝试次数的增加而提高获得正确解的概率)