1、进程、线程和协程之间的区别与联系
进程:直观点说,保存在硬盘上的程序运行以后,会在内存空间里形成一个独立的内存体,这个内存体有自己独立的地址空间,有自己的堆,上级挂靠单位是操作系统。操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。
进程之间的通信方式有:无名管道( pipe )、高级管道(popen)、有名管道(named pipe)、消息队列( message queue )、信号量( semophore ) 、信号 ( sinal ) 、共享内存( shared memory ) 、套接字( socket )。
线程:称为轻量级进程(Lightweight Process,LWP),是操作系统调度(CPU调度)执行的最小单位。
【区别】:
调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
【联系】:
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程;
协程:是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。
通常创建协程时,会从进程的堆中分配一段内存作为协程的栈。线程的栈有8MB,而协程栈的大小通常只有几十 KB。而且,C库内存池也不会为协程预分配内存,它感知不到协程的存在。这样,更低的内存占用空间为高并发提供了保证,毕竟十万并发请求,就意味着10万个协程。为了实现高性能,我们应该尽可能的减少异步线程。因为协程没有局部存储,相对来说空间成本就小很多,同时它又能满足需求。2、什么是 TCP,什么是UDP,两者之间的对比
TCP(Transmission Control Protocol):传输控制协议,是一种面向连接的、可靠的、基于字节流的传输层通信协议。由IETF的RFC 793定义。
UDP(User Datagram Protocol):用户数据报协议,是一种面向无连接,不可靠、以数据报文段的形式传输的传输层通信协议。由RFC 768描述了UDP。
TCP、UDP区别
1、 连接
TCP面向连接(如打电话先拨号建立连接)
UDP无连接,即发送数据报前不用建立连接(古代写信,无法彼此建立连接,且无法保证信件是否会丢失)
2、安全
TCP提供可靠的服务,通过TCP连接发送的数据,无差错、不丢失、不重复按序到达。
UDP尽最大努力交付,不保证可靠的传输服务。
3、传输效率
TCP传输效率低、UDP传输效率高
4、连接数量
TCP连接只能一对一、点对点通信。
UDP连接支持一对一、一对多、多对一和多对多的交互通信。
5、首部
TCP报文首部20个字节,UDP报文首部8个字节
6、可靠
TCP的逻辑通信是全双工的可靠信道,UDP则是不可靠信道。
7、面向方式
TCP面向字节流,实际上是把TCP数据看成一串无结构的字节流,由于连接的问题,当网络出现波动时,连接可能出现波动问题。
UDP面向报文。UDP没有阻塞控制,因此网络出现拥堵不会使源主机的发送速率降低。
3、HTTPS 与 HTTP 的区别
HTTP协议以明文方式发送内容,不提供任何方式的数据加密。HTTP协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。 HTTPS是具有安全性的ssl加密传输协议。 http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。并且https协议需要到ca申请证书。HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。 HTTPS协议的主要作用可以分为两种: 1)建立一个信息安全通道,来保证数据传输的安全; 2)确认网站的真实性。HTTPS在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。 Tips: 有效的证书需要由权威机构CA签名,CA会用自己的私钥来生成数字签名。4、什么是宏任务与微任务?
Js 是单线程的,但是一些高耗时操作就带来了进程阻塞问题。为了解决这个问题,Js 有两种任务的执行模式:同步模式(Synchronous)和异步模式(Asynchronous)。 在异步模式下,创建异步任务主要分为:宏任务与微任务。宏任务是由宿主(浏览器、Node)发起的,而微任务由 JS 自身发起。宏任务(Macrotask) | 微任务(Microtask) |
---|---|
setTimeout | requestAnimationFrame(有争议) |
setInterval | MutationObserver(浏览器环境) |
MessageChannel | Promise.[ then/catch/finally ] |
I/O,事件队列 | process.nextTick(Node环境) |
setImmediate(Node环境) | queueMicrotask |
script(整体代码块) |
EventLoop事件循环的具体流程如下:
- 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务,放入调用栈,开始执行;
- 执行完该宏任务下所有同步任务后,即调用栈清空后,该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序,依次执行其中的微任务,直至微任务队列清空为止;
- 当微任务队列清空后,一个事件循环结束;
- 接着从宏任务队列中,找到下一个执行的宏任务,开始第二个事件循环,直至宏任务队列清空为止。
5、节流和防抖的区别
防抖(debounce):触发高频率事件时n秒后只会执行一次,如果n秒内再次触发,则会重新计算。
简单概括:每次触发时都会取消之前的延时调用。
const debounce = (cb) => { let timeout; return function () { clearTimeout(timeout); timeout = setTimeout(() => cb.apply(this, arguments), 500); }; };
节流(throttle):高频事件触发,每次触发事件时设置一个延迟调用方法,并且取消之前延时调用的方法。
简单概括:每次触发事件时都会判断是否等待执行的延时函数。
const throttle = (cb, delay = 500) => { let lastCalled = 0; return (...args) => { const now = new Date().getTime(); if (now - lastCalled < delay) return; lastCalled = now; cb(...args); }; };
区别:函数防抖一定连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次。
6、千位分隔符,有小数
实现方式一:function format(str){ if (isNaN(str)) return 'please input a number!'; let num = str.split('.'); let ret; if (num.length > 1){ ret = addSplit(num[0])+'.' + addSplit(num[1]); }else{ ret = addSplit(num[0]); } return ret; } function addSplit(str,flag){ let arr = flag?str.split('').reverse():str.split(''); arr.unshift(0); let ret = []; for (let i = 1; i < arr.length; i++){ ret.push(arr[i]); if (i % 3 === 0){ ret.push(','); }
} return flag?ret.reverse().join(''):ret.join(''); } console.log(format('www.bbbbb')); console.log(format('5454323232.5453')); console.log(format('545454'));
实现方式2
function format(num) { var str = num+''; return str.split("").reverse().reduce((prev, next, index) => { // prev 累计器累计回调的返回值; 表示上一次调用回调时的返回值,或者初始值 init; // next 必需。表示当前正在处理的数组元素; // index 可选。表示当前正在处理的数组元素的索引,若提供 init 值,则起始索引为- 0,否则起始索引为1; return ((index % 3) ? next : (next + ',')) + prev; }) } let num = 1234567890.4323; format(num); // '123,456,789,0.4,323'
金额的话,小数需要特殊处理下,小数点不加“,”。正则实现:
function formatThousand(money) { let res = money.toString().replace(/\d+/, function(num){ // 先提取整数部分 return num.replace(/(\d)(?=(\d{3})+$)/g, function($1){ // ?= 表示正向引用 return $1+","; }); }) return res; } formatThousand(232323.4343343) // '232,323.43433'
7、二叉树的基础概念和遍历方式
树是一种非线性的数据结构,由n(n>=0)个有限节点组成一个具有层次关系的集合,把他叫做树是因为它看起来像一颗倒挂的树,也就是说它是根朝上,叶朝下,子树是不能相交的,除了根节点外,每个节点有且仅有一个父节点,一棵N个节点的树有N-1条边,树是递归定义的。
二叉树就是树形结构(天然的查找语义),树形结构可以更加高效的进行查找和搜索。
1)遍历:
按照一定的顺序“访问(根据不同的场景,访问的需求是不同的,如打印节点值或是计算节点个数)”这个集合的所有元素,不重复,不遗漏。
2)四大遍历方式
对于二叉树这种非线性结构而言,遍历比线性结构就复杂得多,有四大遍历方式(对于二叉树来讲,遍历操作是其他操作的基础):前中后序遍历、层序遍历。
注意:在写前三种遍历方式时可以借用栈结构,保证做到不重不漏不出错,此时的“访问”就是输出结点的值
a.前序遍历:【preOrder】
先访问根节点,递归访问左子树,递归访问右子树,“根左右”,第一次访问根节点就可以输出节点值。
function treeFrontEach(treeList){ if (!treeList || treeList.value === null) return null; console.log(treeList.value); treeFrontEach(treeList.left); treeFrontEach(treeList.right); }
b.中序遍历:【inOrder】
先递归访问左子树,然后访问根节点,最后递归访问右子树,“左根右”。
function treeMiddleEach(treeList){ if (!treeList || treeList.value === null) return null; treeMiddleEach(treeList.left); console.log(treeList.value); treeMiddleEach(treeList.right); }
c.后序遍历:【postOrder】
先递归访问左子树,递归访问右子树,再访问根节点,"左右根"。
拓展:后序的转置输出恰好是前序遍历的镜像:根右左
function treeEndEach(treeList){ if (!treeList || treeList.value === null) return null; treeEndEach(treeList.left); treeEndEach(treeList.right); console.log(treeList.value); }
d.层序遍历:【levelOrder】
按照二叉树的层次一层层访问节点,先左再右。
标签:UDP,遍历,return,第一篇,TCP,面试,num,treeList,套题 From: https://www.cnblogs.com/cczlovexw/p/17090702.html