auto commit
This commit is contained in:
parent
8a77f76a2c
commit
41176bb717
|
@ -8,6 +8,7 @@
|
|||
* [三、源码分析](#三源码分析)
|
||||
* [ArrayList](#arraylist)
|
||||
* [Vector](#vector)
|
||||
* [CopyOnWriteArrayList](#copyonwritearraylist)
|
||||
* [LinkedList](#linkedlist)
|
||||
* [HashMap](#hashmap)
|
||||
* [ConcurrentHashMap](#concurrenthashmap)
|
||||
|
@ -25,23 +26,23 @@
|
|||
|
||||
### 1. Set
|
||||
|
||||
- HashSet:基于哈希实现,支持快速查找,但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的;
|
||||
- HashSet:基于哈希实现,支持快速查找,但不支持有序性操作,例如根据一个范围查找元素的操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
|
||||
|
||||
- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN);
|
||||
- TreeSet:基于红黑树实现,支持有序性操作,但是查找效率不如 HashSet,HashSet 查找时间复杂度为 O(1),TreeSet 则为 O(logN)。
|
||||
|
||||
- LinkedHashSet:具有 HashSet 的查找效率,且内部使用链表维护元素的插入顺序。
|
||||
|
||||
### 2. List
|
||||
|
||||
- ArrayList:基于动态数组实现,支持随机访问;
|
||||
- ArrayList:基于动态数组实现,支持随机访问。
|
||||
|
||||
- Vector:和 ArrayList 类似,但它是线程安全的;
|
||||
- Vector:和 ArrayList 类似,但它是线程安全的。
|
||||
|
||||
- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
|
||||
|
||||
### 3. Queue
|
||||
|
||||
- LinkedList:可以用它来支持双向队列;
|
||||
- LinkedList:可以用它来实现双向队列。
|
||||
|
||||
- PriorityQueue:基于堆结构实现,可以用它来实现优先队列。
|
||||
|
||||
|
@ -85,14 +86,14 @@ java.util.Arrays#asList() 可以把数组类型转换为 List 类型。
|
|||
public static <T> List<T> asList(T... a)
|
||||
```
|
||||
|
||||
如果要将数组类型转换为 List 类型,应该注意的是 asList() 的参数为泛型的变长参数,因此不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
|
||||
应该注意的是 asList() 的参数为泛型的变长参数,不能使用基本类型数组作为参数,只能使用相应的包装类型数组。
|
||||
|
||||
```java
|
||||
Integer[] arr = {1, 2, 3};
|
||||
List list = Arrays.asList(arr);
|
||||
```
|
||||
|
||||
也可以使用以下方式生成 List。
|
||||
也可以使用以下方式使用 asList():
|
||||
|
||||
```java
|
||||
List list = Arrays.asList(1,2,3);
|
||||
|
@ -235,12 +236,12 @@ public synchronized E get(int index) {
|
|||
}
|
||||
```
|
||||
|
||||
### 2. ArrayList 与 Vector
|
||||
### 2. 与 ArrayList 的区别
|
||||
|
||||
- Vector 是同步的,因此开销就比 ArrayList 要大,访问速度更慢。最好使用 ArrayList 而不是 Vector,因为同步操作完全可以由程序员自己来控制;
|
||||
- Vector 每次扩容请求其大小的 2 倍空间,而 ArrayList 是 1.5 倍。
|
||||
|
||||
### 3. Vector 替代方案
|
||||
### 3. 替代方案
|
||||
|
||||
为了获得线程安全的 ArrayList,可以使用 `Collections.synchronizedList();` 得到一个线程安全的 ArrayList。
|
||||
|
||||
|
@ -255,7 +256,15 @@ List<String> synList = Collections.synchronizedList(list);
|
|||
List<String> list = new CopyOnWriteArrayList<>();
|
||||
```
|
||||
|
||||
CopyOnWriteArrayList 是一种 CopyOnWrite 容器,从以下源码看出:读取元素是从原数组读取;添加元素是在复制的新数组上。读写分离,因而可以在并发条件下进行不加锁的读取,读取效率高,适用于读操作远大于写操作的场景。
|
||||
## CopyOnWriteArrayList
|
||||
|
||||
### 读写分离
|
||||
|
||||
写操作在一个复制的数组上进行,读操作还是在原始数组中进行,读写分离,互不影响。
|
||||
|
||||
写操作需要加锁,防止同时并发写入时导致的写入数据丢失。
|
||||
|
||||
写操作结束之后需要把原始数组指向新的复制数组。
|
||||
|
||||
```java
|
||||
public boolean add(E e) {
|
||||
|
@ -264,7 +273,7 @@ public boolean add(E e) {
|
|||
try {
|
||||
Object[] elements = getArray();
|
||||
int len = elements.length;
|
||||
Object[] newElements = Arrays.copyOf(elements, len + 1);
|
||||
Object[] newElements = Arrays.copyOf(elements, len + 1);
|
||||
newElements[len] = e;
|
||||
setArray(newElements);
|
||||
return true;
|
||||
|
@ -276,13 +285,26 @@ public boolean add(E e) {
|
|||
final void setArray(Object[] a) {
|
||||
array = a;
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
@SuppressWarnings("unchecked")
|
||||
private E get(Object[] a, int index) {
|
||||
return (E) a[index];
|
||||
}
|
||||
```
|
||||
|
||||
### 适用场景
|
||||
|
||||
CopyOnWriteArrayList 在写操作的同时允许读操作,大大提高了读操作的性能,因此很适合读多写少的应用场景。
|
||||
|
||||
但是 CopyOnWriteArrayList 有其缺陷:
|
||||
|
||||
- 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
|
||||
- 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。
|
||||
|
||||
所以 CopyOnWriteArrayList 不适合内存敏感以及对实时性要求很高的场景。
|
||||
|
||||
## LinkedList
|
||||
|
||||
### 1. 概览
|
||||
|
|
|
@ -871,7 +871,9 @@ $ ls -al /etc | less
|
|||
|
||||
## 提取指令
|
||||
|
||||
cut 对数据进行切分,取出想要的部分。切分过程一行一行地进行。
|
||||
cut 对数据进行切分,取出想要的部分。
|
||||
|
||||
切分过程一行一行地进行。
|
||||
|
||||
```html
|
||||
$ cut
|
||||
|
@ -891,7 +893,7 @@ root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16)
|
|||
$ last | cut -d ' ' -f 1
|
||||
```
|
||||
|
||||
示例 2:将 export 输出的讯息,取出第 12 字符以后的所有字符串。
|
||||
示例 2:将 export 输出的信息,取出第 12 字符以后的所有字符串。
|
||||
|
||||
```html
|
||||
$ export
|
||||
|
@ -906,7 +908,7 @@ $ export | cut -c 12
|
|||
|
||||
## 排序指令
|
||||
|
||||
**sort** 进行排序。
|
||||
**sort** 用于排序。
|
||||
|
||||
```html
|
||||
$ sort [-fbMnrtuk] [file or stdin]
|
||||
|
@ -1204,7 +1206,7 @@ pid_t wait(int *status)
|
|||
|
||||
如果成功,返回被收集的子进程的进程 ID;如果调用进程没有子进程,调用就会失败,此时返回 -1,同时 errno 被置为 ECHILD。
|
||||
|
||||
参数 status 用来保存被收集的子进程退出时的一些状态,如果我们对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL:
|
||||
参数 status 用来保存被收集的子进程退出时的一些状态,如果对这个子进程是如何死掉的毫不在意,只想把这个子进程消灭掉,可以设置这个参数为 NULL:
|
||||
|
||||
```c
|
||||
pid = wait(NULL);
|
||||
|
@ -1218,7 +1220,7 @@ pid_t waitpid(pid_t pid, int *status, int options)
|
|||
|
||||
作用和 wait() 完全相同,但是多了两个可由用户控制的参数 pid 和 options。
|
||||
|
||||
pid 参数指示一个子进程的 ID,表示只关心这个子进程的退出 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号。
|
||||
pid 参数指示一个子进程的 ID,表示只关心这个子进程退出的 SIGCHLD 信号。如果 pid=-1 时,那么和 wait() 作用相同,都是关心所有子进程退出的 SIGCHLD 信号。
|
||||
|
||||
options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 waitpid() 调用变成非阻塞的,也就是说它会立即返回,父进程可以继续执行其它任务。
|
||||
|
||||
|
@ -1238,7 +1240,7 @@ options 参数主要有 WNOHANG 和 WUNTRACED 两个选项,WNOHANG 可以使 w
|
|||
|
||||
系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
|
||||
|
||||
要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时所有的僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵死进程所占有的资源,从而结束僵尸进程。
|
||||
要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 所收养,这样 init 就会释放所有的僵死进程所占有的资源,从而结束僵尸进程。
|
||||
|
||||
# 参考资料
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ ACID 要求强一致性,通常运用在传统的数据库系统上。而 BASE
|
|||
|
||||
主要用于实现分布式事务,分布式事务指的是事务操作跨越多个节点,并且要求满足事务的 ACID 特性。
|
||||
|
||||
通过引入协调者(Coordinator)来调度参与者的行为,,并最终决定这些参与者是否要真正执行事务。
|
||||
通过引入协调者(Coordinator)来调度参与者的行为,并最终决定这些参与者是否要真正执行事务。
|
||||
|
||||
## 运行过程
|
||||
|
||||
|
|
|
@ -216,7 +216,9 @@ HTTP 重定向负载均衡服务器收到 HTTP 请求之后会返回服务器的
|
|||
|
||||
### 2. DNS 重定向
|
||||
|
||||
使用 DNS 作为负载均衡器,根据负载情况返回不同服务器的 IP 地址。大型网站基本使用了这种方式做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。
|
||||
使用 DNS 作为负载均衡器,根据负载情况返回不同服务器的 IP 地址。
|
||||
|
||||
大型网站基本使用了这种方式做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。
|
||||
|
||||
缺点:
|
||||
|
||||
|
|
|
@ -90,12 +90,20 @@
|
|||
|
||||
## 题目描述
|
||||
|
||||
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。例如,如果输入长度为 7 的数组 {2, 3, 1, 0, 2, 5},那么对应的输出是第一个重复的数字 2。
|
||||
在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
|
||||
|
||||
要求复杂度为 O(N) + O(1),也就是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。牛客网讨论区这一题的首票答案使用 nums[i] + length 来将元素标记,这么做会有加法溢出问题。
|
||||
```html
|
||||
Input:
|
||||
{2, 3, 1, 0, 2, 5}
|
||||
|
||||
Output:
|
||||
2
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
要求复杂度为 O(N) + O(1),也就是时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。牛客网讨论区这一题的首票答案使用 nums[i] + length 来将元素标记,这么做会有加法溢出问题。
|
||||
|
||||
这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素放到第 i 个位置上。
|
||||
|
||||
以 (2, 3, 1, 0, 2, 5) 为例:
|
||||
|
@ -158,7 +166,11 @@ Given target = 20, return false.
|
|||
|
||||
## 解题思路
|
||||
|
||||
从右上角开始查找。因为矩阵中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间。
|
||||
从右上角开始查找。矩阵中的一个数,它左边的数都比它小,下边的数都比它大。因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间。
|
||||
|
||||
当前元素的查找区间为左下角的所有元素,例如元素 12 的查找区间如下:
|
||||
|
||||
<div align="center"> <img src="../pics//026d3cb4-67f7-4a83-884d-8032f57ec446.png" width="250"/> </div><br>
|
||||
|
||||
复杂度:O(M + N) + O(1)
|
||||
|
||||
|
@ -261,25 +273,14 @@ public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
|||
}
|
||||
```
|
||||
|
||||
### 使用 Collections.reverse()
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
while (listNode != null) {
|
||||
ret.add(listNode.val);
|
||||
listNode = listNode.next;
|
||||
}
|
||||
Collections.reverse(ret);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
### 使用头插法
|
||||
|
||||
利用链表头插法为逆序的特点。
|
||||
|
||||
头结点和第一个节点的区别:头结点是在头插法中使用的一个额外节点,这个节点不存储值;第一个节点就是链表的第一个真正存储值的节点。
|
||||
头结点和第一个节点的区别:
|
||||
|
||||
- 头结点是在头插法中使用的一个额外节点,这个节点不存储值;
|
||||
- 第一个节点就是链表的第一个真正存储值的节点。
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
|
@ -302,6 +303,20 @@ public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
|||
}
|
||||
```
|
||||
|
||||
### 使用 Collections.reverse()
|
||||
|
||||
```java
|
||||
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
|
||||
ArrayList<Integer> ret = new ArrayList<>();
|
||||
while (listNode != null) {
|
||||
ret.add(listNode.val);
|
||||
listNode = listNode.next;
|
||||
}
|
||||
Collections.reverse(ret);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
# 7. 重建二叉树
|
||||
|
||||
[NowCoder](https://www.nowcoder.com/practice/8a19cbe657394eeaac2f6ea9b0f6fcf6?tpId=13&tqId=11157&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
|
||||
|
@ -322,22 +337,23 @@ inorder = [9,3,15,20,7]
|
|||
前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果。
|
||||
|
||||
```java
|
||||
private Map<Integer, Integer> inOrderNumsIndexs = new HashMap<>(); // 缓存中序遍历数组的每个值对应的索引
|
||||
// 缓存中序遍历数组的每个值对应的索引
|
||||
private Map<Integer, Integer> inOrderNumsIndexs = new HashMap<>();
|
||||
|
||||
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
|
||||
for (int i = 0; i < in.length; i++)
|
||||
inOrderNumsIndexs.put(in[i], i);
|
||||
return reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
|
||||
return reConstructBinaryTree(pre, 0, pre.length - 1, 0, in.length - 1);
|
||||
}
|
||||
|
||||
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in, int inL, int inR) {
|
||||
private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int inL, int inR) {
|
||||
if (preL > preR)
|
||||
return null;
|
||||
TreeNode root = new TreeNode(pre[preL]);
|
||||
int inIndex = inOrderNumsIndexs.get(root.val);
|
||||
int leftTreeSize = inIndex - inL;
|
||||
root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, in, inL, inL + leftTreeSize - 1);
|
||||
root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, in, inL + leftTreeSize + 1, inR);
|
||||
root.left = reConstructBinaryTree(pre, preL + 1, preL + leftTreeSize, inL, inL + leftTreeSize - 1);
|
||||
root.right = reConstructBinaryTree(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1, inR);
|
||||
return root;
|
||||
}
|
||||
```
|
||||
|
@ -350,16 +366,6 @@ private TreeNode reConstructBinaryTree(int[] pre, int preL, int preR, int[] in,
|
|||
|
||||
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
|
||||
|
||||
## 解题思路
|
||||
|
||||
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
|
||||
|
||||
<div align="center"> <img src="../pics//cb0ed469-27ab-471b-a830-648b279103c8.png" width="250"/> </div><br>
|
||||
|
||||
② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
|
||||
|
||||
<div align="center"> <img src="../pics//e143f6da-d114-4ba4-8712-f65299047fa2.png" width="250"/> </div><br>
|
||||
|
||||
```java
|
||||
public class TreeLinkNode {
|
||||
int val;
|
||||
|
@ -373,6 +379,16 @@ public class TreeLinkNode {
|
|||
}
|
||||
```
|
||||
|
||||
## 解题思路
|
||||
|
||||
① 如果一个节点的右子树不为空,那么该节点的下一个节点是右子树的最左节点;
|
||||
|
||||
<div align="center"> <img src="../pics//cb0ed469-27ab-471b-a830-648b279103c8.png" width="250"/> </div><br>
|
||||
|
||||
② 否则,向上找第一个左链接指向的树包含该节点的祖先节点。
|
||||
|
||||
<div align="center"> <img src="../pics//e143f6da-d114-4ba4-8712-f65299047fa2.png" width="250"/> </div><br>
|
||||
|
||||
```java
|
||||
public TreeLinkNode GetNext(TreeLinkNode pNode) {
|
||||
if (pNode.right != null) {
|
||||
|
@ -398,7 +414,7 @@ public TreeLinkNode GetNext(TreeLinkNode pNode) {
|
|||
|
||||
## 题目描述
|
||||
|
||||
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。队列中的元素为 int 类型。
|
||||
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。
|
||||
|
||||
## 解题思路
|
||||
|
||||
|
@ -442,7 +458,7 @@ public int pop() throws Exception {
|
|||
|
||||
<div align="center"> <img src="../pics//a0df8edc-581b-4977-95c2-d7025795b899.png" width="300"/> </div><br>
|
||||
|
||||
递归方法是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,避免重复求解子问题。
|
||||
递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。
|
||||
|
||||
```java
|
||||
public int Fibonacci(int n) {
|
||||
|
@ -521,11 +537,11 @@ public int JumpFloor(int n) {
|
|||
|
||||
```java
|
||||
public int JumpFloor(int n) {
|
||||
if (n <= 1)
|
||||
if (n <= 2)
|
||||
return n;
|
||||
int pre2 = 0, pre1 = 1;
|
||||
int result = 0;
|
||||
for (int i = 1; i <= n; i++) {
|
||||
int pre2 = 1, pre1 = 2;
|
||||
int result = 1;
|
||||
for (int i = 2; i < n; i++) {
|
||||
result = pre2 + pre1;
|
||||
pre2 = pre1;
|
||||
pre1 = result;
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
|
||||
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
|
||||
|
||||
可以通过数据库备份和恢复来实现,在系统发生奔溃时,使用备份的数据库进行数据恢复。
|
||||
可以通过数据库备份和恢复来实现,在系统发生崩溃时,使用备份的数据库进行数据恢复。
|
||||
|
||||
----
|
||||
|
||||
|
@ -79,9 +79,9 @@
|
|||
- 只有满足一致性,事务的执行结果才是正确的。
|
||||
- 在无并发的情况下,事务串行执行,隔离性一定能够满足。此时要只要能满足原子性,就一定能满足一致性。
|
||||
- 在并发的情况下,多个事务并发执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
|
||||
- 事务满足持久化是为了能应对数据库奔溃的情况。
|
||||
- 事务满足持久化是为了能应对数据库崩溃的情况。
|
||||
|
||||
<div align="center"> <img src="../pics//35650b4b-efa1-49ba-9680-19837027cfc9.png" width="500"/> </div><br>
|
||||
<div align="center"> <img src="../pics//a58e294a-615d-4ea0-9fbf-064a6daec4b2.png" width="400"/> </div><br>
|
||||
|
||||
## AUTOCOMMIT
|
||||
|
||||
|
|
BIN
pics/a58e294a-615d-4ea0-9fbf-064a6daec4b2.png
Normal file
BIN
pics/a58e294a-615d-4ea0-9fbf-064a6daec4b2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
Loading…
Reference in New Issue
Block a user