元素|
49 |
50 | 属性选择器
51 | |选择器|例子|描述|
52 | |-|-|-|
53 | |[attribute] |[target] |选择带有 target 属性的所有元素。|
54 | |[attribute=value] |[target=_blank] |选择带有 target="\_blank" 属性的所有元素。|
55 |
56 | 伪类选择器
57 | |选择器|例子|描述|
58 | |-|-|-|
59 | |:active| a:active |选择活动链接。|
60 | |::after| p::after |在每个 的内容之后插入内容。|
61 | |::before| p::before |在每个
的内容之前插入内容。|
62 | |:first-child |p:first-child |选择属于父元素的第一个子元素的每个
元素。|
63 | |:focus |input:focus |选择获得焦点的 input 元素。|
64 | |:fullscreen| :fullscreen |选择处于全屏模式的元素。|
65 | |:hover |a:hover |选择鼠标指针位于其上的链接。|
66 | |:link| a:link |选择所有未访问过的链接。|
67 | |:not(selector) |:not(p) |选择非
元素的每个元素。|
68 | |:nth-child(n)| p:nth-child(2) |选择属于其父元素的第二个子元素的每个
元素。|
69 | |:visited| a:visited |选择所有已访问的链接。|
70 |
71 | ### Q: CSS 选择器的权重是什么样的?
72 |
73 | | 样式 | 权重 |
74 | | ---------- | --------- |
75 | | !important | 权重最大 |
76 | | 内联样式 | 权重 1000 |
77 | | 类选择器 | 权重 10 |
78 | | id 选择器 | 权重 100 |
79 | | 派生选择器 | 权重 1 |
80 |
81 | ## 3、常见规则
82 |
83 | ### Q:position 的值有哪几种,布局方式是什么样的?
84 |
85 | | 值 | 描述 |
86 | | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
87 | | static | 元素框正常生成。块级元素生成一个矩形框,作为文档流的一部分,行内元素则会创建一个或多个行框,置于其父元素中。 |
88 | | relative | 相对于其正常位置进行定位,元素仍保持其未定位前的形状,它原本所占的空间仍保留。 |
89 | | absolute | 元素框从文档流完全删除,相对于值不为 static 的第一个父元素进行定位。元素原先在正常文档流中所占的空间会关闭,就好像元素原来不存在一样。元素定位后生成一个块级框,而不论原来它在正常流中生成何种类型的框。 |
90 | | fixed | 元素框的表现类似于将 position 设置为 absolute,不过其包含块是视窗本身。 |
91 |
92 | ### Q: 简单描述下 flex 布局?
93 |
94 | 使用 flex 布局的元素会成为容器(flex container),它内部的元素自动成为 flex 项目(flex item)。
95 | 容器拥有两根隐形的轴,水平的主轴(main axis),和竖直的交叉轴。
96 | 此外,需注意使用 flex 容器内元素,即 flex item 的 float,clear、vertical-align 属性将失效。
97 |
98 | 
99 |
100 | flex 的各种属性:
101 |
102 | ```
103 | 1、flex-direction
104 | 属性决定主轴的方向(即项目的排列方向)。
105 |
106 | row(默认值):主轴为水平方向,起点在左端。
107 | row-reverse:主轴为水平方向,起点在右端。
108 | column:主轴为垂直方向,起点在上沿。
109 | column-reverse:主轴为垂直方向,起点在下沿。
110 | ```
111 |
112 | ```
113 | 2、flex-wrap
114 | 默认情况下,项目都排在”轴线”上。本属性定义如果一条轴线排不下,如何换行。
115 |
116 | nowrap(默认值):不换行。
117 | wrap:换行,第一行在上方。
118 | wrap-reverse:换行,第一行在下方。
119 | ```
120 |
121 | ```
122 |
123 | 3、align-items
124 | 定义弹性盒子在交叉轴上如何对齐。
125 |
126 | flex-start:交叉轴的起点对齐。
127 | flex-end:交叉轴的终点对齐。
128 | center:交叉轴的中点对齐。
129 | baseline: 项目的第一行文字的基线对齐。
130 | stretch(默认值):如果项目未设置高度或设为 auto,将占满整个容器的高度。
131 |
132 | ```
133 |
134 | ```
135 |
136 | 4、justify-content
137 | 定义弹性盒子在主轴方向上的对齐方式。
138 |
139 | flex-start:容器开头对齐。
140 | flex-end:容器终点对齐。
141 | center:容器中点对齐。
142 |
143 | ```
144 |
145 | ## 4、常见概念
146 |
147 | ### Q:FC 是什么?BFC 和 IFC 是什么?
148 |
149 | FC:格式化模型。
150 |
151 | FC 会根据 CSS 盒子模型将文档中的元素转换为一个个的盒子,每个盒子的布局由以下因素决定:
152 | 1、盒子的尺寸:精确指定、由约束条件指定或没有指定
153 | 2、盒子的类型:行内盒子(inline)、行内级盒子(inline-level)、原子行内级盒子(atomic inline-level)、块盒子(block)
154 | 3、定位方案:普通流定位、浮动定位或绝对定位
155 | 4、文档树中的其它元素:即当前盒子的子元素或兄弟元素
156 | 5、视窗尺寸与位置
157 | 6、包含的图片的尺寸
158 | 7、其他的某些外部因素
159 |
160 | BFC:块级格式化上下文。
161 |
162 | 1、在 BFC 中,盒子从顶端开始垂直地一个接一个地排列,两个盒子之间的垂直的间隙是由它们的 margin 值所决定的。在一个 BFC 中,两个相邻的块级盒子的垂直外边距会产生折叠。
163 | 2、在 BFC 中,每一个盒子的左外边缘会触碰到容器的左边缘。
164 | 3、浮动元素、绝对定位元素,以及设置了 overflow 属性(除了 visible)的元素不是块级盒子的块容器,因此会为他们的内容创建新的 BFC。
165 |
166 | IFC:行内级格式化上下文。
167 |
168 | 1、在 IFC 中,盒子一个接着一个地水平放置。这些盒子会通过不同的方式进行对齐,如底部对齐,顶部对齐,文字基线对齐。
169 | 2、矩形区域包含着来自一行的盒子叫做盒行盒(line box)。
170 | 3、line box 的宽度由浮动情况和它的包含块决定。line box 的高度由 line-height 计算决定(也就是说,由其内部的块撑开)。
171 |
172 | ### Q:如何清除浮动?
173 |
174 | 浮动可以理解为让某个 div 元素脱离标准流,漂浮在标准流之上。
175 | 一个浮动元素会尽量向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。
176 |
177 | 清除浮动可以理解为打破横向排列。清除浮动的关键字是 clear,其取值有以下几种:
178 | 1、none,默认值。允许两边都可以有浮动对象
179 | 2、left,不允许左边有浮动对象
180 | 3、right,不允许右边有浮动对象
181 | 4、both,不允许有浮动对象
182 | 对于 CSS 的清除浮动(clear),一定要牢记:这个规则只能影响使用清除的元素本身,不能影响其他元素。
183 |
184 | ### Q:什么是回流?什么是重绘?
185 |
186 | 当页面中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新绘制,这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
187 |
188 | 当页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color。则就叫称为重绘。
189 |
190 | 任何对页面中元素的操作都会引起回流或者重绘,比如:
191 |
192 | 1、添加、删除元素(回流+重绘)
193 | 2、隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
194 | 3、移动元素,比如改变 top,left(重绘+回流)。
195 | 4、对 style 的操作(对不同的属性操作,影响不一样)。
196 | 5、还有一种是用户的操作,比如改变浏览器大小,改变浏览器的字体大小等(回流+重绘)
197 |
198 | ### Q:如何开启 GPU 加速?其优缺点是什么?
199 |
200 | 当页面中某个 DOM 元素应用了某些 CSS 规则时就会开启 GPU 加速,如 3D 变换:
201 |
202 | ```css
203 | .cube {
204 | -webkit-transform: translate3d(250px, 250px, 250px) rotate3d(250px, 250px, 250px, -120deg) scale3d(0.5, 0.5, 0.5);
205 | }
206 | ```
207 |
208 | 如果不想对元素用 3D 变换但是还想要开 GPU 加速,就可以:
209 |
210 | ```css
211 | .cube {
212 | -webkit-transform: translateZ(0);
213 | -moz-transform: translateZ(0);
214 | -ms-transform: translateZ(0);
215 | -o-transform: translateZ(0);
216 | transform: translateZ(0);
217 | }
218 | ```
219 |
220 | 但是,一定要注意:不要随意使用 GPU 加速,如果的确能够显著提高性能,可以尝试使用 GPU 加速。但是另一方面,使用 GPU 可能会导致严重的性能问题,因为它增加了内存的使用,而且它会减少移动端设备的电池寿命。
221 |
--------------------------------------------------------------------------------
/开发/前端开发/【4】网络及浏览器.md:
--------------------------------------------------------------------------------
1 | # 校招前端面试常见问题【4】——网络及浏览器
2 |
3 | ## 1、网络相关
4 |
5 | ### Q:请简述一下 HTTP 协议,以及 HTTP1.0/1.1/2.0/3.0 的区别?
6 |
7 | HTTP 协议:超文本传输协议,使用 TCP/IP 协议传输数据。是一个应用层的协议。
8 |
9 | HTTP1.0:HTTP 1.0 规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个 TCP 连接,服务器完成请求处理后立即断开 TCP 连接,服务器不跟踪每个客户也不记录过去的请求。因此 HTTP 1.0 存在很大的性能缺陷——当访问一个包含有许多资源文件的网页时,每次请求和响应都需要建立一个单独的连接,每次连接只是传输一个文档和图像,请求之间完全分离。即使图像文件都很小,但是客户端和服务器端每次建立和关闭连接却是一个相对比较费时的过程,会严重影响到性能。
10 |
11 | HTTP 1.1:支持长连接的 HTTP 协议,在一个 TCP 连接上可以传送多个 HTTP 请求和响应。一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接。
12 |
13 | HTTP2.0:支持多路复用的 HTTP 协议。 HTTP 2.0 允许同时通过单一的 HTTP 2.0 连接发起多重的请求-响应消息。所有通信都在一个连接上完成,这个连接可以承载任意数量的双向数据流。由于 TCP 有慢启动的特点,如果 HTTP 连接很多,就会十分低效。HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接。
14 |
15 | HTTP3.0:也就是 QUIC (quick udp internet connection)协议,是由 google 提出的使用 udp 进行多路并发传输的协议。通过使用 UDP 协议,省去了 TCP 握手和慢启动的时间,拥有极低的建立连接延时。
16 |
17 | ### Q:请简述一下 HTTPS 协议?
18 |
19 | HTTPS 在传输数据之前需要客户端与服务器之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。TLS/SSL 协议不仅仅是一套加密传输的协议,TLS/SSL 中使用了非对称加密,对称加密以及 HASH 算法。
20 |
21 | 握手过程的简单描述如下:
22 | 1、客户端将自己支持的加密规则发送给服务器。
23 |
24 | 2、网站从中选出加密算法和 HASH 算法,将证书发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
25 |
26 | 3、获得证书之后客户端要做以下工作:
27 | a) 验证证书(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等)。
28 | b) 如果通过验证,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密
29 | c) 计算握手信息的 HASH,然后将握手信息也一并加密,最后将所有信息发送给网站。
30 |
31 | 4、网站接收浏览器发来的数据之后要做以下的操作:
32 | a) 使用自己的私钥将信息解密取出密码,使用密码解密握手消息,判断 HASH 是否一致。
33 | b) 计算握手的 HASH,并使用密码加密握手消息,发送给浏览器。
34 |
35 | 5、浏览器解密并计算握手消息的 HASH,如果与服务端发来的 HASH 一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码进行加密。
36 |
37 | ### Q:请简述一下 HTTP 协议中的缓存策略?
38 |
39 | HTTP 的缓存策略有两种:强缓存和协商缓存。
40 |
41 | 强缓存是利用 http 头中的 Expires 和 Cache-Control 两个字段来控制的,用来表示资源的缓存时间。强缓存中,普通刷新会忽略它,但不会清除它,需要强制刷新。
42 |
43 | 例如:
44 | cache-control: max-age=691200 (HTTP 1.1)
45 | expires: Fri, 14 Apr 2017 10:47:02 GMT (HTTP 1.0)
46 |
47 | 协商缓存主要涉及到两个 header 字段:E-Tag 和 Last-Modified。每次读取数据时客户单都会跟服务器通信,并且会增加缓存标识。在第一次请求服务器时,服务器会返回资源,并且返回一个资源的缓存标识,一起存到浏览器的缓存数据库。当第二次请求资源时,浏览器会首先将缓存标识发送给服务器,服务器拿到标识后判断标识是否匹配,如果不匹配,表示资源有更新,服务器会将新数据和新的缓存标识一起返回到浏览器;如果缓存标识匹配,表示资源没有更新,并且返回 304,浏览器就读取本地缓存服务器中的数据。
48 |
49 | 例如:
50 | E-Tag: 123456abcd
51 | Last-Modify: Thu,31 Dec 2037 23:59:59 GMT。
52 |
53 | ## 2、浏览器相关
54 |
55 | ### Q:请列举一下你知道的浏览器内核的种类?
56 |
57 | Trident:IE6、IE7、IE8、IE9、IE10、360 浏览器和猎豹浏览器。
58 | Gecko:firefox 浏览器。
59 | Blink:opera 浏览器。
60 | Webkit:sarfari 和 chrome 浏览器。
61 |
62 | ### Q:浏览器内核中的有哪些线程?
63 |
64 | 内核主要分成五部分:
65 |
66 | 
67 |
68 | GUI 渲染线程:负责渲染浏览器界面,解析 HTML,CSS,构建渲染树,布局和绘制等。当界面需要重绘或回流时,该线程就会执行。注意,GUI 渲染线程与 JS 引擎线程是互斥的,
69 |
70 | JS 引擎线程:解析和执行 javascript。
71 |
72 | 事件触发线程:归属于浏览器而不是 JS 引擎,用来控制事件循环。
73 |
74 | 定时器触发线程:setInterval 与 setTimeout 所在线程。浏览器定时计数器并不是由 JavaScript 引擎计数的,因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待 JS 引擎空闲后执行)。
75 |
76 | 异步 http 请求线程:在 XMLHttpRequest 在连接后是通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由 JavaScript 引擎执行。
77 |
78 | ### Q:请简述一下浏览器的渲染流程?
79 |
80 | 
81 |
82 | 接收到文档后,渲染引擎会对 HTML 文档进行解析生成 DOM 树、对 CSS 文件进行解析生成布局树;同时执行页面中的 JavaScript 代码;最终根据 DOM 树和布局树,计算样式生成渲染树,渲染树中,只会包含即将显示在页面中的元素及其样式信息(如 head 元素、display 为 hidden 的元素就不会包含在渲染树中);根据渲染树需要进行布局来计算每个元素在页面上的位置;
83 |
84 | 接下来渲染引擎开始进行绘制(paint),这一步分为若干阶段:
85 | 1、根据渲染树绘制每层的各个元素。
86 | 2、栅格化绘制出的图像(将渲染树中的节点转换成屏幕上的实际像素)
87 | 3、显示在屏幕上。
88 | 每一层的绘制是由浏览器来完成的;最后的合成是由 GPU 来完成;而栅格化过程取决于浏览器的设置,chrome 默认开启 GPU 栅格化,否则由 CPU 进行。
89 |
90 | ### Q:浏览器从输入请求到呈现页面有哪几步?
91 |
92 | 1、URL 解析
93 | 2、DNS 查询
94 | 3、TCP 连接
95 | 4、处理请求
96 | 5、接受响应
97 | 6、渲染页面
98 |
99 | ### Q:localstorage、sessionstorage 的区别,以及使用场景是什么?
100 |
101 | localStorage:生命周期是永久的,关闭页面或浏览器之后 localStorage 中的数据也不会消失。localStorage 除非主动删除数据,否则数据永远不会消失(只会存储 string)。
102 |
103 | sessionStorage:生命周期是在仅在当前会话下有效。sessionStorage 引入了一个“浏览器窗口”的概念,sessionStorage 是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是 sessionStorage 在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage 也是不一样的。
104 |
105 | 使用方法:
106 |
107 | ```javascript
108 | window.localStorage
109 | window.sessionStorage
110 | ```
111 |
112 | API:
113 |
114 | ```javascript
115 | setItem(key, value) // 保存数据,以键值对的方式储存信息。
116 | getItem(key) // 获取数据,将键值传入,即可获取到对应的 value 值。
117 | removeItem(key) // 删除单个数据,根据键值移除对应的信息。
118 | clear() // 删除所有的数据
119 | key(index) // 获取某个索引的 key
120 | ```
121 |
--------------------------------------------------------------------------------
/开发/前端开发/【5】前端框架及打包工具.md:
--------------------------------------------------------------------------------
1 | # 校招前端面试常见问题【5】——前端框架及常用工具
2 |
3 | ## React
4 |
5 | #### Q:请简述一下虚拟 DOM 的概念?
6 |
7 | 基于 React 进行开发时所有的 DOM 构造都是通过虚拟 DOM 进行,每当数据变化时,React 都会重新构建整个 DOM 树,然后 React 将当前整个 DOM 树和上一次的 DOM 树进行对比,得到 DOM 结构的区别,然后仅仅将需要变化的部分进行实际的浏览器 DOM 更新。
8 |
9 | React 在构建 DOM 的时候,是使用 javascript 的对象模拟 DOM 的,针对 js 的对象进行比较要比针对浏览器 DOM 进行比较的开销小很多。
10 |
11 | #### Q:请简述一下 React 的生命周期?
12 |
13 | 
14 |
15 | #### Q:请简述一下 React Fiber 的概念?
16 |
17 | 在页面元素很多,且需要频繁刷新的场景下,React 15 会出现掉帧的现象。那么为什么会出现掉帧问题呢?其根本原因,是大量的同步计算任务阻塞了浏览器的 UI 渲染。默认情况下,JS 运算、页面布局和页面绘制都是运行在浏览器的主线程当中,他们之间是互斥的关系。如果 JS 运算持续占用主线程,页面就没法得到及时的更新。当我们调用 setState 更新页面的时候,React 会遍历应用的所有节点,计算出差异,然后再更新 UI。如果页面元素很多,整个过程占用的时机就可能超过 16 毫秒,就容易出现掉帧的现象。而原因就是 React 15 采用的是递归的方式遍历整颗组件树。
18 |
19 | react16 将底层更新单元的数据结构改成了链表结构。以前的协调算法是递归调用,通过 react dom 树级关系构成的栈递归。而 fiber 是扁平化的链表的数据存储结构,通过 child 找第一个子节点,return 找父节点,sibling 找兄弟节点。遍历从递归改为循环。
20 |
21 | 这是 React 核心算法的一次大的更新,重写了 React 的 reconciliation 算法。reconciliation 算法是用来更新并且渲染 DOM 树的算法。以前 React 15.x 的版本使用的算法称为“stack reconciliation”,现在称为“fiber reconciler”。
22 |
23 | fiber reconciler 主要的特点是可以把更新流程拆分成一个一个的小的单元进行更新,并且可以中断,转而去执行高优先级的任务或者浏览器的动画渲染等,等主线程空闲了再继续执行更新。
24 |
25 | 对于流畅度问题,我们很容易想到一个 api:requestldleCallback , 这个 api 可以在浏览器空闲的时候执行回调,我们把复杂的任务分片在浏览器空闲的时间执行,就不会影响浏览器的渲染等工作。这个就可以解决复杂任务长时间霸占主线程导致渲染延迟。
26 |
27 | 但是可能由于兼容性的考虑,react 团队放弃了这个 api,转而利用 requestAnimationFrame 和 MessageChannel pollyfill 了一个 requestIdleCallback
28 |
29 | 当前帧先执行浏览器的渲染等任务,如果当前帧还有空闲时间,则执行任务,直到当前帧的时间用完。如果当前帧已经没有空闲时间,就等到下一帧的空闲时间再去执行。
30 |
31 | #### Q:React setState 的时机?
32 |
33 | 使用 setState 时不会直接更新数据,而是会直接将其挂到更新队列中。
34 | 更新的时机是:当前宏任务结束后,微任务开始前。
35 |
36 | ```javascript
37 | this.state = {
38 | a: 1,
39 | }
40 |
41 | // 这种情况只会+1,因为它相当于Object.assign(oldState, {count: XXX}, {count: XXX})
42 | this.setState({ count: this.state.count + 1 })
43 | this.setState({ count: this.state.count + 1 })
44 |
45 | console.log(this.state.count) // 这时候会取到原来的state,也就是1
46 |
47 | // 进行改造,这样就一定会+2了
48 | this.setState((state, props) => {
49 | return { count: state.count + 1 }
50 | })
51 | this.setState((state, props) => {
52 | return { count: state.count + 1 }
53 | })
54 | ```
55 |
56 | ## Vue
57 |
58 | #### Q:什么是 mvvm 模式?
59 |
60 | M: 模型 => 数据,业务逻辑,验证逻辑,模型常常包含业务逻辑。
61 | V: 视图 => 交互界面,是模型数据的可视化呈现,视图可能包含展示逻辑。
62 | VM:视图和模型的中间人。
63 |
64 | 数据双向绑定:V 的变动直接反映在了 VM 上,M 的变化也直接反映在了 VM 上。
65 |
66 | #### Q:请简述一下 vue 响应式数据的原理?
67 |
68 | 响应式数据的关键在于:data 如何更新 view,以及 view 如何更新 data。
69 |
70 | 1、view 更新 data 可以通过事件监听,比如 input 标签监听 'input' 事件就可以实现了。
71 |
72 | 2、而 data 更新 view 的重点是如何知道数据变了。这时候我们就通过`Object.defineProperty()`对属性设置一个 set 函数,当数据改变了就会来触发这个函数,所以我们只要将一些需要更新的方法放在这里面就可以实现 data 更新 view 了。
73 |
74 | Object.defineProperty 的具体用法:
75 |
76 | ```javascript
77 | Object.defineProperty(obj, prop, descriptor)
78 | obj:要在其上定义属性的对象。
79 | prop:要定义或修改的属性的名称。
80 | descriptor:将被定义或修改的属性描述符。
81 |
82 | descriptor 具有以下两种可选值:
83 | get:给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入 this 对象。
84 | set:给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
85 | ```
86 |
87 | 一个简单的响应式数据的例子:
88 |
89 | ```html
90 |
91 |
92 |
93 |
94 | defineProperty
95 |
96 |
97 |
101 |
102 |
118 |
119 |
120 | ```
121 |
122 | #### Q:请简述一下 Vue 的生命周期?
123 |
124 | 
125 |
126 | #### Q:请简述一下 Vue router 的原理?
127 |
128 | Vue router 有两种模式:hash 模式和 history 模式,分别对应了两种原理:
129 |
130 | hash 模式:
131 |
132 | ```
133 | hash("#")符号的本来作用是加在 URL 指示网页中的位置,例如:
134 | http://www.example.com/index.html#print
135 |
136 | #本身以及它后面的字符称之为 hash 可通过 window.location.hash 属性读取。
137 | hash 虽然出现在 url 中,但不会被包括在 http 请求中,对服务器端完全无用,因此,改变 hash 不会重新加载页面。
138 |
139 | 我们可以为 hash 的改变添加监听事件:
140 | window.addEventListener("hashchange",funcRef,false)
141 |
142 | 每一次改变 hash,我们都会重新注入对应的组件,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。
143 | ```
144 |
145 | history 模式:
146 |
147 | ```
148 | 从HTML5开始,History interface提供了2个新的方法:pushState(),replaceState()使得我们可以对浏览器历史记录栈进行修改。
149 |
150 | window.history.pushState(stateObject, title, URL)
151 | window.history.replaceState(stateObject, title, URL)
152 |
153 | stateObject: 当浏览器跳转到新状态时,触发popState事件,该事件将携带stateObject参数的副本
154 | title: 所添加记录的标题
155 | URL: 所添加记录的URL
156 |
157 | 我们可以为window.history的改变添加监听事件:
158 | window.addEventListener("popstate",funcRef,false)
159 |
160 | 在监听事件中,重新注入对应的组件,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。
161 |
162 | 用 HTML5 实现,单页路由的 url 就不会多出一个#,变得更加美观。但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求,可能会造成404。
163 |
164 | ```
165 |
166 | ## 打包工具
167 |
168 | #### Q:介绍一下 webpack?
169 |
170 | webpack 是一个模块打包工具,在 webpack 中,一切文件都是模块,webpack 能做的就是将它们打包在一起。
171 |
172 | webpack 在配置时主要有如下常用属性:
173 |
174 | 1、entry 以及 output:
175 | 入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。
176 |
177 | output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 output 字段,来配置这些处理过程。
178 |
179 | ```javascript
180 | const path = require('path')
181 | module.exports = {
182 | entry: './path/to/my/entry/file.js',
183 | output: {
184 | path: path.resolve(__dirname, 'dist'),
185 | filename: 'my-first-webpack.bundle.js',
186 | },
187 | }
188 | ```
189 |
190 | 2、loader:
191 | loader 让 webpack 能够去处理那些非 JavaScript 文件。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
192 | 本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。
193 |
194 | ```javascript
195 | const path = require('path')
196 | const config = {
197 | module: {
198 | rules: [{ test: /\.txt$/, use: 'raw-loader' }],
199 | },
200 | }
201 | ```
202 |
203 | 3、plugins:
204 | 插件相比于 loader,可以用于执行范围更广的任务,比如压缩打包,优化等。想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。
205 |
206 | ```javascript
207 | const HtmlWebpackPlugin = require('html-webpack-plugin') // 通过 npm 安装
208 | const webpack = require('webpack') // 用于访问内置插件
209 | const config = {
210 | module: {
211 | rules: [{ test: /\.txt$/, use: 'raw-loader' }],
212 | },
213 | plugins: [new HtmlWebpackPlugin({ template: './src/index.html' })],
214 | }
215 | ```
216 |
--------------------------------------------------------------------------------
/开发/前端开发/【6】nodejs.md:
--------------------------------------------------------------------------------
1 | # 校招前端面试常见问题【6】——NodeJS
2 |
3 | ## NodeJS
4 |
5 | #### Q:NodeJS 的 IO 模型特点是什么?与多线程同步 IO 有什么不同?
6 |
7 | NodeJS 的 IO 模型(更准确的说是 js 的执行环境,也就是 v8)的特点是“单线程异步非阻塞”。
8 |
9 | 而与多线程同步 IO,两者各有优劣,应该根据实际应用场景来做取舍。
10 |
11 | 在传统的观点里,异步 IO 的好处是 IO 本身并不需要占用太多的资源,缺点在于非线性代码带来的复杂度和难以理解维护,而多线程同步 IO 的缺点在于性能资源的开销和线程管理的问题。
12 |
13 | 所以很显然,在相同的机器资源里面,异步 IO 的并发量肯定是要高于多线程同步 IO 的;但是服务器程序本身肯定不只是由 IO 组成,还有逻辑运算的部分,过重的逻辑运算依旧会影响性能。换句话说,密集型 CPU 任务会阻塞 js 的执行,导致异步 IO 得不到处理,极大地影响到 node 处理响应的时间。
14 |
15 | 总之,node 的 IO 模型更适合处理 IO 密集型的任务。多线程同步 IO 更适合处理计算密集型的任务。
16 |
17 | #### Q:V8 引擎垃圾回收机制是什么样的?
18 |
19 | 1、如何判断是否可以回收
20 | (1)标记清除
21 | 当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
22 |
23 | 具体做法:
24 | 垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。
25 | 然后,它会去掉运行环境中的变量以及被环境中变量所引用的变量的标记
26 | 此后,依然有标记的变量就被视为准备删除的变量,原因是在运行环境中已经无法访问到这些变量了。
27 | 最后,垃圾收集器完成内存清除工作,销毁那些带标记的值并回收它们所占用的内存空间。
28 |
29 | (2)引用计数
30 | 引用计数的含义是跟踪记录每个值被引用的次数。
31 | 当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。
32 | 如果同一个值又被赋给另一个变量,则该值的引用次数加 1。
33 | 相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。
34 | 当这个值的引用次数变成 0 时,就可以将其占用的内存空间回收回来,这样,当垃圾收集器下次再运行时,它就会释放那 些引用次数为零的值所占用的内存。
35 | 但这样会有循环引用的问题。
36 |
37 | 2、V8 垃圾回收策略
38 | 将内存分为两个生代:新生代(new generation)和老生代(old generation)。
39 | 新生代中的对象为存活时间较短的对象,老生代中的对象为存活时间较长或常驻内存的对象,分别对新老生代采用不同的垃圾回收算法来提高效率,对象最开始都会先被分配到新生代(如果新生代内存空间不够,直接分配到老生代),新生代中的对象会在满足某些条件后,晋升到老生代。
40 |
41 | 新生代主要使用 Scavenge 进行管理,将内存平均分为两块,使用空间叫 From,闲置空间叫 To,新对象都先分配到 From 空间中,在空间快要占满时将存活对象复制到 To 空间中,然后清空 From 的内存空间,此时,调换 From 空间和 To 空间,继续进行内存分配,当满足晋升条件时对象会从新生代晋升到老生代。
42 |
43 | 对象晋升的条件主要有两个:
44 | 如果一个对象是第二次经历从 From 空间复制到 To 空间,那么这个对象会被移动到老生代中。
45 | 当要从 From 空间复制一个对象到 To 空间时,如果 To 空间已经使用了超过 25%,则这个对象直接晋升到老生代中。(设置 25%这个阈值的原因是当这次 Scavenge 回收完成后,这个 To 空间会变为 From 空间,接下来的内存分配将在这个空间中进行。如果占比过高,会影响后续的内存分配)
46 |
47 | 老生代主要采用 Mark-Sweep 和 Mark-Compact 算法,一个是标记清除,一个是标记整理。两者不同的地方是,Mark-Sweep 在垃圾回收后会产生碎片内存,而 Mark-Compact 在清除前会进行一步整理,将存活对象向一侧移动,随后清空边界的另一侧内存,这样空闲的内存都是连续的,但是带来的问题就是速度会慢一些。在 V8 中,老生代是 Mark-Sweep 和 Mark-Compact 两者共同进行管理的。
48 |
49 | #### Q:实现一个 EventEmitter?
50 |
51 | 实现:
52 |
53 | ```javascript
54 | class EventEmitter {
55 | constructor() {
56 | this._events = {}
57 | }
58 |
59 | subscribe(type, handler) {
60 | if (this._events.hasOwnProperty(type)) {
61 | this._events[type].push(handler)
62 | } else {
63 | this._events[type] = [handler]
64 | }
65 | }
66 |
67 | unsubscribe(type, handler) {
68 | if (this._events.hasOwnProperty(type)) {
69 | const index = this._events[type].indexOf(handler)
70 | if (index > -1) {
71 | this._events[type].splice(index, 1)
72 | }
73 | }
74 | }
75 |
76 | once(type, handler) {
77 | let fired = false
78 | let _this = this
79 | function magic() {
80 | _this.unsubscribe(type, magic)
81 |
82 | if (!fired) {
83 | fired = true
84 | handler.apply(_this, arguments)
85 | }
86 | }
87 | this.subscribe(type, magic)
88 | }
89 |
90 | emit(type, args) {
91 | if (this._events.hasOwnProperty(type)) {
92 | this._events[type].forEach((fn) => fn(args))
93 | }
94 | }
95 | }
96 |
97 | module.exports = EventEmitter
98 | ```
99 |
100 | 使用:
101 |
102 | ```javascript
103 | const EventEmitter = require('./myEventEmitter')
104 |
105 | const eventEmitter = new EventEmitter()
106 |
107 | const fn = (args) => {
108 | console.log('good args', args)
109 | }
110 | const fn2 = (args) => {
111 | console.log('good args 2', args)
112 | }
113 | const fn3 = (args) => {
114 | console.log('good args 3', args)
115 | }
116 |
117 | eventEmitter.subscribe('good', fn)
118 | eventEmitter.subscribe('good2', fn2)
119 |
120 | eventEmitter.emit('good', 11111)
121 | eventEmitter.emit('good2', 22222)
122 |
123 | eventEmitter.unsubscribe('good', fn)
124 |
125 | eventEmitter.emit('good2', 22222)
126 |
127 | eventEmitter.once('good3', fn3)
128 | eventEmitter.emit('good3', 33333)
129 |
130 | eventEmitter.emit('good3', 33333)
131 | ```
132 |
133 | #### Q:es6 模块化、commonjs 模块化的区别?
134 |
135 | es6 模块化:
136 |
137 | ```
138 | 在es6规范中,使用import和export可以使js文件模块化。
139 | 每个import的js文件都是单例,如果再次import,就直接在内存中进行读取。
140 |
141 | 导出方式1:
142 | //lib.js 文件
143 | let foo = "stringFoo";
144 | let fn0 = function() {
145 | console.log("fn0");
146 | };
147 | export{foo, fn}
148 |
149 | //main.js文件
150 | import {foo, fn} from "./lib";
151 | console.log(bar+"_"+foo);
152 |
153 | ```
154 |
155 | commonjs 模块化:
156 |
157 | ```
158 | Node 应用由模块组成,采用 CommonJS 模块规范。
159 |
160 | 每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。如果要定义全局变量,需要global属性。
161 |
162 | CommonJS规范规定,每个模块内部,module变量代表当前模块。
163 | 这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。
164 | 为了方便,Node为每个模块提供一个exports变量,指向module.exports。
165 |
166 | 例如:
167 | var test = function () {
168 | console.log(123);
169 | };
170 | module.exports.test = test;
171 |
172 | 使用require('XXX')加载模块。
173 | require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
174 | ```
175 |
176 | ## NodeJS 相关框架
177 |
178 | #### Q:请简述一下 Koa 的洋葱模型?
179 |
180 | koa 洋葱模型是指 koa 中每个中间件的执行顺序。
181 | koa 在执行多个中间件中的逻辑时,会先执行第一个中间件的逻辑,执行到 next()函数后会执行第二个中间件的逻辑,以此类推,直到最后一个中间件。当最后一个中间件执行完毕后,会跳回执行倒数第二个中间件 next()函数后面的代码,以此类推,直到第一个中间件 next()函数后面的代码执行完毕。
182 |
183 | 
184 |
185 | 举例来说:
186 |
187 | ```javascript
188 | const Koa = require('koa')
189 |
190 | const app = new Koa()
191 | const PORT = 3000
192 |
193 | // #1
194 | app.use(async (ctx, next) => {
195 | console.log(1)
196 | await next()
197 | console.log(1)
198 | })
199 | // #2
200 | app.use(async (ctx, next) => {
201 | console.log(2)
202 | await next()
203 | console.log(2)
204 | })
205 |
206 | app.use(async (ctx, next) => {
207 | console.log(3)
208 | })
209 |
210 | app.listen(PORT)
211 | console.log(`http://localhost:${PORT}`)
212 | ```
213 |
214 | 访问 http://localhost:3000,控制台打印:
215 |
216 | ```
217 | 1
218 | 2
219 | 3
220 | 2
221 | 1
222 | ```
223 |
--------------------------------------------------------------------------------
/开发/大数据/mapreduce.md:
--------------------------------------------------------------------------------
1 | ## 海量数据处理常用技术概述
2 | > 如今互联网产生的数据量已经达到PB级别,如何在数据量不断增大的情况下,依然保证快速的检索或者更新数据,是我们面临的问题。
3 | 所谓海量数据处理,是指基于海量数据的存储、处理和操作等。因为数据量太大无法在短时间迅速解决,或者不能一次性读入内存中。
4 |
5 | 在解决海量数据的问题的时候,我们需要什么样的策略和技术,是每一个人都会关心的问题。今天我们就梳理一下在解决大数据问题
6 | 的时候需要使用的技术,但是注意这里只是从技术角度进行分析,只是一种思想并不代表业界的技术策略。
7 | 常用到的算法策略
8 | 1. 分治:多层划分、MapReduce
9 | 2. 排序:快速排序、桶排序、堆排序
10 | 3. 数据结构:堆、位图、布隆过滤器、倒排索引、二叉树、Trie树、B树,红黑树
11 | 4. Hash映射:hashMap、simhash、局部敏感哈希
12 |
13 | ### 海量数据处理--从分而治之到Mapreduce
14 |
15 | **分治**
16 | > 分治是一种算法思想,主要目的是将一个大问题分成多个小问题进行求解,之后合并结果。我们常用到的有归并排序:*先分成两部分进行排序,之后在合并*,
17 | 当然还有其他的很多应用,就比如是我们上篇文章中提到的Top K问题,就是将大文件分成多个小文件进行统计,之后进行合并结果。这里我们对分治进行抽象,
18 | 依然从上述提到的Top K频率统计开始出发。定义如下:有M多个Query日志文件记录,要求得到Top K的Query。
19 | 我们可以抽象成几个步骤:
20 | 1. 多个文件的输入,我们叫做**input splits**
21 | 2. 多进程同时处理多个文档,我们叫做**map**。
22 | 3. **partition** *从上文中我们知道。因为我们要将相同的Query映射的一起*
23 | 4. 多进程处理划分或的文件,我们叫做**reduce**
24 | 5. 合并过个文件的结果,我们叫做**merge**
25 |
26 | > 上面的这四个步骤是我们从Top K问题抽象出来的,为什么我们对每一步进行一个取名字?因为这就是最简单的MapReduce的原理。我们现在就可以认为之前已经
27 | 用过Mapreduce的思想了,它就是这么简单,当然中的很多问题我都没有提出来,但是主要的思想就是这样,很成熟的MapReduce的实现,有Hadoop和CouchDB等。
28 | 我给出一张图片来表示这个过程。
29 | 
30 |
31 | **MapReduce**
32 | > MapReduce是一种编程模式、大数据框架的并行处理接口和分布式算法计算平台,主要用于大规模数据集合的并行计算。一个Mapreduce的程序主要有两部分组成: map和reduce. 它主要借鉴了函数式编程语言和矢量编程语言特性。
33 | MapReduce最早是由Google公司研究提出的一种面向大规模数据处理的并行计算模型和方法。Google公司设计MapReduce的初衷主要是为了解决其搜索引擎中大规模网页数据的并行化处理。
34 |
35 | **MapReduce组成**
36 | > 1. **Map:**
37 | 用户根据需求设置的Map函数,每一个工作节点(主机)处理本地的数据,将结果写入临时文件,给调用Reduce函数的节点使用。
38 |
39 | > 2. **Shuffle:**
40 | 在MapReduce的编程模式中,我们要时刻注意到数据结构是(key, value)对,Shuffle就是打乱数据,也是我们之前提到过的Partition处理,主要目的是将相同的key的数据映射到同一个Reduce工作的节点(这是主要的功能,当然还有其他的功能)。
41 |
42 | > 3. **Reduce:**
43 | Reduce函数,并行处理相同key的函数,返回结果。
44 |
45 | Mapreduce模式这么流行,现在几乎所有的大公司都在使用Hadoop框架,当然可能会有一些优化,不过主要的思想依然是MapReduce模式。在公司中或者个人的使用的时候,我们一般会先搭建Hadoop环境,之后最简单的使用就是提供Map函数和Reduce函数即可,语言可以使用C++、Java、Python等。例如我们提到的Top k问题的伪代码的例子:
46 |
47 | ```
48 | map(String key, String values):
49 | // key: 文档名字
50 | // values: 文档内容
51 | for each line in values:
52 | EmitIntemediate(line, "1")
53 |
54 | ..... // 这中间的省略号,表示还可以加一些代码,
55 | ..... // 不加也不影响结果,只是效率问题,后面会提到
56 |
57 | reduce(String key, Iterator values):
58 | // key: a query
59 | // values: a lists of counts
60 | int result = 0;
61 | for each v in values:
62 | result += ParseInt(v)
63 | Emit(AsString(result))
64 | ```
65 |
66 | **代码抽象**
67 | > map: (k1, v1) ---> list(k2, v2)
68 | reduce: (k2, list(v2)) ---> list(v3)
69 |
70 | MapReduce支持的数据格式,从上述的代码中,我们可以看到MapReduce的输入和输出都是(k, v)对的格式。当然这只是转换之后的格式,一般来书我们的输入文件都是文件,MapReduce认为第一个分隔符之前的字段是key,后面的values,(values可以不存在,例如我们的Top k问题就没有values)。所有在使用的时候,我们只需要用分隔符空格将key和values分开,每一行代表一个数据,提供我们需要的Map和Reduce函数即可。
71 |
72 | 文章到此应该已经可以结束了,我们可以在任何MapReduce框架下,根据需求写出map函数和reduce函数。对于想用使用MapReduce的程序员来说,在写函数的时候只需要注意key和value怎么设置,如何编写map和reduce函数,因为中间的细节,运行的框架已经帮我们封装的很好的,这就是为什么Mapreduce在业界流行。这种编程模式很简单,只要提map和reduce函数,对于那些没有并行计算和分布式处理经验的程序员,MapReduce框架帮我们处理好了并行计算、错误容忍、本地读取优化和加载平衡的细节,我们只需要关注业务,不用关心细节,还有就是这么编程模式可以简单的解决很多常见的问题,例如: linux中的grep命令,Sort,Top K,倒排索引等问题。
73 |
74 | 知其然而知其所以然,不仅更能帮助我们写出更优的代码,更重要的是如何在改进现有的技术,使其更好的应用到我们的业务上,因为很多大公司都会重写这种代码,使其在公司内部更好的应用。
75 |
76 |
77 | ### 浅谈技术细节
78 |
79 | MapReduce模式下我们需要关注的问题如下(参考论文):
80 | 1. **数据和代码如何存储?**
81 | > 设置一个Master,拷贝代码文件,分配给节点进行处理,指定Map或者Reduce已经输入和输出文件的路径。所有Master节点是一个管理节点负责调度。
82 | 2. **如何Shuffle?**
83 | > 在MapReduce中都是(key, values)数据,输入的M个文件直接对应M的Map,产生的中间结果key2,通过哈希函数,
84 | hash(key) % R(R是Reduce的个数)。当然我们需要设置一个好的hash函数,保证任务不平衡分到不同的Reduce节点上。
85 |
86 | 3. **节点之间如何通信?**
87 | > Master负责调度和通信,其他节点之和Master节点通信,master监控所有节点的信息,比如是map或者reduce任务,是否运行结束,占用的资源、文件读写速度等,master会重新分配那些已经完成的节点任务,对所有的错误的节点重新执行。
88 | 4. **节点出现错误如何解决?**
89 | > 因为有master的存在,可以重新执行出现错误的运行节点,注意的是对于出错的map任务,其分配到的reduce任务也要重新执行。节点运行bug,我们可以修改代码,使其更鲁棒,但是有时候我们必须使用try-catch操作跳过一些错误的bad lines.
90 | 5. **Map和Reduce个数如何设置?**
91 | > 这个设置和集群的个数和经验有很大关系,建议我们每一个map任务的输入数据16-64MB, 因此map的个数 = 总的文件大小 / 16-64MB. reduce的个数建议大于节点的个数,这样可以保证更好的并行计算。
92 | 6. **怎么控制负载平衡?**
93 | > master会监控所有节点的运行状态,并且要对所有的运行完成的节点重新分配任务,来保证负载均衡,需要注意的是这里的并行计算是map和reduce的分别并行计算,必须保证map执行之后才能执行reduce(因为你有shuffle操作)。
94 | 7. **技巧**
95 | >+ map任务运行时候尽可能的读取本地或者当前局域内的文件,减少文件传输的网络带宽
96 | > + M和R的设置会对master的监督有一定的影响,因为要监督所有的状态
97 | > + 备份运行状态很重要,可以知道那台节点运行的缓慢,可能出现异常,可以让其他节点代替它运行任务
98 | > + shuffle操作的hash函数真的很重要,可以有效的解决负载均衡
99 | > + map生成的中间文件要根据key进行排序,也可以便于划分
100 | > + map和reduce之间有时候需要加合并(combiner)操作,可以起到加速作用
101 |
102 | ### 参考
103 | 1. [MapReduce wikipedia ](https://en.wikipedia.org/wiki/MapReduce)
104 | 2. [MapReduce Paper](https://static.googleusercontent.com/media/research.google.com/en//archive/mapreduce-osdi04.pdf)
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/开发/大数据/questions.md:
--------------------------------------------------------------------------------
1 | # 面试题目
2 |
3 | ## 1. 相同URL
4 | > **题目**: 给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?
5 |
6 | > 方案1:估计每个文件的大小为50G×64=320G,远远大于内存限制的4G。所以不可能将其完全加载到内存中处理。考虑采取分而治之的方法。
7 | >
8 | > 遍历文件a,对每个url求取 hash(url)%1000[比如ASCII码值求和], 然后根据所取得的值将url分别存储到1000个小文件(记为a0, a1, … , a999)中。这样每个小文件的大约为300M。
9 | >
10 | > 遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0, b1, … , b999)。
11 | >
12 | > 这样处理后,所有可能相同的url都在对应的小文件(a0 vs b0, a1 vs b1, … , a999 vs b999)中,不对应的小文件不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
13 | >
14 | > 求每对小文件中相同的url时,可以把其中一个小文件的url存储到hash_set中。然后遍历另一个小文件的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。
15 |
16 |
17 | > 方案2:如果允许有一定的错误率,可以使用Bloom filter,4G内存大概可以表示340亿bit。将其中一个文件中的url使用Bloom filter映射为这340亿bit,然后挨个读取另外一个文件的url,检查是否与Bloom filter,如果是,那么该url应该是共同的url(注意会有一定的错误率)。
18 |
19 | ## 2. Query排序
20 | > **题目**: 有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求你按照query的频度排序。
21 | >
22 | > 方案1:
23 | > 顺序读取10个文件,按照hash(query)%10的结果将query写入到另外10个文件(r1,r2…r10)中。这样新生成的文件每个的大小大约也1G(假设hash函数是随机的)。
24 | >
25 | > [2G左右的机器] 对r1,r2…r10用hash_map(query, query_count)来统计每个query出现的次数。利用快速/堆/归并排序按照出现次数进行排序。将排序好的query和对应的query_cout输出到文件中。这样得到了10个排好序的文件(r1,r2…r10).
26 | >
27 | > 对(r1,r2…r10)这10个文件归并排序(内排序和外排序结合)
28 | >
29 | > 方案2:
30 | > 一般query的总量是有限的,只是重复的次数比较多而已,可能对于所有的query,一次性就可以加入到内存了。这样,我们就可以采用trie树/hash_map等直接来统计每个query出现的次数,然后按出现次数做快速/堆/归并排序就可以了。
31 |
32 | ## 3. Top k 单词
33 | **题目**: 有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
34 |
35 | > 1. 顺序读文件,对每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(x0, x1, … x4999)中。这样每个文件大概是200k左右,如果有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。
36 | > 2. 对每个小文件,统计每个文件出现的词及相应的频率(可以采用trie树/hash_map等),并取出现频率最大的100个词(可以用含100个结点的最小堆),并把100词及相应的频率存入文件,这样又得到了5000个文件。
37 | > 3. 下一步就是把这5000个文件进行归并(类似与归并排序)的过程了。
38 |
39 | ## 4. IP统计
40 | > **题目**: 海量日志数据,提取出某日访问百度次数最多的那个IP。
41 | > 1. 定位到某日,并把访问百度的日志中的IP取出来,逐个写入到大文件中。注意IP是32位,最多有2^32个IP。-
42 | > 2. 采用映射的方法,比如模1000,把整个大文件映射为1000个小文件
43 | > 3. 找出每个小文件出现频率最大的IP(可以采用hash_map进行频率统计,然后再找出频率最大的几个)及相应的频率。
44 | > 4. 然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。
45 |
46 | ## 5. 不重复的整数
47 | > **题目**: 在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
48 | >
49 | > 方案1:
50 | > 采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32*2bit = 1G内存,还可以接受。
51 | > 扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。
52 | >
53 | > 方案2:
54 | >
55 | > 采用上题类似的方法,进行划分小文件的方法。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。
56 | >
57 | ## 6. Top K
58 | > **题目**: 海量数据分布在100台电脑中,想个办法高校统计出这批数据的TOP10。
59 |
60 | > mapreduce还没有使用,是不是应该使用下mapreduce, 找key,定value.
61 |
62 | # 参考
63 | 1. https://blog.csdn.net/u012289441/article/details/45192775
64 | 2. https://blog.csdn.net/v_july_v/article/details/6685962
65 |
--------------------------------------------------------------------------------
/数学/统计学/logic.md:
--------------------------------------------------------------------------------
1 | # 逻辑题目
2 | 逻辑题目现在也是面试中常考的题目,也不清楚面试出这种题目的意义,可能就是考察
3 | 面试人员是否逻辑清晰. 这种题目没有什么好的方法,除非你见过原题,否则,只能根据
4 | 所给出的条件慢慢分析,尽量不要用常规思路,希望大家要跳跃思维. 如果实在不行就
5 | 给出一种解法,可能不是最优的,至少表示我们有逻辑.
6 |
7 | ## 1. 猜数字
8 | > **题目**:
9 | > 两人玩游戏,在脑门上贴数字(正整数>=1),只看见对方的,看不见自己的,而且两人的数字相差1.
10 | ```
11 | 以下是两人的对话:
12 | A:我不知道
13 | B:我也不知道
14 | A:我知道了
15 | B:我也知道了
16 | 问A头上的字是多少,B头上的字是多少.
17 | ```
18 | **解析**: 只看见对方的,看不见自己的.
19 | 1. 假设A头上的数字是x(x>=1); B头上的数字是y(y>=1).
20 | 2. 条件1, A看到B头上的y,说不知道自己的是多少.
21 | 确定y>=2, 这样的话x可以取两个值y+1,和y-1.
22 | 3. 条件2: B看到x后,说我也不知道,同理可以确定,x>=2, y取值是x-1和x+1.
23 | 4. 条件3, A说我知道了, 因为A的头上x取值是y-1和y+1, 尽然A说自己知道了, 肯定要排除y-1和y+1的其中的一个,现在有 x>=2, y>=2, 只有y=2的时候,y-1=1, 此时x只能是y+1=2, 如果x=1, B就可以直接知道自己的是2, A确定自己是3,之后,
24 | 5. 条件4, B说他也知道了, A是3, B是2和4,只有自己是2的时候,A才可能知道自己是3.
25 |
26 | ## 2. 握手
27 | > **题目**:
28 | > 五队夫妇甲、乙、丙、丁、戊举行家庭聚会,每一个人都可能和其他人握手,但夫妇之间绝对不握手,聚会结束时,甲先生问其他人: 各握了几次手?
29 | 得到的答案是: 0、1、2、3、4、5、6、7、8,试问:甲太太握了几次手? 确定一点每一对夫妇一定会有一个人和其他夫妇握手.
30 |
31 | **解析**:
32 | 每一对夫妇一定会有一个人和其他夫妇握手. 所有一对夫妇握手的次数和一定是8.
33 | 记0、1…8这9个人分别为A0、A1…A8。
34 | 首先,A8和A0是夫妇。因为A0没有和其他任何人握手,而A8握了别家的所有人的手。
35 | 继续推导,A1和A7是夫妇。因为A1已经和A8握过1次手,A7必须和除了A0和自己配偶以外的所有人握手,因此,A1和A7只能是夫妇。
36 | 同理,A2和A6是夫妇,A3和A5是夫妇,
37 | 最后,A4和甲是夫妇。题目中4只出现一次,因而甲和甲的夫人都握了4次手。
38 |
39 | ## 3. 找出毒药
40 | > **题目**:
41 | > 实验室里有8瓶饮料,已知其中有且仅有一瓶有毒,小白鼠喝了有毒的饮料后,将会在24小时后毒发身亡。实验室的小李需要在24小时后知道有毒的饮料是哪瓶,他可以使用小白鼠试喝饮料,请问,小李最少需要用几只小白鼠试喝饮料?
42 |
43 | **解析**:
44 |
45 | 将8个瓶子进行如下编码:
46 | (000)_2=0
47 | (001)_2=1
48 | (010)_2=2
49 | (011)_2=3
50 | (100)_2=4
51 | (101)_2=5
52 | (110)_2=6
53 | (111)_2=7
54 | 编码后的0/1位表示一个老鼠,0-7表示8个瓶子。按照3个二进制位中每位是否为1分类,即最低位为1的1、3、5、7号瓶子的药混起来给老鼠1吃,次低位为1的2、3、6、7号瓶子的药混起来给老鼠2吃,最高位为1的4、5、6、7号瓶子的药混起来给老鼠3吃.
55 | 24小时后,哪个老鼠死了,相应的位标为1。如最低老鼠1死了、次低老鼠2死了、最高老鼠3没死,那么就是011=5号瓶子有毒。
56 | 即:n只老鼠可以最多检验2^n个瓶子。所有8个饮料最多用三个小白鼠.
57 |
58 |
59 | ## 4. 坏鸡蛋
60 | > **题目**: 有十二个鸡蛋,有一个是坏的(重量与其余鸡蛋不同)请问用天平最少称几次,才能称出哪个鸡蛋是坏的?
61 |
62 | **解析**:
63 |
64 | 题目中没有说明坏的蛋是比好的蛋重还是轻。本题可以将鸡蛋分成三份,每份四只。为表述方便,将鸡蛋编号为1到12。
65 | 第一次,取1234放在天平的左端,5678放在天平的右端。天平有两种情况,平衡或不平衡。
66 | 1)先分析天平平衡的情况:若平,则重量不同的蛋在剩下的4个中。
67 | 第二次用天平,任意取3个1到8号中的蛋放在天平的左端,从9到12号蛋中任意取3个(例如9,10,11)放在另右端,又有两种情况,平衡或不平衡
68 | 若平衡,则12号蛋为重量不同的蛋,第三次用天平,把12号蛋和其他任意一蛋比较,可以知道是轻还是重.
69 | 若不平衡,则可知重量不同的蛋在9,10,11这3个蛋中,并且可以知道他比其他蛋重还是轻,第三次用天平,任意取其中2蛋(例如9,10)放在天平两端,若平衡,则剩下的蛋(11号蛋)为要找的蛋,若不平衡,根据前面判断的该蛋是比较轻还是重可以判断天平上的其中一个蛋为要找的蛋.
70 | 2)下面分析第一次天平不平衡的情况。那么有左端重或者右端重两种情况,不妨假设左端重(如果是右端重也是一样的)。
71 | 现在第二次用天平,从左端任意拿下3个蛋(例如123),从右端拿3个蛋(例如567)放到左端,再从第一次称时剩下的4个蛋中任意拿3个(例如9,10,11)到右端,这时天平会出现3种情况:a)左端重,b)平衡,c)右端重。我们一个一个来分析。
72 | a)左端重,那么要找的蛋肯定是4号蛋或者8号蛋。第三次用天平,把其中一蛋(例如4号蛋)放在天平左端,任意取其余10个蛋中的一个蛋放在右端,又有3种情况:
73 | 一)若平衡,则8号蛋为要找的蛋,并且根据第二次用天平的结果,可知比其余蛋轻。
74 | 二)若左端重,则4号蛋为要找的蛋,并且比其余蛋重。
75 | 三)若右端重,则4号蛋为要找的蛋,并且比其余蛋轻。
76 | b)平衡,那么要找的蛋在从左端拿下的三个蛋(1,2,3)中,由于第一次用天平左端重,所以可知这个蛋比其余的蛋重,接下了来的分析和前面的一样,不再重复。
77 | c)右端重,那么要找的蛋在从右端移到左端的3个蛋(5,6,7)中,并且由天平第一次左端重,第二次右端重可知,该蛋比其他蛋轻,接下来的分析同前面一样。
78 | 所以,需要称重三次。
79 |
80 |
81 | ## 5. 测半径
82 | > **题目**: 一个球、一把长度大约是球的直径2/3长度的直尺.你怎样测出球的半径?
83 |
84 | **解析**:
85 |
86 |
87 |
88 | ## 6. 过河
89 | > **题目**: 有A、B、C、D四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟,只有一支手电筒,并且同时最多只能两个人一起过桥. 请问,最短需要几分钟四人都能过桥?
90 |
91 | **解析**:
92 |
93 | A: 1
94 | B: 2
95 | C: 5
96 | D: 10
97 | 1. AB过去(花费2分钟),A回来(花费1分钟),共1+2=3
98 | 2. CD过去,让花费时间相近的人一起走,可以降低时间的浪费(花费10分钟), B回来(花费2分钟),共10+2=12
99 | 3. AB一起过去(花费2分钟),ABCD全部过来共花费3+12+2=17分钟.
100 |
101 |
102 | ## 7. 称石头
103 | > **题目**: 给你8颗小石头和一架天平,其中有7颗石头重量一样,另外一个比这7颗略重。请问在最坏情况下,最少要称重几次,才能把这颗较重的石头找出来?
104 |
105 | **解析**:
106 |
107 | 分为332.进行称重
108 |
109 | 首先任取8个石子中的6个进行称重,天平两边都是3个石子.
110 | 1. 如果重量相等
111 | 再称剩下的两个石子即可找出重的.(2次)
112 | 2. 如果不相等.
113 | 取较重的一边的任意2个称重,如果相等则剩下的1个是重的,如不相等则较重的一个是要找的石子.(2次)
114 |
115 | 最少两次称重可以找出重的石头.
116 |
117 | ## 8. 倒水
118 | > **题目**: 假设有一个池塘,里面有无穷多的水. 现有2个空水壶,容积分别为5升和6升. 问题是如何只用这2个水壶从池塘里取得3升的水.
119 |
120 | **解析**:
121 |
122 | 1. 6升容器装满水, 将水把5升容器倒满, 则6升容器中剩下1升水.
123 | 2. 清空5升容器,并将6升容器中的1升水倒入5升容器中.
124 | 3. 6升容器装满水, 将水把5升容器倒满, 则6升容器中剩下2升水.
125 | 4. 清空5升容器,并将6升容器中的2升水倒入5升容器中.
126 | 5. 6升容器装满水, 将水把5升容器倒满, 则6升容器中剩下3升水.
127 |
128 | ## 9. 绳子时间
129 | > **题目**: 烧一根不均匀的绳子要用一个小时,如何用它来判断半个小时? 烧一根不均匀的绳子,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳子的方法来计时45分钟呢?:
130 |
131 | **解析**:
132 |
133 | 1. 如何判断半个小时
134 | 将根绳子两头同时点燃,绳子全部烧完,就是半个小时.
135 |
136 | 1. 如何计时45分钟,
137 | 选择使用两个绳子A和B,将绳子A两头点燃,绳子B一头点燃.
138 | 当绳子A烧完已经过去30分钟,此时点燃绳子B的另一端,直到绳子B烧完一共是45分钟.
139 |
140 |
141 | ## 10. 植树
142 | > **题目**: 怎么样种植4棵树木,使其中任意两棵树的距离相等?
143 |
144 | **解析**:
145 |
146 | 从三维空间考虑,画出一个空间正四面体,使其所有的边的长度相同.
147 |
148 |
149 |
150 |
151 | # 参考(copy)
152 | 1. https://blog.csdn.net/linjcai/article/details/80868385
153 | 2. https://www.julyedu.com/question/select/kp_id/1
154 | 3. https://www.cnblogs.com/pang951189/p/7439670.html
--------------------------------------------------------------------------------
/数学/统计学/probability.md:
--------------------------------------------------------------------------------
1 | # 概率题目
2 | 现在的面试中,大部分公司都会问道概率相关的问题,我们现在给出几道常见的概率问题.
3 |
4 | ## 1. 三角形问题
5 | > **题目**: 给你一根铅笔,将铅笔折两次,组成三角形的概率是多大.
6 |
7 | > **解析**:
8 |
9 | 设: 铅笔长度是1, 折两次之后,得到三条边,对应的长度分别是x,y,1-x-y.
10 | 1. 得到条件:
11 | 0 < x < 1
12 | 0 < y < 1
13 | 0 < 1-x-y < 1
14 | 计算得到面积是: S=1/2
15 | 2. 根据两边之和大于第三边,进行计算:
16 | x + y > 1-x-y => x + y > 1/2
17 | x + (1-x-y) > y => y < 1/2
18 | y + (1-x-y) > x => x < 1/2
19 | 计算得到面积是: A=1/8
20 | 做线性规划求解:
21 | 第一步,根据1中的所有条件,画出中的取值面积S,
22 | 第二步,根据2中的不等式,画出满足条件的面积A.
23 | 最后的概率=A/S=(1/8) / (1/2) = 1/4.
24 |
25 | 方法二: (思路来自网友Summer)
26 | 排除存在的可能性,
27 |
28 | 第一次,x+y=1,假设y>x,如果选择y作为一条边肯定不满足,这时就排除了1/2,只能选x作为一个边。
29 |
30 | 第二次,从y中折出两条边,一定满足三边只和大于第三边,只能根据两边只差>第三边进行排除。因为y>x,一定是从y中的两个边之差>x。假设从y中折一个a,一个y-a。计算,
31 | y-a-a>x,得到y>x+2a,又因为x<1/2,y>1/2,
32 | 根据三个不等式得到排除概率1/4。
33 |
34 | 1-1/2-1/4,
35 |
36 |
37 | ## 2. 排列组合
38 | > **题目**: 20个阿里巴巴B2B技术部的员工被安排为4排,每排5个人,我们任意选其中4人送给他们一人一本《effective c++》,那么我们选出的4人都在不同排的概率是多少?
39 |
40 | > **解析**:
41 |
42 | 1. 从20个人中,任选4个,是C(20,4).
43 | 2. 4个人在不同排,即从每排中选中一个C(5,1)*C(5,1)*C(5,1)*C(5,1)
44 | 3. 所以四个人在不同的概率是 C(5,1)^4 / C(20,4)
45 |
46 |
47 | ## 3. 男女比例
48 | > **题目**: 在一个世世代代都重男轻女的村庄里,村长决定颁布一条法律,村子里没有生育出儿子的夫妻可以一直生育直到生出儿子为止,假设现在村子上的男女比例是1:1,这条法律颁布之后的若干年村子的男女比例将会多少?
49 |
50 | > **解析**:
51 |
52 | 还是1:1.
53 | 先验性的认为生男生女的自然概率相同,都是0.5;由于生育儿子后就不再生,所以,每个家庭都有且只有一个儿子。假定家庭数目为1,则S(男)=1。
54 | 有0.5的家庭一胎生男就停止生育;剩下的0.5的家庭,有0.25二胎生男则停止生育……,从而,每个家庭的女孩数目为:
55 | $$S(女)=\sum_{i=1}^{m}(\frac{1}{2})^i(i-1)=1$$
56 |
57 |
58 | ## 4. 取球问题
59 | > **题目**: 袋中有红球,黄球,白球各一个,每次任意取一个又放回,如此连续抽取3次,求下列概率值:
60 | > 1. 颜色不全相同
61 | > 2. 颜色全相同
62 | > 3. 颜色全不同
63 | > 4. 颜色无红色
64 |
65 | > **解析**:
66 |
67 | 1. 每次都取红球的概率是1/3, 如果都是3次都是红色概率则是: (1/3)*(1/3)*(1/3)=1/27
68 | 所有颜色全相同的概率是3*(1/3)*(1/3)*(1/3)=1/9.
69 |
70 | 2. 颜色不全相同的概率: 1-颜色全相同的概率=8/9.
71 |
72 | 3. 颜色全不同:
73 | 假设三次依次是红,黄,白: 概率是(1/3)*(1/3)*(1/3)=1/27
74 | 颜色全排列是A(3,3)=6
75 | 所有颜色全不同的概率是6*1/27 = 2/9
76 | 4. 无红色的概率:
77 | (2/3)*(2/3)*(2/3)=8/27
78 |
79 | ## 5. 等概率器
80 | > **题目**: 已知一随机发生器,产生0的概率是p,产生1的概率是1-p,现在要你构造一个发生器,使得它产生0和1的概率均为1/2。(或者是非等概率硬币,也是一样的情况).
81 |
82 | > **解析**:
83 |
84 | 找到等概率事件. 考虑连续产生两个随机数,结果只有四种可能:00、01、10、11,其中产生01和产生10的概率是相等的,均为p*(1-p),于是可以利用这个概率相等的特性等概率地产生01随机数。
85 | 比如把01映射为0,10映射为1。于是整个方案就是:
86 | 产生两个随机数,如果结果是00或11就丢弃重来,如果结果是01则产生0,结果是10则产生1。
87 |
88 |
89 | ## 6. 再谈等概率器
90 | > **题目**: 给你一个不均匀的骰子,1-6出现的概率都不相同,你也不知道每个面出现的概率,现在让你用这个骰子构造一个01发生器,使得01出现的概率都是1/2.
91 |
92 | > **解析**:
93 |
94 | 方法1:
95 | 找到一个等概率事件,因为每一个面出现的概率都不知道,现在我们假设扔6次骰子,1-6分别出现一次为事件p,那么p这个序列的概率就是(p1*p2*p3*p4*p5*p6), 我们将这样构造
96 | 1. 所有以(1,2,3)开头的这样的序列p对应0;
97 | 2. 所有以(4,5,6)开头的这样的序列p对应1;
98 | 3. 每6次作为一个事件,不满足p序列的要求,这次实验就作废.
99 |
100 | 看起来0和1产生的概率都是1/2,都是有一个问题,我们需要扔很多次才能得到一次0或1.这种方法理论上可行,实际中不好用.
101 |
102 | 方法2:
103 | 0101:大于小于.
104 | 我们将扔两次骰子作为一个时间,假设第一是x,第二次是y.
105 | 1. x > y: 对应0
106 | 2. x < y: 对应1
107 | 3. x == y: 当x属于[1,2,3]时对应0, 否则对应1.
108 |
109 | 各个面出现的概率不同,这个满足要求吗?
110 | 11 12 13 14 15 16
111 | 21 22 23 24 25 26
112 | 31 32 33 34 35 35
113 | 41 42 43 44 45 46
114 | 51 52 53 54 55 56
115 | 61 62 63 64 65 66
116 |
117 | 可以看出,左下对应0,右上对应1. 而且出现的次数相同.
118 |
119 |
120 | ## 7. 吃苹果
121 | > **题目**: 有一苹果两个人抛硬币来决定谁吃这个苹果先抛到正面者吃。问先抛者吃到苹果的概率是多少?
122 |
123 | > **解析**:
124 | 先抛者A吃苹果, 后者是B:
125 | A(第一次)吃: 1/2
126 | A(第二次)吃: 1/2(!A)*1/2(!B)*1/2(A)=1/8
127 | 这是一个等比数列,公比是1/4, 首项是1/2.
128 | 求解的(1/2)*(1-(1/4^n)) / (1-1/4) = (1/2)/(3/4) = 2/3.
129 |
130 | ## 8. 蚂蚁爬三角形
131 | > **题目**: 一个三角形, 三个端点上有三只蚂蚁,蚂蚁可以绕任意边走,问蚂蚁不相撞的概率是多少?
132 |
133 | > **解析**:
134 |
135 | 1.每个蚂蚁在方向的选择上有且只有2种可能,共有3只蚂蚁,所以共有2的3次方种可能
136 | 2.不相撞有有2种可能,即全为顺时针方向或全为逆时针方向。
137 | 不相撞概率=不相撞/全部=2/8
138 |
139 |
140 | ## 9. 正确的概率
141 | > **题目**: 甲乙两个人答对一道题的概率分别为90%和80%,对于一道判断题,他们都选择了“正确”,问这道题正确的概率.
142 |
143 | > **解析**:
144 |
145 | 设:
146 | 甲的选择是"正确"的,是事件A.
147 | 乙的选择是"正确"的,是事件B.
148 | 这道题是正确的是事件C.
149 | 则有:
150 | $$P(A|C)=0.9 \tag{1}$$
151 |
152 | $$P(B|C)=0.8 \tag{2}$$
153 |
154 | 目标是求: P(C|AB), 根据贝叶斯公式有:
155 |
156 | $$P(C|AB)=\frac{P(AB|C)*P(C)}{P(AB|C)*P(C)+(AB|\bar{C})*P(\bar{C})} \tag{3}$$
157 |
158 | 可以认为A和B是独立事件.则有:
159 | $$P(AB|C)=P(A|C)*P(B|C)=0.72$$
160 | $$P(AB|{C})=P(A|{C})*P(B|\bar{C})=(1-0.9)*(1-0.8)=0.02$$
161 |
162 | 根据实际情况,一道题对或者错的概率是0.5. 则公式3的结果是:
163 |
164 | $$\frac{0.72*0.5}{0.72*0.5+0.02*0.5}=\frac{36}{37}$$
165 |
166 | ## 10. 和超过1的个数
167 | > **题目**: 从(0,1)中随机取数,期望情况下取多少个数才能让和超过1.
168 |
169 | > **解析**:
170 |
171 |
172 |
173 |
174 |
175 | # 参考
176 | 1. https://www.julyedu.com/question/selectAnalyze/kp_id/6/cate/%E6%A6%82%E7%8E%87%E7%BB%9F%E8%AE%A1
177 | 2. https://blog.csdn.net/huazhongkejidaxuezpp/article/details/73662357
178 | 3. https://www.cnblogs.com/sunflower627/p/4839031.html
179 | 4. http://www.voidcn.com/article/p-afkjgouj-qm.html
180 | 5. https://blog.csdn.net/rudyalwayhere/article/details/7349957
--------------------------------------------------------------------------------
/数据结构与算法/sort.md:
--------------------------------------------------------------------------------
1 | # 排序(sort)
2 | > 排序的目的是让一组无序的对象变成有序(升序、降序),排序在面试中很容易被问道。排序之所以这么重要是因为排序是解决大部分问题的第一步,一些看似复杂的问题当数据有序的时候就变的简单,例如查找问题,如果数组有序可以使用搞笑的折半查找。
3 |
4 | 需要提出,这篇文章并不介绍排序,什么插入、冒泡、希尔等算法,我们都不会介绍,我们的目的是给出最常见的关于排序的面试题目,俗称押题,当然希望每个人都能研究每一个题目,在面试过程中遇到排序问题,都可以解决。
5 |
6 | ## 1. 快速排序
7 | > 题目: 这是面试中最常见的问题,手写快排,面试官主要是考查候选人的算法基本工。
8 | > 公司: 爱奇艺,某金融公司
9 |
10 | ```
11 | template
12 | static bool cmp(const T a, const T b) {
13 | return a < b;
14 | }
15 |
16 | template
17 | int Poivt(T list[], int start,int end, bool (*cmp)(T, T)=cmp) {
18 | int t = randint(start, end);
19 | swap(list[t],list[start]);
20 | int p,i,j;
21 | i = start+1;
22 | j = end;
23 | p = start;
24 | while(1) {
25 | while(istart && !cmp(list[j],list[p])) --j;
27 | if(j<=i) break;
28 | else{
29 | swap(list[i],list[j]);
30 | ++i;
31 | --j;
32 | }
33 | }
34 | swap(list[j],list[p]);
35 | return j;
36 | }
37 |
38 | // qsort
39 | template
40 | void QuickSort(T list[], int start,int end, bool (*cmp)(T, T)=cmp) {
41 | if(start>=end) return;
42 | int p = Poivt(list,start,end,cmp);
43 | QuickSort(list,start,p-1,cmp);
44 | QuickSort(list,p+1,end,cmp);
45 | }
46 | ```
47 |
48 | ## 2. 堆排序
49 | > 题目: 手写堆排序
50 | > 公司: 阿里
51 | ```
52 | // 第一步建立最大堆, 下标从1开始 A[1..n]
53 | void BuildMaxHeap(int *A, int n, int &heapsize){
54 | heapsize=n; //全局变量,表示最大堆的大小
55 | for(int i = n/2; i > 1; i --){
56 | MaxheapFY(A, i);
57 | }
58 | }
59 |
60 | // heapsort
61 | void HeapSort(int *A,int n){
62 | BuildMaxHeap(A,n);//建立最大堆
63 | for(int i = n;i >= 1;i --){
64 | swap(A[0],A[i]);
65 | heapsize --;
66 | MaxheapFY(A, 1);
67 | }
68 | }
69 |
70 | // 维护位置i最大堆的性质
71 | void MaxheapFY(int *A, int i){
72 | int l,r,now;
73 | l = i * 2;
74 | r = i * 2 + 1;
75 | now = i;
76 | if(l <= heapsize && A[l] > A[now]) {
77 | now = l;
78 | }
79 | if(r <= heapsize && A[r] > A[now]){
80 | now = r;
81 | }
82 | if(now != i){
83 | swap(A[i], A[now]);
84 | MaxheapFY(A, now);
85 | }
86 | }
87 |
88 | ```
89 |
90 | ## 3. 归并排序
91 | > 题目: 手写归并排序
92 |
93 | ```
94 | template
95 | void Merge(T list[], int start, int mid, int end, bool (*cmp)(T, T)=cmp) {
96 | T *temp = new T[end-start+1];
97 | int i=start,j=mid+1,k=0;
98 | while(i<=mid && j<=end) {
99 | if(cmp(list[i],list[j])) temp[k++] = list[i++];
100 | else temp[k++] = list[j++];
101 | }
102 | while(i<=mid) {
103 | temp[k++] = list[i++];
104 | }
105 | while(j<=end) {
106 | temp[k++] = list[j++];
107 | }
108 | // copy
109 | for(i=start;i<=end;i++){
110 | list[i] = temp[i-start];
111 | }
112 | delete [] temp;
113 | }
114 |
115 | // MergeSortUtil
116 | template
117 | void MergeSortUtil(T list[], int start,int end, bool (*cmp)(T, T)=cmp) {
118 | if(start>=end) return;
119 | int mid = (start+end) / 2;
120 | MergeSortUtil(list,start,mid,cmp);
121 | MergeSortUtil(list,mid+1,end,cmp);
122 |
123 | Merge(list,start,mid,end,cmp);
124 | }
125 | ```
126 |
127 | ## 4. 实现多路归并排序
128 | > 题目: 实现常用的多路归并排序(使用最大堆,或者优先队列)
129 | > 公司: 百度,360
130 |
131 | ```
132 | // vec中每一个vector都是有序的
133 | vector MultMerge(vector > vec, vector &result) {
134 | int n = vec.size();
135 | priority_queue, greater > q;
136 | vector::iterator> vec_it;
137 | for(int i = 0; i < n; i ++) {
138 | vector::iterator it = vec[i].begin();
139 | vec_it.push_back(it);
140 | }
141 | for(int i = 0; i < n; i ++) {
142 | if(q.size() < k && vec_it[i] != vec[i].end()) {
143 | q.push(*(vec_it[i]));
144 | }
145 | }
146 | while(q.size()) {
147 | int cand = q.top();
148 | q.pop();
149 | result.push_back(cand);
150 | int index = 0;
151 | for(int i = 0; i < n; i ++) {
152 | if(vec_it[i] != vec[i].end() && cand == *(vec_it[i])) {
153 | index = i;
154 | vec_it[index] ++;
155 | break;
156 | }
157 | }
158 | if(vec_it[index] != vec[index].end()) {
159 | q.push(*(vec_it[index]));
160 | }
161 |
162 | }
163 | return result;
164 | }
165 | ```
166 |
167 | ## 5. 单链表插入排序
168 | > 题目: 单链表的插入排序(升序)。
169 | > 公司: 百度
170 |
171 | ```
172 | struct Node {
173 | int data;
174 | struct Node * next;
175 | };
176 |
177 | void InsertLinked(Node** sorted, Node* tmp) {
178 | Node* cur;
179 | // 当前插入节点是最小的值
180 | if(*sorted == NULL || tmp->data <= (*sorted)->data) {
181 | tmp->next = *sorted;
182 | *sorted = tmp;
183 | }
184 | else { // 找到插入的位置
185 | cur = *sorted;
186 | while(cur->next != NULL && tmp->data > cur->next->data) {
187 | cur = cur->next;
188 | }
189 | tmp->next = cur->next;
190 | cur->next = tmp;
191 | }
192 | }
193 |
194 | void InsertSort(Node** head) {
195 | // 有序链表
196 | Node *sorted = NULL;
197 | Node * cur = *head;
198 |
199 | while(cur != NULL) {
200 | Node *next = cur->next;
201 |
202 | // 将cur插入到sorted中,这是一个有序的链表
203 | InsertLinked(&sorted, cur);
204 |
205 | cur = next;
206 | }
207 | *head = sorted;
208 | }
209 |
210 | ```
211 |
212 | ## 6. 单链表归并排序
213 | > 题目: 单链表的归并排序。
214 | > 公司: 百度
215 |
216 | ```
217 | void MergeSort(Node **head_ref) {
218 | Node *head = *head_ref;
219 | Node *left;
220 | Node *right;
221 |
222 | // 判断是否是null
223 | if(head == NULL || head->next == NULL) {
224 | return;
225 | }
226 |
227 | // 链表分成两个部分,left 和 right
228 | split(head, &left, &right);
229 |
230 | MergeSort(left);
231 | MergeSort(right);
232 |
233 | *head_ref = Merge(left, right);
234 | }
235 |
236 | // 左右各一半,
237 | void split(Node *head, Node **left, Node **right) {
238 | //1. 先计算长度n,分别选择前一半和后一半。
239 | //2. 使用快慢指针,各取一半
240 | int n = 0;
241 | Node *cur = head;
242 | while(cur != NULL) {
243 | n ++;
244 | }
245 | *left = head;
246 |
247 | int k = n / 2;
248 | cur = head;
249 | Node *p = NULL;
250 | while(k--) {
251 | p = cur;
252 | cur = cur->next;
253 | }
254 | p->next = NULL;
255 |
256 | *right = cur;
257 | }
258 |
259 | Node* Merge(Node *left, Node *right) {
260 | // merge right to left
261 | Node *head = NULL;
262 | head->data = -1;
263 | Node *p = head;
264 |
265 | while(left != NULL && right != NULL) {
266 | if(left->data <= right->data) {
267 | p->next = left;
268 | left = left->next;
269 | }
270 | else {
271 | p->next = right;
272 | right = right->next;
273 | }
274 | p = p->next;
275 | }
276 |
277 | if(left != NULL) {
278 | p->next = left;
279 | }
280 | if(right != NULL) {
281 | p->next = right;
282 | }
283 |
284 | return head->next;
285 | }
286 | ```
287 |
288 |
--------------------------------------------------------------------------------
/模拟面试/readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/datawhalechina/daily-interview/a46161eb2274bf38fd383e374b7c07ba16b1041e/模拟面试/readme.md
--------------------------------------------------------------------------------
/计算机基础/操作系统.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 操作系统
4 |
5 | ## 知识体系
6 |
7 | ## Questions
8 |
9 | ### 1.进程和线程的区别
10 |
11 | - **进程**是系统进行资源分配和调度的基本单位;
12 | - **线程**是CPU调度和分派的基本单位。
13 | - 每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器,线程之间切换的开销小;
14 | - 一个进程至少有一个线程,线程依赖于进程而存在;
15 | - 每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行;
16 | - 多线程程序只要有一个线程崩溃,整个程序就崩溃了,但多进程程序中一个进程崩溃并不会对其它进程造成影响,因为进程有自己的独立地址空间,因此多进程更加健壮。
17 |
18 | ### 2.协程
19 |
20 | - **协程**:协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
21 |
22 | ### 3.进程的状态
23 |
24 | #### 三态模型
25 |
26 | * **运行**:当一个进程在处理机上运行时,则称该进程处于运行状态。处于此状态的进程的数目小于等于处理器的数目,对于单处理机系统,处于运行状态的进程只有一个。在没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。
27 | * **就绪**:当一个进程获得了除处理机以外的一切所需资源,一旦得到处理机即可运行,则称此进程处于就绪状态。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由I/O操作完成而进入就绪状态时,排入高优先级队列。
28 | * **阻塞**:一个进程正在等待某一事件发生(例如请求I/O而等待I/O完成等)而暂时停止运行,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。
29 |
30 | 
31 |
32 | #### 五态模型
33 |
34 | * **新建**:对应于进程被创建时的状态,尚未进入就绪队列。
35 | * **终止**:进程完成任务到达正常结束点,或出现无法克服的错误而异常终止,或被操作系统及有终止权的进程所终止时所处的状态。
36 |
37 | 
38 |
39 | ### 4.进程间通信方式
40 |
41 | * **匿名管道**:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
42 | * **高级管道**:将另一个程序当做一个新的进程在当前程序进程中启动,则它算是当前程序的子进程。
43 | * **有名管道**:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
44 |
45 | * **消息队列**:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
46 |
47 | * **信号量**:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
48 |
49 | * **信号**: 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
50 |
51 | * **共享内存**:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
52 |
53 | * **套接字**:套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
54 |
55 | ### 5.僵尸进程和孤儿进程
56 |
57 | * **僵尸进程**:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
58 | * **孤儿进程**:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
59 |
60 | ### 6.死锁
61 |
62 | * **死锁**:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
63 |
64 | #### 死锁产生的必要条件
65 |
66 | * **互斥条件**:一个资源每次只能被一个进程使用;
67 | * **请求与保持条件**:一个进程因请求资源而阻塞时,对已获得的资源保持不放;
68 | * **不剥夺条件**:进程已获得的资源,在未使用完之前,不能强行剥夺;
69 | * **循环等待条件**:若干进程之间形成一种头尾相接的循环等待资源关系。
70 |
71 | #### 死锁预防
72 |
73 | * 破坏互斥条件:允许某些资源同时被多个进程访问,但是有些资源本身并不具有这种属性;
74 | * 破坏请求与保持条件:
75 | * 实行资源预先分配策略(当一个进程开始运行之前,必须一次性向系统申请它所需要的全部资源,否则不运行);
76 | * 只允许进程在没有占用资源的时候才能申请资源(申请资源前先释放占有的资源);
77 | * 破坏不剥夺条件:允许进程强行抢占被其它进程占有的资源,这样做会降低系统性能;
78 | * 破坏循环等待条件:将系统中的所有资源统一编号,进程可在任何时刻提出资源申请,但所有申请必须按照资源的编号顺序(升序)提出。
79 |
80 | #### 死锁避免
81 |
82 | > 银行家算法
83 | >
84 | > 参考: [ 银行家算法](https://zh.wikipedia.org/wiki/%E9%93%B6%E8%A1%8C%E5%AE%B6%E7%AE%97%E6%B3%95)
85 |
86 | ### 7.页面置换算法
87 |
88 | * **最佳置换算法**(OPT):选择以后永不使用的或者是在最长时间内不再被访问的页面;
89 | * **先进先出置换算法**(FIFO):优先淘汰最早进入内存的页面,亦即在内存中驻留时间最久的页面;
90 | * **最近最久未使用置换算法**(LRU):置换出未使用时间最长的页面;
91 | * **第二次机会算法**(SCR):按FIFO选择某一页面,若其访问位为1,给第二次机会,并将访问位置0;
92 | * **时钟算法**(CLOCK):SCR中需要将页面在链表中移动(第二次机会的时候要将这个页面从链表头移到链表尾),时钟算法使用环形链表,再使用一个指针指向最老的页面,避免了移动页面的开销。
93 |
94 | * 注:[LRU算法题](https://leetcode-cn.com/problems/lru-cache/)
95 |
96 | ### 8.分页和分段的区别
97 |
98 | * 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的 ;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的;
99 | * 段的大小不固定,由它所完成的功能决定;页的大小固定,由系统决定;
100 | * 段向用户提供二维地址空间;页向用户提供的是一维地址空间;
101 | * 段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。
102 |
103 | ### 9.硬中断和软中断
104 |
105 | **硬中断**是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。
106 |
107 | 处理中断的驱动是需要运行在CPU上的,因此,当中断产生的时候,CPU会中断当前正在运行的任务,来处理中断。在有多核心的系统上,一个中断通常只能中断一颗CPU(也有一种特殊的情况,就是在大型主机上是有硬件通道的,它可以在没有主CPU的支持下,可以同时处理多个中断)。
108 |
109 | **硬中断**可以直接中断CPU。它会引起内核中相关的代码被触发。对于那些需要花费一些时间去处理的进程,中断代码本身也可以被其他的硬中断中断。
110 |
111 | **软中断**的处理非常像硬中断。然而,它们仅仅是由当前正在运行的进程所产生的。通常,软中断是一些对I/O的请求。这些请求会调用内核中可以调度I/O发生的程序。对于某些设备,I/O请求需要被立即处理,而磁盘I/O请求通常可以排队并且可以稍后处理。根据I/O模型的不同,进程或许会被挂起直到I/O完成,此时内核调度器就会选择另一个进程去运行。I/O可以在进程之间产生并且调度过程通常和磁盘I/O的方式是相同。
112 |
113 | **软中断**仅与**内核**相联系。而内核主要负责对需要运行的任何其他的进程进行调度。一些内核允许设备驱动的一些部分存在于用户空间,并且当需要的时候内核也会调度这个进程去运行。
114 |
115 | **软中断**并不会直接中断CPU。也只有当前正在运行的代码(或进程)才会产生软中断。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。有一个特殊的软中断是Yield调用,它的作用是请求内核调度器去查看是否有一些其他的进程可以运行。
116 |
117 |
118 |
119 | ### 10.IO模型
120 |
121 | * **阻塞式 I/O**:应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回;
122 |
123 | * **非阻塞式 I/O**:应用进程可以继续执行,但是需要不断地执行系统调用来获知 I/O 是否完成,这种方式称为轮询;
124 |
125 | * **I/O 复用**:单个进程具有处理多个 I/O 事件的能力;
126 |
127 | * **select**:将文件描述符放入一个集合中,调用select时,将这个集合从用户空间拷贝到内核空间(缺点1:每次都要复制,**开销大**),由内核根据就绪状态修改该集合的内容。(缺点2)**集合大小有限制**,32位机默认是1024(64位:2048);采用水平触发机制。select函数返回后,需要通过遍历这个集合,找到就绪的文件描述符(缺点3:**轮询的方式效率较低**),当文件描述符的数量增加时,效率会线性下降;
128 |
129 | 默认单个进程打开的FD有限制是1024个,可修改宏定义,但是效率仍然慢。
130 |
131 | * **poll**:基本原理与select一致,也是轮询+遍历;唯一的区别就是**poll**采用链表的方式存储,没有最大文件描述符限制。
132 |
133 | * **epoll**:通过内核和用户空间共享内存,避免了不断复制的问题;支持的同时连接数上限很高(1G左右的内存支持10W左右的连接数);文件描述符就绪时,采用回调机制,避免了轮询(回调函数将就绪的描述符添加到一个链表中,执行epoll_wait时,返回这个链表);支持水平触发和边缘触发,采用边缘触发机制时,只有活跃的描述符才会触发回调函数。
134 |
135 | * **信号驱动式 I/O**:内核在数据到达时向应用进程发送 SIGIO 信号;
136 |
137 | * 异步 I/O:内核完成所有操作后向应用进程发送信号。
138 |
139 |
140 |
141 | ## 参考链接
142 |
143 | * https://github.com/wolverinn/Waking-Up#2-%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F
144 | * http://www.linuxidc.com/Linux/2014-03/98013.htm
145 | * https://blog.csdn.net/violet_echo_0908/article/details/51201278
146 | * https://www.cnblogs.com/wlwl/p/10293057.html
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/计算机基础/数据库.md:
--------------------------------------------------------------------------------
1 | # 数据库
2 |
3 | ## 1. 事务四大特性
4 |
5 | 1. 原子性,要么执行,要么不执行
6 | 2. 隔离性,所有操作全部执行完以前其它会话不能看到过程
7 | 3. 一致性,事务前后,数据总额一致
8 | 4. 持久性,一旦事务提交,对数据的改变就是永久的
9 |
10 |
11 | ## 2. 数据库模型编辑
12 |
13 | 1. 对象模型
14 | 2. 层次模型(轻量级数据访问协议)
15 | 3. 网状模型(大型数据储存)
16 | 4. 关系模型
17 | 5. 面向对象模型
18 | 6. 半结构化模型
19 | 7. 平面模型(表格模型,一般在形式上是一个二维数组。如表格模型数据Excel)
20 |
21 |
22 | ## 3. 数据库三范式
23 |
24 | 第一范式:列不可再分
25 | 第二范式:行可以唯一区分,主键约束
26 | 第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束 且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上
27 |
28 |
29 | ## 4. 关系型数据库和非关系型数据库
30 | + **关系数据库**,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。数据库事务必须具备ACID特性,ACID分别是Atomic原子性,Consistency一致性,Isolation隔离性,Durability持久性。代表数据库:Oracle、Mysql、DB2等。
31 |
32 | + **关系型数据库的优点**
33 | 1. 容易理解:二维表结构表示逻辑世界的相对的概念,直观明了
34 | 2. 使用方便:使用SQl语句查询想要的一切
35 | 3. 易于维护:遵循数据库的设计原则,降低数据的冗余性
36 |
37 |
38 | + **非关系型数据库**,又被称为NoSQL(Not Only SQL),意为不仅仅是SQL,主要是指非关系型、分布式、不提供ACID (数据库事务处理的四个基本要素)的数据库设计模式。对NoSQL 最普遍的定义是“非关联型的”,强调Key-Value 存储和文档数据库的优点。代表数据库:MongoDB、Redis等
39 |
40 | + **非关系型数据库的有点**
41 | 1. 将所有数据看做key-value对,根据key值得到想要的一切数据
42 | 2. 适用于SNS(Social Networking Services)中,例如facebook,微博。系统的升级,功能的增加,往往意味着数据结构巨大变动,key值不变,在value中加入需要的字段,一般value的格式是json或者文本等。
43 |
44 |
45 | **待续...**
46 |
47 |
48 | # 参考
49 | 1. https://blog.csdn.net/qq_22222499/article/details/79060495#8BB_54
50 | 2. https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93/103728?fr=aladdin
51 | 3. https://www.2cto.com/database/201710/688377.html
--------------------------------------------------------------------------------