该博主看起来挺厉害的百度携程滴滴头条360的offer都有,
我马上也要人生第一次前端实习面试了是美团的所以刷题看看人家的面经~
链接:https://m.sohu.com/a/236608417_690895
这个各方面都有吧能找到答案的我一般都做了传送门
文章目录
一、算法部分
如果你算法不好就可以直接告诉面试官。不需要过多作假,面试官其实也很聪明的,如果你作弊很快就能发现的。
1、基本排序的方式
冒泡、快排、桶排、堆排序、归并排序、插入排序。
(1)冒泡:排序思路:遍历数组,每次遍历就将最大(或最小)值推至最前。越往后遍历查询次数越少, 跟插入排序刚好相反。
(2)快排:选择一个元素作为基数(通常是第一个元素),把比基数小的元素放到它左边,比基数大的元素放到它右边(相当于二分),再不断递归基数左右两边的序列。
(3)桶排:取 n 个桶,根据数组的最大值和最小值确认每个桶存放的数的区间,将数组元素插入到相应的桶里,最后再合并各个桶。(n个容量至少为1的桶,从大到小,每次的数字放到对应桶的排序好的位置)
(4)堆排序:因为js模拟二叉树比较麻烦,所以堆排序的优势用js语言无法体现, 相对而言C语言的链表在实现上更能表现堆排序,堆排序或许更适合指针类的计算机语言。
形成大顶堆,然后顶和末尾叶节点交换,堆容量递减。并且每次构建完上边的堆要回去看一下其子堆有没有受到影响,(太麻烦了不写了,我解读一个别人的吧)
(5)归并排序:递归将数组分为两个序列,有序合并这两个序列(递归向下)
写的时候一直死递归,超出堆栈(但是我明明谢了结束条件了啊,发现有一个地方逗号写成了.然后块级作用域变量left、right忘了加let导致污染,真是难顶)
(6)插入排序:以第一个元素作为有序数组,其后的元素通过在这个已有序的数组中找到合适的位置并插入。
var arr=[1,3,6,9,2,7,5,4,8,3];//十位数
console.log(arr);
function swap(a,i,j){
let t=a[i];
a[i]=a[j];
a[j]=t;
}
/** */
// //1.冒泡 稳定 n方
// function PopSort(arr){
// let len=arr.length;
// for(let i=0;i<len;i++)
// for(let j=0;j<len-i;j++)
// if(arr[j]>arr[j+1])swap(arr,j,j+1);//如果比右边的大就交换
// }
// PopSort(arr);
// //2.快排 不稳定 nlogn
// function QuickSort(arr){
// let len=arr.length;
// if(len<=1)return arr;//结束条件,如果arr长度为1或0
// let left=[];
// let right=[];
// let p=arr[0];
// for(let i=1;i<len;i++){//比基小放左边,反之放右边
// if(arr[i]<=p)left.push(arr[i]);
// else right.push(arr[i]);
// }
// //返回的是左边+基+右边
// return QuickSort(left).concat(p,QuickSort(right));
// }
// arr=QuickSort(arr);
// //3.桶排序 稳定 n+k
// function buckerSort(arr,n){
// let len=arr.length;
// let max=Math.max(...arr);
// let min=Math.min(...arr);
// let range=Math.ceil((max-min)/n)||1;//取到桶容量大小
// let ranges=[];
// let bucker=new Array(n).fill(0);//保证元素不为空才能用map
// bucker=bucker.map((b)=>{
// return [];
// })//写成二维数组
// for(let i=1;i<=n;i++){
// ranges.push(min+range*i);//得到各个桶的范围(最大值)
// }
// arr.map((a)=>{
// for(let i=0;i<n;i++){//便利n个桶
//
// if (a<=ranges[i])
// {
// let f=0;
// var l=bucker[i].length||0;
// for(let j=0;j<l;j++)
// if(bucker[i][j]>a){bucker[i].splice(j,0,a);f=1;break;}//插到特定位置,并跳出
//
// if(f==0){bucker[i].push(a);}//如果原来是空直接push
// break;//如果能插入桶就跳出
// }
// }
// })
// let s=[];
// bucker=bucker.map(b=>{
// s.push(...b);
// })
// return s;
// }
// arr=buckerSort(arr,3);
// //4.堆排序 不稳定 nlogn
// function heapSort(){
// function maxHeapify(array, index, heapSize) {//调整形成大顶堆
// var iMax, iLeft, iRight;
// while (true) {
// iMax = index;
// iLeft = 2 * index + 1;//左孩子
// iRight = 2 * (index + 1);//右孩子
// if (iLeft < heapSize && array[index] < array[iLeft]) {
// iMax = iLeft;
// }
// if (iRight < heapSize && array[iMax] < array[iRight]) {
// iMax = iRight;
// }
// //检查该三节点小堆是否符合大顶
// if (iMax != index) {//如果不符合,能交换,但是要向下审查
// swap(array, iMax, index);
// index = iMax;//把基准换成被交换到子节点的节点
// } else {//如果没发生交换,说明下面更没有可能变动
// break;
// }
// }
// }
// function buildMaxHeap(array) {//堆构建
// var i, iParent = Math.floor(array.length / 2) - 1;//得到最后一个非叶节点
// for (i = iParent; i >= 0; i--) {//从非叶节点开始往前做堆调整,以此节点i为基准(即小堆父)
// maxHeapify(array, i, array.length);
// }
// }
// function sort(array){//起点
// buildMaxHeap(array);//先构筑一个大顶堆
// for (var i = array.length - 1; i > 0; i--) {//从最后一个节点开始向前做大顶堆与叶节点的交换
// swap(array, 0, i);//交换堆顶与堆尾
// maxHeapify(array, 0, i);//重调大顶,不包括末尾节点
// }
// //循环进行完之后,从尾部向头部变成当前堆最大的值
// return array;
// }
// return sort(arr);
// }
// arr=heapSort(arr)
// //5.归并排序 稳定 nlogn
// function mergeSort(arr){
// function sort(array){
// let len=array.length;
// if(len<=1)return array;
// let mid=Math.floor(len/2);
// let left=array.slice(0,mid);
// let right=array.slice(mid,len);
// return merge(sort(left),sort(right));
// }
// function merge(left,right){
// let s=[];
// while(left.length>0&&right.length>0){
// if(left[0]<right[0]){
// s.push(left.shift());
// }
// else{
// s.push(right.shift());
// }
// }
// return s.concat(left,right);
// }
// return sort(arr);
// }
// arr=mergeSort(arr);
// //6.插入排序 稳定 n方
// function insertSort(arr){
// for(let i=0;i<arr.length;i++){
// for(let j=i;j<arr.length;j++)
// if(arr[i]>arr[j]){
// let t=arr[j];
// arr.splice(j,1);//取出这个数
// arr.splice(i,0,t);//插到比他大的那个数前面
// }
// }
// }
// insertSort(arr);
/** */
console.log(arr);
上边六种都通过了;冒泡和插入比较简单并且都稳定,但是复杂度较高。快排是在方便实现的里面稳定性和复杂度都比较好的了
2、二分查找。
//首先保证查找序列有序唯一,用快排排个序
let arr=[808,6,9,51,33,54,4];
function QuickSort(arr){
let len=arr.length;
if(len<=1)return arr;//结束条件,如果arr长度为1或0
let left=[];
let right=[];
let p=arr[0];
for(let i=1;i<len;i++){
//比基小放左边,反之放右边
if(arr[i]<=p)left.push(arr[i]);
else right.push(arr[i]);
}
//返回的是左边+基+右边
return QuickSort(left).concat(p,QuickSort(right));
}
arr=QuickSort(arr);
let k=4;//查个4,保证肯定在里面
let key=binarySearch(arr,k,0,arr.length);
console.log(`查${
k}在第${
key+1}个\n`,arr);
/** */
function binarySearch(arr,k,low,high){
if(arr.length<1)return;
let mid=Math.floor((low+high)/2);
if(arr[mid]==k)return mid;
else if(arr[mid]>k){
return binarySearch(arr,k,low,mid-1);
}
else{
return binarySearch(arr,k,mid+1,high);
}
}
3.二叉树便利(前序中序后序)
https://blog.csdn.net/zsy_snake/article/details/80353336
4.尽量刷牛客和剑指offer
二、计算机网络部分
1.最重要的就是http协议。毕竟前端大多都和http协议搞事情。
http协议大多包含了以下几种问题吧
-
http请求报文解构都包含什么
请求行(请求方法+url+http协议版本号)
GET:获取资源
客户端—-GET(访问某个资源)—–>服务器POST:发给服务器的参数全部放在请求体中
客户端—-POST(上传某个信息)—–>服务器PUT:传输文件(PUT自身不带验证机制,任何人都可以传,不安全)
客户端—-PUT(上传一个文件)—–>服务器HEAD:获得报文首部(不返回主体部分,只返回响应的首部)
客户端—-HEAD(把那个相关信息告诉我)—–>服务器DELETE:删除文件(与PUT相反,不安全)
客户端—-HEAD(把那个文件删除)—–>服务器OPTIONS:询问支持的方法(查询对请求URI指定资源支持的方法)
客户端—-OPTIONS(这个资源支持哪些方法访问)—–>服务器请求头(存放请求信息,缓存)
Host: 指定请求的服务器的域名和端口号 --域名端口号
Accept: 指定客户端能够接受的内容类型 – 类型
Accept-Charset: 浏览器可以接受的字符编码集 --编码集
Accept-Encoding: 指定浏览器可以支持的web服务器返回内容压缩编码类型 – 内容压缩编码类型
Accept-Language: 浏览器可接受的语言 – 语言
Accept-Ranges:可以请求网页实体的一个或多个子范围字段 – 子范围字段
AuthorizationHTTP:授权证书
Cache-Control: 指定请求和响应遵循的缓存机制
Connection:表示是否需要持久连接 --连接
CookieHTTP: 请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器 – cookie
Content-Length:请求的内容长度 – 长度正文(数据实体)
-
http协议常用的状态码
参见:https://www.jianshu.com/p/b58025e61b2d
分类 描述 1** 信息。服务器收到请求,请继续执行请求 2** 成功。请求被成功接收并处理 3** 重定向。需要进一步操作来完成请求 4** 客户端错误。无法完成请求,或请求包含语法错误 5** 服务器错误。服务器在处理请求的过程中发成错误 -100继续请求者应当继续提出请求。服务器已收到请求的一部分,正在等待其余部分。
101切换协议请求者已要求服务器切换协议,服务器已确认并准备切换。
-200成功服务器已成功处理了请求。
-201已创建请求成功并且服务器创建了新的资源。
-202已接受服务器已接受请求,但尚未处理。
-203非授权信息服务器已成功处理了请求,但返回的信息可能来自另一来源。
-204无内容服务器成功处理了请求,但没有返回任何内容。
-205重置内容服务器成功处理了请求,内容被重置。
206部分内容服务器成功处理了部分请求。
-300多种选择针对请求,服务器可执行多种操作。
-301永久移动请求的网页已永久移动到新位置,即永久重定向。
-302临时移动请求的网页暂时跳转到其他页面,即暂时重定向。
303查看其他位置如果原来的请求是 POST,重定向目标文档应该通过 GET 提取。
-304未修改,此次请求返回的网页未修改,继续使用上次的资源。
-305使用代理请求者应该使用代理访问该网页。
307临时重定向请求的资源临时从其他位置响应。
-400错误请求,服务器无法解析该请求。
-401未授权请求,没有进行身份验证或验证未通过。
-403禁止访问服务器拒绝此请求。
-404未找到服务器找不到请求的网页。
-405方法禁用服务器禁用了请求中指定的方法。
-406不接受,无法使用请求的内容响应请求的网页。
407需要代理授权请求者需要使用代理授权。
-408请求超时服务器请求超时。
-409冲突,服务器在完成请求时发生冲突。
-410已删除请求的资源已永久删除。
411需要有效长度服务器不接受不含有效内容长度标头字段的请求。
412未满足前提条件服务器未满足请求者在请求中设置的其中一个前提条件。
-413请求实体过大请求实体过大,超出服务器的处理能力。
414请求 URI 过长请求网址过长,服务器无法处理。
415不支持类型请求的格式不受请求页面的支持。
416请求范围不符页面无法提供请求的范围。
417未满足期望值服务器未满足期望请求标头字段的要求。
-500服务器内部错误服务器遇到错误,无法完成请求。
-501未实现服务器不具备完成请求的功能。
-502错误网关服务器作为网关或代理,从上游服务器收到无效响应。-503服务不可用服务器目前无法使用。
-504网关超时服务器作为网关或代理,但是没有及时从上游服务器收到请求。
-505HTTP 版本不支持服务器不支持请求中所用的 HTTP 协议版本。 -
http头部关于缓存的字段(浏览器缓存控制机制有两种:HTML Meta标签 和 HTTP头信息)
强缓存定义: 在缓存未失效时候,浏览器向服务端发起请求,直接从缓存中获取数据,
Expires的值为web服务器返回的到期时间(GMT 格林威治时间),浏览器下次请求时间小于服务器返回的时间则浏览器直接从缓存中获取数据,而不用再次发送请求。
cache-control常见的取值:private, public, no-cache, max-age, no-store
private : 客户端可以缓存
public : 客户端和代理服务器都可缓存
max-age= : 缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)
no-cache : 并不是不缓存,是会被缓存的,只不过每次在向浏览器提供相应数据时,浏览器每次都要向服务器发送请求,有服务器来决策评估缓存的有效性。
no-store : 所有内容都不缓存。对比缓存定义:服务器对比判断文件是否修改,告诉浏览器是否可以使用本地缓存。对比生效时,服务器返回给浏览器的http code 为304,服务器只返回http header信息,并无响应正文。浏览器收到304的返回,知道本地缓存并无修改,直接使用本地缓存。
Last-Modified或者if-Modified-Since,服务器响应浏览器请求,告诉浏览器资源的最后修改时间
ETag是实体标签(Entity Tag)的缩写, 根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果),可以标识资源的状态。可以理解为一个资源的唯一标识符,只要文件发生变化Etag的值也变化。
http请求头缓存相关字段
If-Modified-Since
客户端存取的该资源最后一次修改的时间,来自上次服务端的Last-Modified。
If-None-Match
客户端存取的该资源的检验值,来自上次服务端的ETag。
cache-control:no-cache
no-cache: 跳过本地强制缓存和告诉服务器跳过对比缓存,重新请求资源。
web缓存的实现方式类型:
- 数据库数据缓存
- 服务器段缓存(包括代理服务器缓存和CDN缓存)
- 浏览器端缓存
- web应用层缓存
-
http和https的区别
http用80端口,https用443
https运行在SSL/TLS上,SSL/TLS运行在TCP上;http直接运行在TCP上
HTTPS协议可以理解为HTTP协议的升级(SSL+Http协议),就是在HTTP的基础上增加了数据加密。数据保密,数据防篡改,证书验证(CA)。
https比http的无状态连接要慢
-
http1.0 -1.1- spdy-2.0 大致的区别
1.0-》1.1:
引入更多的缓存处理(如Entity tag)
带宽优化(range头域)
错误通知(新增了错误状态码)
Host头处理(相比之前的虚拟主机,必须包含头域)
支持了长连接和请求的流水线处理,在一个TCP连接上可以多请求多响应
1.1-》spdy
降低延迟
请求优先级(防阻塞)
header压缩,避免重复header。
基于https的加密协议传输
服务端推送
spdy-》2.0
2.0支持明文http传输,spdy只能用https
消息头的压缩算法优化
新的二进制格式
多路复用(连接共享),比长连接优化
除此之外暂时想不到其他的了。缓存字段和200、404、304之类非常常用的状态码一定要记住。
2.tcp的三次握手和四次挥手
tcp三次握手
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
SYN:同步序列编号(Synchronize Sequence Numbers)
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三