后台开发面经-北森
1、八大数据结构及分类
- 1、数组 频繁查询,对存储空间要求不大,很少增加和删除的情况
- 2、栈 栈常应用于实现递归功能方面的场景,例如斐波那契数列
- 3、队列 因为队列先进先出的特点,在多线程阻塞队列管理中非常适用
- 4、链表 数据量较小,需要频繁增加,删除操作的场景
- 5、树 二叉树既有链表的好处,也有数组的好处,是两者的优化方案,在处理大批量的动态数据方面非常有用。
- 6、散列表 哈希冲突的问题,如果处理的不好会浪费大量的时间,导致应用崩溃
- 7、堆 因为堆有序的特点,一般用来做数组中的排序,称为堆排序
- 8、图
https://blog.csdn.net/yeyazhishang/article/details/82353846
因为哈希表是基于数组衍生的数据结构,在添加删除元素方面是比较慢的,所以很多时候需要用到一种数组链表来做,也就是拉链法。拉链法是数组结合链表的一种结构,较早前的hashMap底层的存储就是采用这种结构,直到jdk1.8之后才换成了数组加红黑树的结构。
图是一种比较复杂的数据结构,在存储数据上有着比较复杂和高效的算法,分别有邻接矩阵 、邻接表、十字链表、邻接多重表、边集数组等存储结构
2、堆栈特点及应用
栈:特点就是一个先进后出的结构。
队列:特点就是一个先进先出的结构。
栈的应用:非常广泛,在CPU内部就有提供栈这个机制。主要用途:函数调用和返回,数字转字符,表达式求值,走迷宫等等。在CPU内部栈主要是用来进行子程序调用和返回,中断时数据保存和返回。在编程语言中:主要用来进行函数的调用和返回。可以说在计算机中,只要数据的保存满足先进后出的原理,都优先考虑使用栈,所以栈是计算机中不可缺的机制。
队列的应用:队列主要用在和时间有关的地方,特别是操作系统中,队列是实现多任务的重要机制。windows中的消息机制就是通过队列来实现的。进程调度也是使用队列来实现,所以队列也是一个重要的机制。只要满足数据的先进先出原理就可以使用队列。
3、链表和数组的区别
不同:
- 链表是链式的存储结构;数组是顺序的存储结构。(物理地址存储的连续性)
- 链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。(存储元素不同,前者指针+值,后者值)
- 链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难;
- 数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
相同:两种结构均可实现数据的顺序存储,构造出来的模型呈线性结构。
区别一:物理地址存储的连续性
数组的元素在内存中是连续存放的。
链表的元素在内存中不一定是连续存放的,通常是不连续的。
区别二:访问速度
数组的访问速度很快,因为数组可以根据数组可以根据下标进行快速定位。
链表的访问速度较慢,因为链表访问元素需要移动指针。
区别三:添加、删减元素速度
数组的元素增删速度较慢,因为需要移动大量的元素。
链表的元素增删速度较快,因为只需要修改指针即可。
4、介绍下树,前缀树的应用场景,可以怎样优化
前缀树也叫字典树,常用语字符串的查找。
字典树==前缀树==Trie树:
查询某个单词(前缀)在所有单词中出现次数的一种数据结构
查询和插入时间复杂度都是O(n),是一种以空间换时间的方法。
详细介绍参考于: https://www.cnblogs.com/vincent1997/p/11237389.html (需要详细看)
https://blog.csdn.net/aiphis/article/details/48247469
应用:
前缀树有着广泛的应用,例如自动补全,拼写检查、搜索单词、搜索前缀等等
5、并发编程,说一下理解,线程池的优缺点(看详细内容)
1. 什么是线程池?
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
通俗点讲,线程池就是一个容器,集中管理线程。线程使用完毕不会销毁,而是会先存储在线程池内。
2. 提交一个任务到线程池中,线程池的处理流程如下:
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
https://blog.csdn.net/jiao1902676909/article/details/88980110
3. 线程池的优缺点
1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。
2、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。
4、线程处理流程:
创建的线程池具体配置为:核心线程数量为5个;全部线程数量为10个;工作队列的长度为5
刚开始都是在创建新的线程,达到核心线程数量5个后,新的任务进来后不再创建新的线程,而是将任务加入工作队列,任务队列到达上限5个后,新的任务又会创建新的普通线程,直到达到线程池最大的线程数量10个,后面的任务则根据配置的饱和策略来处理。我们这里没有具体配置,使用的是默认的配置AbortPolicy:直接抛出异常。
一个应用场景
linux高并发的实现,线程池的实现思想,怎样处理高并发就比如说,用迅雷看电影。一边下载,一边播放。这个时候下载进程和播放进程,他们两个就有同步的机制,例如:只能播放视频文件中已经下载完成的部分,没有下载的不能播放。
https://blog.csdn.net/chenkaifang/article/details/81428799
6、并发和并行的区别
并发是轮流处理多个任务,并行是同时处理多个任务
https://www.jianshu.com/p/cbf9588b2afb
7、递归需要考虑一些什么
①一个函数在他的函数体内调用他自身称为递归调用,执行递归函数将反复调用其自身,每执行一次进入新的一层。
②为防止递归函数无休止的进行,必须在函数内有终止条件。
③对于一个函数只要知道他的递归定义式和边界条件,就可以编递归函数。
层数不能太多,在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
充分必要条件是:问题具有某种可借用的类同自身的子问题描述的性质;某一有限步的子问题(也称本原问题)有直接的解存在。
8、动态规划与递归
递归算法就是通过解决同一问题的一个或多个更小的实例来最终解决一个大问题的算法。为了在C语言中实现递归算法,常常使用递归函数,也就是说能调用自身的函数。递归程序的基本特征:它调用自身(参数的值更小),具有终止条件,可以直接计算其结果。
在使用递归程序时,我们需要考虑编程环境必须能够保持一个其大小与递归深度成正比例的下推栈。对于大型问题,这个栈需要的空间可能妨碍我们使用递归的方法。
一个递归模型为分治法,最本质的特征就是:把一个问题分解成独立的子问题。如果子问题并不独立,问题就会复杂的多,主要原因是即使是这种最简单算法的直接递归实现,也可能需要难以想象的时间,使用动态规划技术就可以避免这个缺陷。
可以按照从最小开始的顺序计算所有函数值来求任何类似函数的值,在每一步使用先前已经计算出的值来计算当前值,我们称这项技术为自底向上的动态规划。只要有存储已经计算出的值的空间,就能把这项技术应用到任何递归计算中,就能把算法从指数级运行时间向线性运行时间改进。
性质:动态规划降低了递归函数的运行时间,也就是减少了计算所有小于或等于给定参数的递归调用所要求的时间,其中处理一次递归调用的时间为常量。
具体详见于:https://blog.csdn.net/DeepIT/article/details/6530282
在自顶向下的动态规划中,我们存储已知的值;在自底向上的动态规划中,我们预先计算这些值。我们常常选择自顶向下的动态规划而不选自底向上动态规划,其原因如下:
1 自顶向下的动态规划是一个自然的求解问题的机械转化。
2 计算子问题的顺序能自己处理。
3 我们可能不需要计算所有子问题的解。
我们不能忽视至关重要的一点是,当我们需要的可能的函数值的数目太大以至于不能存储(自顶向下)或预先计算(自底向上)所有值时,动态规划就会变得低效。自顶向下动态规划确实是开发高效的递归算法实现的基本技术,这类算法应纳入任何从事算法设计与实现所需的工具箱。
相关编程题目整理
https://www.cnblogs.com/threetop/p/9075349.html
https://blog.csdn.net/qq_35556064/article/details/82503076
9、数据库的连表查询和嵌套查询,分页查询及子查询
连表查询
from 多个表,使用where筛选条件
分页查询
select * from student limit 2,3
limit 2,3代表从第2条(不包含第2条) 数据开始查询出3条记录
嵌套查询
将一个查询块嵌套在另一个查询块的 WHERE 子句或 HAVING 短语的条件中的查询称为 嵌套查询。
https://www.cnblogs.com/OctoptusLian/p/8183241.html
10、数据库增删查改语句重复应该考虑什么
https://www.cnblogs.com/hoi123/p/6206646.html
11、存储引擎,InnoDB
12、如何实现浏览器和服务器之间的通信
13、单例模式,实现方法,应用场景
https://www.runoob.com/design-pattern/singleton-pattern.html
https://www.jianshu.com/p/3bfd916f2bb2
14、生产者和消费者模式应用场景
https://blog.csdn.net/u011109589/article/details/80519863
15、算法题:156个元素数组,二分查找最少要多少次比较,最多多少次
log2(n)取整后 +1
16、进程和线程的区别,协程,同步机制
协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。
一个程序可以包含多个协程,可以对比与一个进程包含多个线程,
因而下面我们来比较协程和线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。
协程和线程区别:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
https://www.cnblogs.com/guolei2570/p/8810536.html
https://blog.csdn.net/jason_cuijiahui/article/details/82587076
17、数据库索引,存储引擎
https://www.cnblogs.com/liujunyue/p/11323290.html
https://www.cnblogs.com/Jtianlin/p/8954841.html
18、项目难点
19、生成订单号的实现
20、Redis 的数据结构
21、最近在看什么书,这么多看得完吗
22、多线程同步的方法
23、wait 和 notify 以及 notifyAll
24、HashMap 查找效率最低和最高
25、TCP 中 time-wait 状态
26、CPU 占用率达到 100% 可能由什么造成
1、病毒木马造成。 2、打开的网页过多,可造成短时间CPU占用率高。 3、看视频时由于硬盘读写慢、网速慢,也可引起CPU占用率高。 4、玩大型游戏时,也可引起CPU占用率高。 5、有时自启动项过多(其中包括与系统不兼容的程序),引起响应慢引起CPU占用率高。
27、服务调用超时可能由什么造成
28、数据库中,怎样理解这个非关系和关系这个概念的
29、redis相关
30、几种设计模式
31、线程通信
32、mysql存储引擎
33、什么是索引、怎么设置索引
34、什么样的时候做索引,举例说明
35、什么样的不适合做索引,举例说明
36、mysql底层原理说一下
37、数据库序列化
38、内存分区(5个)
39、基础
- 进程和线程
- 进程通信
- 线程通信
- 锁,乐观锁悲观锁
- 为啥要锁,有什么注意事项吗
- 细问了锁和同步的相关知识,比较碎
- 贪吃蛇游戏的设计思路
40、最长公共前缀
41、测试岗
- 项目介绍
- 白盒测试方法
- 黑盒的等价类,因果图
- 数据库查找某个字符
- HTTP请求头
- GET/POST
- 状态码304/403
- 测试微信朋友圈发文字的功能
- 口述找出字符串第一个不重复字符的代码
42、常用状态码
43、基础
怎么判断一个元素是不是数组?
44、霍夫曼树
霍夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树
https://blog.csdn.net/qq_29519041/article/details/81428934
https://www.jianshu.com/p/d632b7e8f003
http://c.biancheng.net/view/3398.html
45、信号量与互斥量的区别
https://www.cnblogs.com/gczr/p/8376087.html
https://blog.csdn.net/qq_34793133/article/details/80087727
46、HTTP 304状态码的详细讲解
https://blog.csdn.net/huwei2003/article/details/70139062
47、数组与指针的区别
48、new分配空间,释放后空间是直接就可以重复使用的么?
49、对单项链表进行排序
50、内核发送缓冲区,UDP有发送缓冲区么?
51、线程池的优势,怎么使用
52、内存池的优势,使用
53、当使用线程池时,程序中怎么解决死锁
54、线程池使用场景,什么时候不适用,线程池管理线程也会有开销
55、写代码中遇到什么问题
56、socket网络编程
57、阻塞编程与非阻塞编程
58、操作系统的中断、系统概念、内核
59、线程的栈空间为8M,进程的空间是多大?
(1)进程栈大小时执行时确定的,与编译链接无关
(2)进程栈大小是随机确认的,至少比线程栈要大,但不会超过2倍
(3)线程栈是固定大小的,可以使用ulimit -a 查看,使用ulimit -s 修改。一般默认是8M。
(4)一般默认情况下,线程栈是在进程的堆中分配栈空间,每个线程拥有独立的栈空间,为了避免线程之间的栈空间踩踏,线程栈之间还会有以小块guardsize用来隔离保护各自的栈空间,一旦另一个线程踏入到这个隔离区,就会引发段错误。
https://blog.csdn.net/elfprincexu/article/details/78779158
60、c++中函数模板与函数的效率
函数模板不能进行隐式类型转换
如果出现函数重载,优先调用普通函数
如果想强制使用函数模板,则可以使用空参数列表
函数模板也可以发生重载
如果函数模板可以产生更好的匹配,那么优先使用函数模板
https://blog.csdn.net/XUCHEN1230/article/details/86370292
编译器会对函数模板进行两次编译:
1,对模板代码本身进行编译;
1,检查函数模板本身有无语法错误等;
2,对参数替换后的代码进行编译;
1,调用函数模板的时候,编译器要根据实际的参数类型得到真正的函数,这个时候编译器会对这个函数进行第二次编译;
61、C++中lambda表达式与传统的函数相比有什么优势
匿名函数
比如你代码里有一些小函数,而这些函数一般只被调用一次(比如函数指针),这时你就可以用lambda表达式替代他们,这样代码看起来更简洁些,用起来也方便
62、缓冲区之间的关系,内核缓冲区,应用缓冲区
63、