Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
wushizhenking
CS-Notes
提交
f8600a46
C
CS-Notes
项目概览
wushizhenking
/
CS-Notes
与 Fork 源项目一致
从无法访问的项目Fork
通知
2
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
C
CS-Notes
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f8600a46
编写于
7月 30, 2018
作者:
C
CyC2018
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
auto commit
上级
95a780d0
变更
4
显示空白变更内容
内联
并排
Showing
4 changed file
with
141 addition
and
12 deletion
+141
-12
README.md
README.md
+6
-2
notes/剑指 offer 题解.md
notes/剑指 offer 题解.md
+31
-10
notes/攻击技术.md
notes/攻击技术.md
+0
-0
notes/系统设计基础.md
notes/系统设计基础.md
+104
-0
未找到文件。
README.md
浏览文件 @
f8600a46
...
@@ -105,6 +105,10 @@ Leetcode 上数据库题目的解题记录。
...
@@ -105,6 +105,10 @@ Leetcode 上数据库题目的解题记录。
## 系统设计 :bulb:
## 系统设计 :bulb:
> [系统设计基础](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/系统设计基础.md)
性能、伸缩性、扩展性、可用性、安全性
> [分布式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/分布式.md)
> [分布式](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/分布式.md)
分布式锁、分布式事务、CAP、BASE、Paxos、Raft
分布式锁、分布式事务、CAP、BASE、Paxos、Raft
...
@@ -113,9 +117,9 @@ Leetcode 上数据库题目的解题记录。
...
@@ -113,9 +117,9 @@ Leetcode 上数据库题目的解题记录。
负载均衡、Session 管理
负载均衡、Session 管理
> [
安全性](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/安全性
.md)
> [
攻击技术](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/攻击技术
.md)
XSS、CSRF、SQL 注入、DDo
s
XSS、CSRF、SQL 注入、DDo
S
> [消息队列](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/消息队列.md)
> [消息队列](https://github.com/CyC2018/InnterviewNotes/blob/master/notes/消息队列.md)
...
...
notes/剑指 offer 题解.md
浏览文件 @
f8600a46
<!-- GFM-TOC -->
<!-- GFM-TOC -->
*
[
1. 前言
](
#1-前言
)
*
[
2. 实现 Singleton
](
#2-实现-singleton
)
*
[
2. 实现 Singleton
](
#2-实现-singleton
)
*
[
3. 数组中重复的数字
](
#3-数组中重复的数字
)
*
[
3. 数组中重复的数字
](
#3-数组中重复的数字
)
*
[
4. 二维数组中的查找
](
#4-二维数组中的查找
)
*
[
4. 二维数组中的查找
](
#4-二维数组中的查找
)
...
@@ -80,6 +81,13 @@
...
@@ -80,6 +81,13 @@
<!-- GFM-TOC -->
<!-- GFM-TOC -->
# 1. 前言
本文的绘图可通过以下途径免费获得并使用:
-
[
ProcessOn
](
https://www.processon.com/view/5a3e4c7be4b0909c1aa18b49
)
-
[
DrawIO
](
https://drive.google.com/file/d/1nSSCpPUC05MFoeFuf_aeTtkm7dG5-bJ1/view?usp=sharing
)
# 2. 实现 Singleton
# 2. 实现 Singleton
[
单例模式
](
https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md
)
[
单例模式
](
https://github.com/CyC2018/Interview-Notebook/blob/master/notes/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.md
)
...
@@ -170,7 +178,7 @@ Given target = 20, return false.
...
@@ -170,7 +178,7 @@ Given target = 20, return false.
当前元素的查找区间为左下角的所有元素,例如元素 12 的查找区间如下:
当前元素的查找区间为左下角的所有元素,例如元素 12 的查找区间如下:
<div
align=
"center"
>
<img
src=
"../pics//
026d3cb4-67f7-4a83-884d-8032f57ec446
.png"
width=
"250"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
f94389e9-55b1-4f49-9d37-00ed05900ae0
.png"
width=
"250"
/>
</div><br>
复杂度:O(M + N) + O(1)
复杂度:O(M + N) + O(1)
...
@@ -456,7 +464,7 @@ public int pop() throws Exception {
...
@@ -456,7 +464,7 @@ public int pop() throws Exception {
如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。
如果使用递归求解,会重复计算一些子问题。例如,计算 f(10) 需要计算 f(9) 和 f(8),计算 f(9) 需要计算 f(8) 和 f(7),可以看到 f(8) 被重复计算了。
<div align="
center
"
>
<img
src=
"../pics//
a0df8edc-581b-4977-95c2-d7025795b899.pn
g"
width=
"300"
/>
</div><br>
<div align="
center
"
>
<img
src=
"../pics//
faecea49-9974-40db-9821-c8636137df61.jp
g"
width=
"300"
/>
</div><br>
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
...
@@ -941,11 +949,11 @@ private void printNumber(char[] number) {
...
@@ -941,11 +949,11 @@ private void printNumber(char[] number) {
① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。
① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。
<div
align=
"center"
>
<img
src=
"../pics//
41392d76-dd1d-4712-85d9-e8bb46b04a2d
.png"
width=
"600"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
27ff9548-edb6-4465-92c8-7e6386e0b185
.png"
width=
"600"
/>
</div><br>
② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。
② 否则,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。
<div
align=
"center"
>
<img
src=
"../pics//
db4921d4-184b-48ba-a3cf-1d1141e3ba2d
.png"
width=
"600"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
280f7728-594f-4811-a03a-fa8d32c013da
.png"
width=
"600"
/>
</div><br>
综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N
\~
2,因此该算法的平均时间复杂度为 O(1)。
综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1,其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数,N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N
\~
2,因此该算法的平均时间复杂度为 O(1)。
...
@@ -1103,7 +1111,7 @@ public void reOrderArray(int[] nums) {
...
@@ -1103,7 +1111,7 @@ public void reOrderArray(int[] nums) {
设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。
设链表的长度为 N。设两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到 N - K 个节点处,该位置就是倒数第 K 个节点。
<div
align=
"center"
>
<img
src=
"../pics//
207c1801-2335-4b1b-b65c-126a0ba966cb
.png"
width=
"500"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
ea2304ce-268b-4238-9486-4d8f8aea8ca4
.png"
width=
"500"
/>
</div><br>
```
java
```
java
public
ListNode
FindKthToTail
(
ListNode
head
,
int
k
)
public
ListNode
FindKthToTail
(
ListNode
head
,
int
k
)
...
@@ -1140,7 +1148,7 @@ public ListNode FindKthToTail(ListNode head, int k)
...
@@ -1140,7 +1148,7 @@ public ListNode FindKthToTail(ListNode head, int k)
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
在相遇点,slow 要到环的入口点还需要移动 z 个节点,如果让 fast 重新从头开始移动,并且速度变为每次移动一个节点,那么它到环入口点还需要移动 x 个节点。在上面已经推导出 x=z,因此 fast 和 slow 将在环入口点相遇。
<div
align=
"center"
>
<img
src=
"../pics//
71363383-2d06-4c63-8b72-c01c2186707d.pn
g"
width=
"600"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
2858f8ad-aedb-45a5-a706-e98c96d690fa.jp
g"
width=
"600"
/>
</div><br>
```
java
```
java
public
ListNode
EntryNodeOfLoop
(
ListNode
pHead
)
public
ListNode
EntryNodeOfLoop
(
ListNode
pHead
)
...
@@ -1346,7 +1354,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2)
...
@@ -1346,7 +1354,7 @@ boolean isSymmetrical(TreeNode t1, TreeNode t2)
下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
下图的矩阵顺时针打印结果为:1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10
<div
align=
"center"
>
<img
src=
"../pics//
0f373947-c68f-45b4-a59e-086154745ac5
.png"
width=
"300"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//
6539b9a4-2b24-4d10-8c94-2eb5aba1e296
.png"
width=
"300"
/>
</div><br>
## 解题思路
## 解题思路
...
@@ -1632,6 +1640,19 @@ private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
...
@@ -1632,6 +1640,19 @@ private void backtracking(TreeNode node, int target, ArrayList<Integer> path)
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。
```
java
public
class
RandomListNode
{
int
label
;
RandomListNode
next
=
null
;
RandomListNode
random
=
null
;
RandomListNode
(
int
label
)
{
this
.
label
=
label
;
}
}
```
<div
align=
"center"
>
<img
src=
"../pics//a01d1516-8168-461a-a24b-620b9cfc40f4.png"
width=
"300"
/>
</div><br>
<div
align=
"center"
>
<img
src=
"../pics//a01d1516-8168-461a-a24b-620b9cfc40f4.png"
width=
"300"
/>
</div><br>
## 解题思路
## 解题思路
...
@@ -1812,7 +1833,7 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
...
@@ -1812,7 +1833,7 @@ private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s)
多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。
多数投票问题,可以利用 Boyer-Moore Majority Vote Algorithm 来解决这个问题,使得时间复杂度为 O(N)。
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0
,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
使用 cnt 来统计一个元素出现的次数,当遍历到的元素和统计元素不相等时,令 cnt--。如果前面查找了 i 个元素,且 cnt == 0,说明前 i 个元素没有 majority,或者有 majority,但是出现的次数少于 i / 2 ,因为如果多于 i / 2 的话 cnt 就一定不会为 0 。此时剩下的 n - i 个元素中,majority 的数目依然多于 (n - i) / 2,因此继续查找就能找出 majority。
```
java
```
java
public
int
MoreThanHalfNum_Solution
(
int
[]
nums
)
public
int
MoreThanHalfNum_Solution
(
int
[]
nums
)
...
@@ -1916,7 +1937,7 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
...
@@ -1916,7 +1937,7 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k)
if
(
maxHeap
.
size
()
>
k
)
if
(
maxHeap
.
size
()
>
k
)
maxHeap
.
poll
();
maxHeap
.
poll
();
}
}
return
new
ArrayList
<>(
maxHeap
)
;
return
new
ArrayList
<>(
maxHeap
);
}
}
```
```
...
@@ -1997,7 +2018,7 @@ public char FirstAppearingOnce()
...
@@ -1997,7 +2018,7 @@ public char FirstAppearingOnce()
## 题目描述
## 题目描述
{6,
-3,-2,7,-15,1,2,
2},连续子数组的最大和为 8(从第 0 个开始,到第 3 个为止)。
{6,
-3, -2, 7, -15, 1, 2,
2},连续子数组的最大和为 8(从第 0 个开始,到第 3 个为止)。
## 解题思路
## 解题思路
...
...
notes/
安全性
.md
→
notes/
攻击技术
.md
浏览文件 @
f8600a46
文件已移动
notes/系统设计基础.md
0 → 100644
浏览文件 @
f8600a46
<!-- GFM-TOC -->
*
[
一、性能
](
#一性能
)
*
[
二、伸缩性
](
#二伸缩性
)
*
[
三、扩展性
](
#三扩展性
)
*
[
四、可用性
](
#四可用性
)
*
[
五、安全性
](
#五安全性
)
<!-- GFM-TOC -->
# 一、性能
## 性能指标
### 1. 响应时间
指从某个请求发出到接收到响应消耗的时间。
在对响应时间进行测试时,通常采用重复请求方式,然后计算平均响应时间。
### 2. 吞吐量
指系统在单位时间内可以处理的请求数量,通常使用每秒的请求数来衡量。
### 3. 并发用户数
指系统能同时处理的并发用户请求数量。
在没有并发存在的系统中,请求被顺序执行,此时响应时间为吞吐量的倒数。例如系统支持的吞吐量为 100 req/s,那么平均响应时间应该为 0.01s。
目前的大型系统都支持多线程来处理并发请求,多线程能够提高吞吐量以及缩短响应时间,主要有两个原因:
-
多 CPU
-
IO 等待时间
使用 IO 多路复用等方式,系统在等待一个 IO 操作完成的这段时间内不需要被阻塞,可以去处理其它请求。通过将这个等待时间利用起来,使得 CPU 利用率大大提高。
并发用户数不是越高越好,因为如果并发用户数太高,系统来不及处理这么多的请求,会使得过多的请求需要等待,那么响应时间就会大大提高。
## 性能优化
### 1. 集群
将多台服务器组成集群,使用负载均衡将请求转发到集群中,避免单一服务器的负载压力过大导致性能降低。
### 2. 缓存
缓存能够提高性能的原因如下:
-
缓存数据通常位于内存等介质中,这种介质对于读操作特别快;
-
缓存数据可以位于靠近用户的地理位置上;
-
可以将计算结果进行缓存,从而避免重复计算。
### 3. 异步
某些流程可以将操作转换为消息,将消息发送到消息队列之后立即返回,之后这个操作会被异步处理。
# 二、伸缩性
指不断向集群中添加服务器来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。
## 伸缩性与性能
如果系统存在性能问题,那么单个用户的请求总是很慢的;
如果系统存在伸缩性问题,那么单个用户的请求可能会很快,但是在并发数很高的情况下系统会很慢。
## 实现伸缩性
应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。
关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。
对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。
# 三、扩展性
指的是添加新功能时对现有系统的其它应用无影响,这就要求不同应用具备低耦合的特点。
实现可扩展主要有两种方式:
-
使用消息队列进行解耦,应用之间通过消息传递的方式进行通信;
-
使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务器框架调用可复用的服务。新增的产品可以用过调用可复用的服务来实现业务逻辑,对其它产品没有影响。
# 四、可用性
## 冗余
保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。
应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。
存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。
## 监控
对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个数据达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。
## 服务降级
服务器降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。
# 五、安全性
要求系统的应对各种攻击手段时能够有可靠的应对措施。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录