2023秋招大厂经典面试题及答案整理归纳(221-240)校招必看

目录

221. Linux大文件如何处理,如何分割?

222.你了解linux常用命令有哪些?

223.解释下数据库ACID什么意思

224. 说说代理设计模式,并画一下代理模式结构图.

225. 什么是开闭原则.

226. mysql联合索引的原理?

227. 数据库查询慢的原因?

228. 说说你了解的排序算法,如何优化一个算法,让其时间复 杂度降到nlogn.

229.如何划分子网?

230. C++智能指针如何解决内存泄露问题.

231. 常见web安全问题,SQL注入、XSS、CSRF,基本原理以 及如何防御

232. linux中软连接和硬链接的区别.

233. STL内存分配方式

234. TCP的拥塞控制机制是什么?请简单说说

235. Mysql连接语句如何实现快速查找?

236. 输入一个矩阵,按照从外向里以顺时针的顺序依次打印岀 每一个数字,例如,如果输入如下4X4矩阵:12345678 9 10 11 12 13 14 15 16则依次打印岀数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

237. 利用快速排序对单链表进行排序

238. Mysql如何实现快速查找?

239. 将一个链表分为奇偶两个链表

240. 有序数组二分查找,返回查找元素最后一次岀现的位置, 若不存在则返回-1


221. Linux大文件如何处理,如何分割?


使用split对文件进行切割,切割有两种方式
1. 根据行数切割,通过-1参数指定需要切割的行数
示例:指定文件名为split-line, -d参数以数字的方式显示
split -1 300 -d —verbose 文件名 split-line
2. 根据大小切割,通过九参数指定需要切割的大小
示例:指定-b参数指定文件大小进行切割,文件大小单位支持K, M, G, T, P, E, Z.
split ~b 30K ~d —verbose 文件名 split-size



222.你了解linux常用命令有哪些?


1、 Is命令
2、 cd切换
3、 pwd查看当前工作目录路径
4、 mkdir创建文件夹
5、 rm删除文件
7、 mv移动/修改文件名
8、 cp复制
9、 cat显示文件详情
14、which查看可执行文件的位置
16、 locate 命令
17、 find文件树中查找文件
⑤、grep文本搜索命令
18、 chmod访问权限
19、 tar压缩和解压
出、chown改为指定的用户或组
21、 df显示磁盘空间
22、 du查看使用空间
23、 In命令
24、 date显示时间
25 cal命令
27wc命令 匆、ps查看进程
29、 top正执行的进程
30、 kill杀死进程
31、 free显示内存使用情况
32、 reboot开关机命令
33、 if conf ig 查看 ip 地址
34、 用户相关

useradd oldboy整添加用户
passwd redhat。设弟密码
whoami女当前用户
su - oldboy/切换用户
logout住退出用户登录
35、权限相关rSread可读,可以用cat等命令查看
wSwrite写入,可以编辑或者删除这个文件
JJ executable 可以执行


36、特殊字符重定向相关
» #追加重定向,把文字追加到文件的结尾
> #重定向符号,清空原文件所芍内容,然后把文字覆盖到文件末尾
< #输入重定向
« #将输入结果输入重定向
37、 iptables, firewall 防火墙
38、 vi和vim编辑文本
39、 PATH常见环境变量变量
40、 I管道命令
41、 alias起别名命令



223.解释下数据库ACID什么意思


ACID特性即数据库管理系统中事务(transaction)的四个特性:原子性(Atomicity)、 —致性(Consistency)、隔离性(Isolation)、持久性(Durability)
所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分 割的工作单位。(执行单个逻辑功能的一组指令或操作称为事务)
1. 原子性
原子性是指事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发 生。
可采用“A向B转账”这个例子来说明解释
在DBMS中,默认情况下一条SQL就是一个单独事务,事务是自动提交的。只有显式的 使用start transaction开启一个事务,才能将一个代码块放在事务中执行。
2. 一致性
—致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说 数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
如A给B转账,不论转账的事务操作是否成功,其两者的存款总额不变(这是业务逻辑 的一致性,至于数据库关系约束的完整性就更好理解了)。
保障机制(也从两方面着手):数据摩层面会在一个事务执行之前和之后,数据会符合 你设置的约束(唯一约束,外键约束,check约束等)和触发器设置;此外,数据库的内 部数据结构(如B树索引或双向链表)部必须是正确的。业务的一致性一般由开发人 员进行保证,亦可砖移至数据库居面。
3. 隔离性
多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。 在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空 间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更 新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后 的状态,事务不会查看到中间状态的数据。 事务最复杂问题都是由事务隔离性引起的。完全的隔离性是不现实的,完全的隔离性要 求数据摩同一时间只执行一条事务,这样会严重影响性能。
关于隔离性中的事务隔离等级(事务之间影响),参见相应博文
4. 持久性
这是最好理解的一个特性:持久性,意味看在事务完成以后,该事务所对数据库所作的 更改便持久的保存在数据库之中,并不会被回滚。(完成的事务是系统永久的部分,对 系统的影响是永久性的,该修改即使出现致命的系统故障也将一直保持)
write ahead logging : SQL Server 中使用了 WAL (Write-Ahead Logging )技术来保证 事务日志的ACID特性,在数据写入到数据库之前,先写入到日志,再将日志记录变更 到存储器中。



224. 说说代理设计模式,并画一下代理模式结构图.


所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用 实际的对象。
代理模式(Proxy)結构图




225. 什么是开闭原则.


幵闭原则(0CP)是面向对象设计中“可复用设计〃的基石,是面向对象设计中最重要 的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。
幵闭原则中“开”,是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的; 幵闭原则中“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码。



226. mysql联合索引的原理?


联合索引是指对表上的多个列合起来做一个索引。联合索引的创建方法与单个索引的创 建方法一样,不同之处仅在于有多个索引列,如下
create table t(
a int,
b int,
primary key(a),
key idx_a_b (a, b)
);
从本质上来说,联合索引就是一棵B+树,不同的是联合索引的键值得数量不是1,而
是>=2。两个整型列组成的联合索引,假定两个键值得名称分别为a、b如图
I (2,4) £ I
(1.1) (1,2) (2,1) (2,4) (3.1) (3,2)
可以看到这与我们之前看到的单个键的B+树并没有什么不同,键值都是排序的,通过 叶子结点可以逻辑上顺序地读出所有数据,就上面的例子来说,即(L1),(L2),
(2,1) ,(2,4) ,(3,1) ,(3,2);数据按(a,b)的顺序进行了存放。
因此,对于查询 select * from table where a=xxx and b=xxx,显然是可以使用(a, b) 这个联合索引的,对于单个列a的查询select * from table where a=xxx,也是可以 使用(a,b)这个索引的。
但对于b列的查询select * from table where b=xxx,则不可以使用(a,b)索引, 其实你不难发现原因,叶子节点上b的值为1、2、1、4、1、2显然不是排序的,因此 对于b列的查询使用不到(a, b)索引.
联合索引的第二个好处是在第一个键相同的情况下,已经对第二个键进行了排序处理, 例如在很多情况下应用程序都需要查询某个用户的购物情况,并按照时间进行排序,最 后取出最近三次的购买记录,这时使用联合索引可以帮我们避免多一次的排序操作,因 为索引木身在叶子节点已经排序了 .



227. 数据库查询慢的原因?


1. 偶尔效率慢的情况
原因1:刷新“脏”页
1)什么是“脏”页
―当对数据库进行插入或者更新操作旺数据库会立刻将刻存的数据页上的信息更 新,但是不会立刻将将更新的数据有到磁盘上,而是先保存到red。log中,等合 适的时机在将red。log的信息存储到磁盘上
针对这种内存中的数据页和磁盘上的数据不同的情况我们将内存中的数据页称为 “脏”页,而内存中和磁盘上数据相同的情况则称为“干净”页。
刷新“脏〃页时,系统会暂停其他的操作,全身心的将数据存到磁盘中,就会导致 平常正常执行的mysql语句变慢
2)什么时候会刷新“脏”页
redo log装满时,内存不够用时,mysql认为系统空闲时,mysql正常关闭时.会刷 新“脏"页
原因2:数据被锁住
可以用show processlist命令查看一下语句执行的状态,查看要查询的数据是否 被锁住
2. —直都存在效率慢的情况
原因1:查询的数据量太大
查看是否查询了不必要的行与列,谚免用select * from table这样的语句 原因2:没有用到索引
当数据量很大时,若没有用索引采用全表索引是很耗费时间的。而这里没有用到索 引由可以分多钟情况
1) 没有建索引,
2) 索引失效
引起索引失效的可能原因
⑴在索引列上用了内置函数或善其他A*/运算
⑵用通配符开头
⑶多列索引违背最佳最匹配原则
⑷皿操作符容器造成索引失效,除非皿的每个操作列都有索引
⑸字符串不加单引号
3) 系统选错索引
系统选错索引其实是索引失效的一种形式,但是由于涉及到的知识点较多,所以单 独莹出来分析。
系统选错索引导致索引失效时系统将全表扫描与用索引要扫描的行数进行比较,若 是觉得运用索引反而要复杂,则系统就会放弃索引采用全表扫描的方式。



228. 说说你了解的排序算法,如何优化一个算法,让其时间复 杂度降到nlogn.


快速排序
快速排序的基本思想是基于分治策略的,基本思想如下:
分解:先从序列中取出一个元素作为基准,以基准元素为标准将序列分解为两个子序列。 其中小于或等于基准的子序列在左侧,大于基准的子序列在右侧。
治理:对拆分之后的子序列进行快速排序
合并:将排序好的子序列合并在一起,从而得到整个序列的排序。
这像是我们常说的“大事化小,小事化了”,大的困难分解成一个个小的问题,逐个击 破。
分治法后面也会讲解
常见的基准元素选取方式有:选择第一个元素,最后一个元素,中位数等等。 代码

#include<iostream>
using namespace std;
/依取基准元素所在的位置
int GetMid(int arr [], int low, int high)
[
int i = low, j = high, pivot = arr [low];
while (i < j)/佼换后继续扫描
[
while (i<j&&axr[j]>pivot) j—;//右侧开始查找比基准元素更小的值 while (i < j&&axr[i] <= pivot) i++;//左侧查找比基准元素大于等于的值 if (i < j)
[
swap (azr[i++], azr[j-]:; //先交换后赋值
}
}
if (arr[i] > pivot)
[
swaptazrti - 1], azz [low]);/^±代码中提供的示例中,在谒到第一次扫描 寸交换i和j都指向37。如果不加最后的判断会出现错误。
return i ~ 1;
}
swap(arr [i], arr [low]);
return i;
}
//快速排序函数
void Quicksort (int axr [], int low, mt high)
[
int mid;
if (low < high)
[
/施归快排
mid = GetMid(axr, low, high;;
Quicksort(arr, mid + 1, high);〃右区间快排
Quicksort (axr, low, mid - 1:; /佐区间快排

int main()
[
int N = 9;
int axr [9] = { 30, 24, 5, 58, 37, 36,12, 42, 39 };
for (int i = 0; i < N; i++)
[
cout « arr[i] « ”宀
\
cout<<endl;
Quicksort(axr, 0, NT);
for (int i = 0; i < N; i++)
[
cout « azr[i] «
}
cout << endl;

合并排序

合并排序采用的就是分治策略,讲一个大的问题分成很多的小问题。先解决小问题,然 后通过小问题解决大问题。

通过将每一个分解的子序列排序,然后不所的逢归子序列合并。达到总序列的有序排列。 合久必分,分久必合就是合并排序的策略。

代码

#include<iostream>
using namespace std;
//最小单元序列的合并
void Merge (int arr [], int low, int mid, int high)
[
//l.申请与azr等长的数组B
int *B = new int [high - low + 1:;
int i = low, j = mid + 1, k = 0.
//2.将azz数组一分为二,按照升序的规则将元素依次放到B数组中
while (i<=mid&&j<=high)
[
if (azr[i] <= azr[j])
[
B [k++] = axr [i++];
}
else
[
B [k++] = axr [j++];
}
}
//3.将arz数组的升序未排序的部分复制到B,左侧和右侧
while (i <= mid) { B [k++] = axr [i++]; }
while (j <= high) { B [k++] = axr j++]; }
//4.将B数组复制到azr数组,释欣B
for (i = low, k = 0; i <= high; i++)
{
azr [i] = B [k++];
I
delete []b;
}
"合并排序
void MergeSort(int arr f], int low, int high)
//递归合并排序 if (low < high) int mid = (low + high) / 2; MergeSort (axr, low, mid); MergeSort (axr, mid + 1, high); Merge (axr, low, mid, high);
int main()
[
int N = 8;
int a [8] = { 42, 15, 23,6,8, 38, 50,12 };
KergeSort (a, 0, N~l);
for (size* i = 0; i < N; i++)
[
cout « a[i] «

时间复杂度

其中快速排序的平均时间复杂度为。(nl。/)。合并排序的二叉树高度为log:,每一层 都是n个元素进行比较,所以总的时间复杂度为0 (nl of)。


229.如何划分子网?

—个IP地址一共有32位(二进制),其中靠前的某些位表示网络号,后面的某些位表 示主机号,网络位数+ 主机位数=IP地址位数=32,简单来说,子网掩码就是网络号的位 数,不会理解的,我可以举个例子:192.168.0.0/24,这一看我们就知道小型公司常用 的网段,可用 IP 地址:192. 168.0.1-192.168.0. 254,子网掩码:255.255.255.0,斜 杠后面的24指的是网络号,那么显然可用的主机号就变成8位,那么可用的主机数就 是2的8次方-2=254。

计算子网掩码的方法就是:已知子网内IP数的多少,求出主机位的位数,用32减去主 机位数就等于网络位数,也就是子网掩码。举最简单的例子。一个C类网络,包括西6 个主机位置,256是2的8次方,所以主机位是8,那么网络位就是308=24,也就是 说子网掩码是24位,用二进制表示就是11111111.11111111.11111111.00000000,换 算成十进制就是255.255.255.0。再比如一个C类网络划分的子网,每个网络主机IP 数是32,而32是2的5次方,所以主机位是5,那么网络位就是32-5=27,也就是说 子网掩码是27位,用二进制表示就是11111111.11111111.11111111.11100000,换算 成十进制就是255. 255. 255. 224。再比如一个B类网络划分的子网,每个网络主机IP 数是1024,而1024是2的10次方,所以主机位是10,那么网络位就是32-10=22 ,也 就是说子网掩码是22位,用二进制表示就是11111111.11111111.11111100.00000000, 换算成十进制就是255. 255. 252. 0。


230. C++智能指针如何解决内存泄露问题.


1. shazedjtr共享的智能指针
std::shmed_ph使用引用计数,每一个sharedjtr的拷贝都指向相同的内存。在最 后一个shared_ptr析构的时候,内存才会被释放。
可以通过构造函数、std_make_shared辅助函数和reset方法来初始化shared_ptr:
//构造函数初始化
std::shared_ptrp ( new int(1));
std::shared_ptrp2 = p ;
//对于一个未初始化的智能指针,可以通过reset方法来初始化。
std::shared_ptrptr; ptr.reset ( new int (1));
if (ptr) {cout « "ph is not null.\n^ ; }
不能将一个原始指针直接赋值给一个智能指针:
std: : shared_ptrp = new int(l) ;//编译报错,不允许直接赋值
获取原始指针:
通过get方法来返回原始指针
std::shared_ptrptr ( new int(1));
int * p =ptr. get ();
指针删除器:
智能指针初始化可以指定删除器
void DeletelntPtr ( int * p ) {
delete p ;
}
std::shared_ptrp ( new int , DeletelntPtr );
当P的引用技术为o时,自动调用删除器来释放对象的内存。删除器也可以是一个 lambda表达式,例如
std::shared_ptrp ( new int , [](int * p){delete p});
注意事项:
(1) .不要用一个原始指针初始化多个shared_ptro
(2) .不要再函数实参中创建Shared_ptr,在调用函数之前先定义以及初始化它。
(。).不荽将lhi»指针作为 ^Cd_plx返回出来。
0).要避免循环引用。


2. unique_ptr独占的智能指针
unique_ptr是一个独占的智能指针,他不允许其他的智能指针共享其内部的指针,不 允许通过赋值将一个unique_ptr赋值给另外一个unique_ptro
unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过 std::move来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了 如果希望只有一个智能指针管理资源或管理数组就用unique_ptr,如果希望多个智能 指针管理同一个资源就用shared_ptro


3. weak_ptr弱引用的智能指针
弱引用的智能指针weak_ptr是用来监视sharedjtr的,不会使引用计数加一,它 不管理shared_ptr内部的指针,主要是对了监视shared_ptr的生命周期,更像是 shared_ptr的一个助手。
weak_ptr没有重载运算符桥口因为它不共享指针,不能操作资源,主要是为了 通过Shared_ptr获得资源的监测权,它的构造不会増加引用计数,它的析构不会减少 引用计数,纯粹只是作为一个旁观者来监视shmed_ph中关离得资源是否存在。
weak_ptr还可以用来返回this指针和解决循环引用的问题。



231. 常见web安全问题,SQL注入、XSS、CSRF,基本原理以 及如何防御


1. SQL注入
原理:
D.SQL命令可查询、插入、更新、删除等,命令的串接。而以分号字元为不同命 令的区别。(原本的作用是用于Subauery或作为查询、插入、更新、删除……等 的条件式)
2) • SQL命令对于传入的字符串参数是用单引号字元所包起来。(但连续2个单引 号字元,在SQL资料库中,则视为字串中的一个单引号字元)
3) . SQL命令中,可以注入注解
预防:
1) .在设计应用程序时,完全使用参数化查询(ParameterizedQuery)来设计数据 访问功能。
2) .在组合SQL字符串时,先针对所传入的参数作字元取代(将单引号字元取代为 连续个单引号字元)
3) .如果使用PHP开发网页程序的话,亦可打幵PHP的腐术引号(Magic quote)功 能(自动将所有的网页传入参数,将单引号字元职代为连续2个单引号字元)
4) .其他,使用其他更安全的方式连接SQL数据库。例如已修正过SQL注入问题的 数据库
5) .连接组件,例如 http://ASP.NET 的 SqlDataSource 对象或是 LINQ to SQL。 使用SQL防注入糸统。
2. XSS攻击
原理:
xss攻击可以分成两种类型:
1. 非持久型X”攻击
非持久型X”攻击是一次性的,仅对当次的页面访问产生影响。非持久型X”攻击 要求用户访问一个被攻击者篡改后的链接,用户访问该链接时,被植入的攻击脚本 被用户游览器执行,从而达到攻击目的。
2. 持久型XSS攻击
持久型XSS攻击会把攻击者的数据存储在服务器端,攻击行为将伴随着攻击数据 —直存在。下面来看一个利用持久型XSS攻击获取session id的实例。
防范:
1 .基于特征的防御
XSS漏洞和著名的SQL注入漏洞一样,都是利用了 Meb页面的编写不完善, 所以每一个漏洞所利用和针对的弱点都不尽相同。这就给XSS漏洞防御带来了困 难:不可能以单一特征来概括所有XSS攻击。
传统XSS防御多采用特征匹配万式,在所有提交的信息中都进行匹配检查。 对于这种类型的XSS攻击,采用的模式匹配方法一般会需要对“javascript” 个关键字进行检索,一旦发现提交信息中包含“javascript” ,就认定为XSS攻 击。这种检测方法的缺陷显而易见:骇客可以通过插入字符或完全编码的方式躲避 检测:
1) .在javascript中加入多个tab键,得到
<IMG SRC="jav ascript:alert('XSS');">;
2) .在javascript中加入(空格)字符,得到
<IMG SRC="javascri pt:alert('XSS
3) .在javascript中加入(回车)字符,得到
<IMG SRC="javasc
ript:alert(,XSS,);,,>;
4) .在javascript中的每个字符间加入回车换行符,得到
<IMG SRC="javascrip\r\nt:alert('XSS');">
5) .对w javascript:alert( 'XSS')”采用完全编码,得到
<IMGSRC=javascrip?74:alert(1XSS')>
上述方法都可以很容易的躲避基于特征的检测。而除了会有大量的漏报外,基 于特征的
还存在大量的误报可能:在上面的例子中,对上述某网站这样一个地址,由于 包含了关键字“javascript”,也埒会触发报警。


2. 基于代码修改的防御
和SQL注入防御一样,XSS攻击也是利用了 Neb页面的编写疏忽,所以还有 一种方法就是从Web应用开发的角度来避免:
对所有用户提交内容进行可靠的输入验证,包括对URL、查询关键字、HTTP 头、POST数据等,仅接受指定长度范围内、采用适当格式、采用所预期的字符的 内容提交,对其他的「律过滤。
实现Session标记(session tokens) ^CAPTCHA系统或者HTTP引用头检查, 以防功能被第三方网站所执行。
确认接收的的内容被妥善的规范化,仅包含最小的、安全的Tag(没有 javascript),去掉任何对远程内容的引用(尤其是样式表和javascript),使用 HTTP only 的 cookie。
3. CSRF攻击
原理:
CSRF攻击原理比较简单,假设Web A为存在CSRF漏洞的网站,Web B为攻 击者构建的恶意网站,User C h Web A网站的合法用户。
1 .用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2 .在用户信息通过给证后,网站A产生Cookie信息并返回给浏览器,此时用 户登录网站A成功,可以正常发送请求到网站A;
3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4 .网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访 问第三方站点A;
5.浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的 情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是 由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致 来自网站B的恶意代码被执行。


防范:
1. 检查Referer字段
HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在 处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域 名下。以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的 网页地址,应该也位于http://www.examplebank.com之下。而如果是CSRF攻击传 来的请求,Referer字段会是包含恶意网址的地址,不会位于 WWW.examplebank. com之下,这时候服务器就能识别出恶意的访问。
2 .添加校验token
由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求 在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击 者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。这种数据 通常是表单中的一个数据项。服务器将其生成并附加在表单中,其内容是一个 伪乱数。当客户端通过表単提交请求时,这个伪乱数也一并提交上去以供校燈。 正常的访问时,客户端浏览器能够正确得到并传回这个伪乱数,而通过CSRF 传来的欺骗性攻击中,攻击者无从事先得知这个伪乱数的值,服务器端就会因 为校验token的值为空或者错误,拒绝这个可疑请求。



232. linux中软连接和硬链接的区别.


原理上,硬链接和源文件的inode节点号相同,两者互为硬链接。软连接和源文件的 inode节点号不同,进而指向的block也不同,软连接block中存放了源文件的路径名 实际上,硬链接和源文件是同一份文件,而软连接是独立的文件,类似于快捷方式,存 備看源文件的位置信息便于指冋。
使用限制上,不能对目录创建硬链接,不能对不同文件系统创建硬链接,不能对不存在 的文件创建硬链接;可以对目录创建软连接,可以跨文件系统创建软连接,可以 对不存在的文件创建软连接。



233. STL内存分配方式


在STL中考虑到小型区块所可能造成的内存碎片问题,SGI STL设计了双层级配置器, 第一级配置器直接使用malloc()和free:);第二级配置器则视情况采用不同的策略:当 配置区块超过128byteS时,则视之为足够大,便调用第一级配置器;当配置区块小于 128byteS时,则视之为过小,为了降低额外负担,便采用复杂的内存池的方式来整理, 而不再求助于第一级配置器。
每次配置器需要向系统要内存的时候,者所是按客户需求向系统申请的,而是一次性向 系统要了比需求更多的内存,放在内存池里,有一个free.start和free.end指示剩 余的空间(也就是说内存池剩余的空间都是连续的,因此每次重新向system heap要空 间的时候,都会把原先内存池里没用完的空间分配给合适的free list。)当heeTist 中没有可用区块了的时候,会首先从内存池里要内存,同样,也不是以按客户需求要多 少块的,而是一次可能会要上2。块,如果内存池内空间允许的话,可能会得到2。个 特定大小的内存,如果内存池给不了那么多,那么就只好尽力给出;如果连一个都给不 出,那么就要开始向系统即system heap要空间了。换算的标准是
bytes_to_get= 2*total_bytes+ROUND_UP:heap_size>>4)。这个时候使用的是 malloc, 如果没成功,就尝试着从大块一点的freelist那里要一个来还给内存池,如果还是不 彳亍,那么会调用第一级空间配置器的malloc: : allocate,看看out-of-memory机制能 做点什么。
假设我们向系统要x大小的内存,
(1) x大于128byte,用第一级配置器直接向系统malloc,至于不成功的处理,过程 仿照new,通过set_new_handler来处理,直到成功返回相应大小的内存或者是抛出异 常或者是干脆结束运行;
(2) x小于128byte,用第二级配置器向内存池相应的free_liSt要内存,如果该 freelist 面没有空闲块了,
(2.1) 从内存池里面要内存,缺省是获得20个节点,如果内存池中剩余的空间不能完 全满足需求量,但足够供应一个(含)以上的区块,则应尽力满足需求。
(2.2) 如果一个都不能够滴足的话,则从系统的heap里面要内存给到内存池,换算 的标准是 bytes_to_get= 2*total_bytes-ROUND_UP (heap_size»4),申请的大小为需求 量的两倍加上一个祐加值,如果鬲存池中还有莉余的内荐,则将残余零头分配给适当 的free list,这时使用的是系统的malloc,如果要不到:
(2. 3)从比较大的freelist那里要内有到内存池,如果还是要不到:
(2.4)从系统的heap里面要内存给到内存池,换算标准跟2. 2 -样,但是这时候使 用的是第一级配置器的allocate,主要是看看能不能通过out_of.memory机制得到一 点内存。所以,freelist总是从内存池里要内存的,而内存池可能从freelist也可能 从系统heap那里要内存。
SGI SIL的alloc的主要开销就在于管理这些小内存,管理这些小内存的主要开销就在 于,每次freelist ±的内存块用完了,需要重新要空间,然后建立起这个来。 freelist!的内存,会一直保留着直到廷序退出才还给系统。但这不会产生内存泄漏, —来是管理的都是小内存,二来是,占用的内存只会是整个程序运行过程中小内存占 用量最大的那一刻所占用的内存。



234. TCP的拥塞控制机制是什么?请简单说说


我们知道“P通过一个定时器(timer)采样了 RTT并计算皿,但是,如果网络上的 延时突然増加,那么,对这个事做出豹应对只有重传数据,然而重传会导致网络的 负担更重,于是会导致更大的延迟以及更多的丢包,这就导致了恶性循环,最终形成“网 络风暴” 一一 TCP的拥塞控制机制就是用于应对这种情况。
首先需要了解一个概念,为了在发送端调节所要发送的数据量,定义了一个“拥塞窗口” (Congestion Window),在发送数据时,将拥塞窗口的大小与接收端ack的窗口大小 做比较,取较小者作为发送数据量的上限。
拥塞控制主要是四个算法:
1. 慢启动:意思是刚刚加入网络的连接,一点一点地提速,不要一上来就把路占满。 连接建好的幵始先初始化cwnd = 1,表明可以传一个MSS大小的数据。
每当收到一个ACK, cwnd卄;呈线性上升
每当过了一个RTT, cwnd = cwnd*2;呈指数让升
阈值 ssthresh ( slow start threshold),是一个上限,当 cwnd >= ssthresh 时,就 会进入“拥塞避免算法〃
2. 拥塞避免:当拥塞窗口 cwnd达到一个阚值时,窗口大小不再呈指数上升,而是以线 性上升,避免増长过快导致网络拥塞。
每当收到一个 ACK, cwnd = cwnd + 1/cwnd
每当过了—个 RTF, cwnd = cwnd + 1
拥塞发生:当发生丢包进行数据包重传旺,表示网络已经拥塞。分两种情况进行处理: 等到对超时,重传数据包
sshthresh = cwnd /2
cwnd重置为1
3. 进入慢启动过程
在收到3个duplicate ACK时就开启重传,而不用等到RTO超时
sshthresh = cwnd = cwnd /2
进入快速'恢复算法一一Fast Recovery
4. 快速恢复:至少收到了 3个Duplicated Acks,说明网络也不那么糟糕,可以快速恢 复。
cwnd = sshthresh + 3 * MSS ( 3的意思是确认有3个数据包被收到了)
重传Duplicated ACKs指定的数据包
如果再收到 duplicated Acks,那么 cwnd = cwnd +1
如果收到了新的Ack,那么,cwnd = sshthresh ,然后就进入了拥塞避免的算法了。



235. Mysql连接语句如何实现快速查找?


1. like语句(和数据库做模糊匹配)''蠕可用于定义通配符
SELECT * FKOM Persons WHERE City LIKE ' 1
2. 顶语句(表示某个字段是否在集合中)
SELECT * FKOM Persons WHERE Las顼ame IN (' Adams'Carter')
3. BETWEEN ••• AND会选取介于两个值之间的数据范围。这些值可以是数值、文本或者 日期。
SELECT * FKOM Persons WHERE Las顼ame BETWEEN ' Adams' AND ’Carter'
4. Alias (给返回的字段或者查询的表设置别名)
SELECT po. Order ID, p. LastName, p. FirstName FKOM Persons AS p, Product_Orders AS po WHERE p. LastNajne=‘Adams' AND p. FirstNajne=,JohnJ
5. join(匹配两个表数据)
SELECT Persons. LastName, Persons. FirstName, Orders. OrderNo FKOM Persons
INNER JOIN Orders ON Persons.Zd_P = Orders. Id_P
常用几种连接语句的区别
JOIN:如果表中有至少一个匹配,则返回行
LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
FULL JOIN:只要其中一个表中存在匹配,就返回行
UNION (注意跟join是不同的)(UNION環作符用于合并两个或多个SELECT语句的结 果集。)
(默认地,UNION操作符选职不同的值。如果允许重复的值,请使用UNION ALL。) 注意:请注意,UNION内部的SELECT语句必须拥有相同数量的列。列也必须拥有相似 的数据类型。同时,每条SELECT语句中的列的顺序必须相同。
SELECT column_najne (s) FKOM table_namel
UNION
SELECT column_najne (s) FKOM table_name2
SELECT INTO (语句从一个表中选取数据,然后把数据插入另一个表中。)
SELECT * INTO Persons_backup FROM Persons
SELECT * INTO Persons IN 5 Backup, mdb' FKOM Persons



236. 输入一个矩阵,按照从外向里以顺时针的顺序依次打印岀 每一个数字,例如,如果输入如下4X4矩阵:1234567
8 9 10 11 12 13 14 15 16则依次打印岀数字
1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

public static ArrayList<Integer> printMatrix(int [][] matrix) {
if (matrix, length == 0){
return null;
ArrayList<Integer> list = new AirayListO ();
//四个边界值
int top = 0;
int left = 0;
int right = matrix[0]. length;
int bottom = matrix, length;
//横着和竖着相加的值(1/-1)
int colFlage = 1;
int rowFlage = 1;
//现在是否是横着移动
boolean flag = true;
int i = 0;
int j = 0;
while (bottom \= top && right J= left) {
//横向移动
if (flag) {
for (; j < right && j >= left; j += rowFlage) { list, add (matrix [i] [j]);
}
//缩小墙壁
if (rowFlage > 0) {
top++;
} else {
bottom"-;
}
//指针恢复
i += rowFlage;
//切换下次横向移动方向
rowFlage = -rowFlage;
j += rowFlage;
广
//纵向移动
if (Jflag){
for (; i < bottom && i >= top; i += colFlage) { list, add (matrix [i] [j]);
}
//缩小墙壁
if (colFlage > 0) {
right—;
} else {
left++;
}
//切换下次纵向移动方向
colFlage = "colFlage;
//恢复指针位置
i += colFlage;

j += colFlage;
}
//切换横向和竖向移动 flag = Jflag;
}
return list;
}

237. 利用快速排序对单链表进行排序

#include^iostream>
#include< c t ime〉
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next (DULL) {}
};
class Solution {
public:
ListNode *sortList(ListNode *head) {
if (head == NULL)
return head;
ListNode* tail=head;
ListNode* end = tail;
while (tail->next != NULL)[
//if (tail->next~>next == NULL)
// end = tail;
tail = tail~>next;
}
qSortList (head, tail);
return head;
}
ListNode* Partition(ListNode* head, ListNode* tail) [
ListNode* newHead = new Lis顼ode(0); newHead->next = head;
ListNode* ipt = newHead, *jpt = head;
int ar = tail->val;
while (jpt != tail)
[
if (jpt~>val < ar)
[
ipt = ipt_>next;
swap (ipt->val, jpt-/val);
}
jpt = jpt_>next;
}
ipt = ipt_>next;
swap(ipt->val, tail->val);
return ipt;
}
void qSortList(ListNode*head, ListNode*tail)
[
if (head == NULL)
return;
if (tail == NULL)
return;
if (tail->next J =NULL && tai»next == head)
return;
else if (tail->nex11=NULL &&
tail->nex>next J =NULL &&
tail->nex >nex t== head)
return;
if (head == tail)
return;
if (head~>next == tail)
{
if (head~>val > tail~>val)
swap(head->vaL tail->val);
return;
}
ListNode* mid = Partition (head, tail);
ListNode*taill = head;
if (taillJ=mid)
while (taill->next != mid) taill = taill->next;
ListNode*head2 = mid~>next;
qSortList (head, taill);
qSortList(head2, tail);
int main。
[
ListNode* headO = new ListNode(200);
ListNode* head = headO;
srand(time(NULL));
for (int i = 0; i < 10000; i++)
[
ListNode* mNode = new ListNode(rand() % 4); headO->next = mNode;
headO = headO~>next;
I
Solution sin;
ListNode*res=sln. sortList(head). while (res->next \= NULL)
[
cout « res->val « " ”;
res = res->next;
}
return 0;

238. Mysql如何实现快速查找?

々include〈iostrearn〉
9 include<ctime>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next (DULL) {} };
class Solution {
public:
ListNode *sortList(ListNode *head) {
if (head == NULL)
return head;
ListNode* tail=head;
ListNode* end = tail; while (tail->next != NULL)[
//if (tail->next->next == NULL)
// end = tail; tail = tail~>next;
I
qSortList (head, tail);
return head;
}
ListNode* Partition(ListNode* head, ListNode* tail)
[
ListNode* newHead = new Lis顼ode(0); newHead->next = head;
ListNode* ipt = newHead, *jpt = head;
int ar =
while (jpt != tail)
[
if (jpt~>val < ar)
[
ipt = ipL>next;
swap ;
}
jpt = jpt_>next;
}
ipt = ipt_>next;
swap (ipt->val, tail->val);
return ipt;
}
void qSortList(ListNode*head, ListNode*tail)
{
if (head == NULL)
return;
if (tail == NULL)
return;
if (tail->next! =NULL && tai»next == head) return;
else if (tail->nextJ=NULL &&tai1->next~>next!=NULL
&& tail->nex t~>nex-== head)
return;
if (head == tail)
return;
if (head->next == tail)
[
if (head~>val > tail~>val) swap(head->vaL tail->val);
return;
}
ListNode* mid = Partition (head, tail);
ListNode*taill = head;
if (taillJ=mid)
while (taill->next \= mid) taill = taill->next;
ListNode*head2 = mid->next;
qSortList (head, taill);
qSortList(head2, tail);
}
};
int main()
[
ListNode* headO = new ListNode(200);
ListNode* head = headO;
srand(time(NULL));
for (int i = 0; i < 10000; i++)
[
ListNode* mNode = new ListNode(rand() % 4); head0->next = mNode;
headO = headO~>next;
}
Solution sin;
ListNode*res=sln. sortList(head).
while (res->next != NULL)
{
cout « res->val « " ”;
res = res-^next;
return 0;

239. 将一个链表分为奇偶两个链表

#include<iostreajn>
# include <mal1o c. h>
using namespace std;
typedef struct node{
int data;
node *next;
}LNode;
LNode *L;
LNode* create (LNode *L, int axr [], mt n) {
L = (LNode*)malloc(sizeof(LNode:);
L~>next = NULL;
LNode *p = L;
for(int i = 0; i < n; ++i){
LNode *newNode = (LNode*)malloc(sizeof(LNode)); newNode~>next = NULL;
newNode-^data = axr [i];
//头插法
p->next = newNode;
p - p->next;
return L;
}
 //分离的方法
void spilt(LNode *L, LNode *B){
LNode *p = L;
B = (LNode*)malloc(sizeof(LNode:);
B~>next = NULL;
LNode *q = B;
LNode *r;
while(p->next != NULL){
if(p->next->data % 2 == 0){
”T面处理的是取下链表中的偶数节点插入到链表中
//偶数的时候取出来插入到链表B中
r - p->next;
p->next - p->next->next.
//q指针向下移动
q->next - r;
r->next = NULL;
q - q->next;
}else{
p = p_>next;
}
I
"输出链表A的内容
LNode *s = L;
while(s->next != NULL){
printf (”%d ”, s->next->data;;
s = s_>next;
}
cout << endl;
"输出链表B的内容
s = B;
while(s->next != NULL){
printf (”%d ”, s->next->data;;
s = s->next;
int main(void){
int axr[12] = {12, 43, 2, 10, 100, 89, 11, 56, 45, 34, 77, 11};
LNode *head = create (L, arr, 12;;
LNode *B;
spilt (head, B);
return 0;
}

240. 有序数组二分查找,返回查找元素最后一次岀现的位置, 若不存在则返回-1

#include^iostream>
using namespace std;
void last (int [] a, int key) {
int min=0, max=a. length"l;
int mid=0;
while(min<=max){
mid=(max+min+1)/2;
if (key>=a[mid]) {
min=mid;
lelse {
max=mid"l;
if (max==min) {
break;
}
}
if (a[max]J=key) {//当查找元素不存在时'返回-1 return ~1;
lelse {
return max;
int main(void){
int[] a= {3, 5,10,10,10,13,13,19, 23};
last (a, 10);
return 0;
}

秋招大厂经典面试题及答案整理不断更新中,感兴趣且正在学习的同学可以点个关注;狮会不断更新文章连载,有问题或者见解可以评论区讨论。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值