Skip to content

Commit 369152a

Browse files
committed
change image
1 parent 8967840 commit 369152a

10 files changed

+256
-148
lines changed

docs/CodingInterviews.md

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -895,13 +895,14 @@ public static boolean VerifySquenceOfBST(int[] sequence, int start, int end) {
895895
} else if(rightChildIndex== start) {//说明全部位于右边子树
896896
return VerifySquenceOfBST(sequence,start,end-1);
897897
}
898+
//继续校验
898899
return VerifySquenceOfBST(sequence,start,rightChildIndex-1) && VerifySquenceOfBST(sequence,rightChildIndex, end-1);
899900
}
900901
```
901902

902903
## 题023 二叉树中和为某一值的路径
903904

904-
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前)
905+
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的 list 中,数组长度大的数组靠前)
905906

906907
就是递归调用每个节点的左右子树,然后将节点值相加,如果节点值和为某个预期值,并且该节点为叶子节点,那么这条路径就是要找的路径。
907908

@@ -1067,8 +1068,6 @@ public RandomListNode Clone(RandomListNode pHead)
10671068
}
10681069
```
10691070

1070-
1071-
10721071
## 题027数组中出现的次数超过一半的数字
10731072

10741073
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出 2 。如果不存在则输出 0 。
@@ -1115,7 +1114,7 @@ public int MoreThanHalfNum_Solution(int [] array) {
11151114

11161115
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
11171116

1118-
其实就是插入排序,只不过只是对k个数进行插入排序
1117+
其实就是插入排序,只不过只是对k个数进行插入排序,但是这样的时间复杂度会是O(N^2),如果是使用快排来做,平均时间复杂度就是O(N)。
11191118

11201119
```java
11211120
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
@@ -1150,13 +1149,13 @@ public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
11501149

11511150
## 题029连续子数组的最大和
11521151

1153-
例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
1152+
例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第1个开始,到第4个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
11541153

11551154
使用动态规划的方法来进行思考
11561155

11571156
f(n) 有两种取值
11581157

1159-
* 当f(n-1)<=0时,取array[n]
1158+
* 当f(n-1)<=0时,取array[n],从这个元素重新开始
11601159

11611160
* 当f(n-1)>0时,取f(n-1)+array[n]
11621161

@@ -1169,7 +1168,7 @@ f(n) 有两种取值
11691168
int currentSum = array[0];
11701169
int maxSum = currentSum;
11711170
for (int i = 1; i < array.length; i++) {
1172-
if (currentSum<0) {
1171+
if (currentSum<0) {//前面的和是负数,就直接丢弃
11731172
currentSum = array[i];
11741173
} else {
11751174
currentSum = currentSum + array[i];
@@ -1618,7 +1617,7 @@ public int IsBalanced_Solution_Depth(TreeNode root) {
16181617
int right = IsBalanced_Solution_Depth(root.right);
16191618
if (left!=-1 && right!= -1) {
16201619
int temp = left-right;
1621-
if (temp<=1&& temp>=-1) {
1620+
if (temp<=1 && temp>=-1) {
16221621
return left > right ? left + 1 : right + 1;
16231622
}
16241623
}
@@ -1868,7 +1867,7 @@ public static void qsort(int[] array, int start ,int end) {
18681867
在n个人中间报数时,每个人的编号是
18691868
0 1 2 ... k k+1 k+2 ... n-1
18701869
当k出局以后,在n-1个人中报数时,每个人的编号是重新从k+1开始计数,原来的编号就映射为下面这样了(原来k+1变成了0,原来的n-1变成了n-1-(k+1))
1871-
n-k+1 ... 0 1 ... n-1 -(k+1)
1870+
n-k-1 ... 0 1 ... n-1 -(k+1)
18721871
所以假设从n-1报数时的编号到n个人报数时的编号存在一个映射关系
18731872
假设f(n)代表n个人报数编号
18741873
f(n) = (f(n-1)+k+1)%n = (f(n-1)+ (m-1)%n + 1)%n =
@@ -1956,7 +1955,7 @@ public int Add(int num1,int num2) {
19561955

19571956
-2147483648 至 2147483647
19581957

1959-
-2的31次方 2的31次方-1
1958+
-2的31次方 2的31次方 -1
19601959

19611960
```java
19621961
public static int StrToInt(String str) {
@@ -2037,12 +2036,14 @@ public int[] multiply(int[] A) {
20372036
int[] d = new int[A.length];
20382037
c[0] = 1;
20392038
int result1 = c[0];
2039+
//构建数组C
20402040
for (int i = 1; i < c.length; i++) {
20412041
result1 = result1 * A[i-1];
20422042
c[i] = result1;
20432043
}
20442044
d[d.length-1] = 1;
20452045
int result2 = 1;
2046+
//构建数组D
20462047
for (int i = d.length-2; i >=0; i--) {
20472048
result2 = result2 * A[i+1];
20482049
d[i] = result2;
@@ -2070,15 +2071,15 @@ public int[] multiply(int[] A) {
20702071
}
20712072

20722073
public char FirstAppearingOnce()
2073-
{
2074-
for (int i = 0; i < str.length(); i++) {
2075-
char c = str.charAt(i);
2076-
if (table[c] == 1) {
2077-
return c;
2078-
}
2079-
}
2080-
return '#';
2074+
{
2075+
for (int i = 0; i < str.length(); i++) {
2076+
char c = str.charAt(i);
2077+
if (table[c] == 1) {
2078+
return c;
2079+
}
20812080
}
2081+
return '#';
2082+
}
20822083
```
20832084

20842085
## 题054 链表中环的入口节点
@@ -2088,11 +2089,11 @@ public char FirstAppearingOnce()
20882089

20892090
一种方法是遍历整个链表,将每个节点添加到HashSet中,判断是否在HashSet中出现过,第一个重复的节点就是环的入口节点。
20902091

2091-
另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第而个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点,
2092+
另一种解决方法是,假设存在环,环的长度为x,第一个指针先走x步,然后第二个指针从链表头结点出发,两个指针一起走,当第二个指针刚好走到环入口时,第一个指针正好在环中走了一圈,也在环的入口,此时的节点就是环的的入口节点,
20922093

20932094
怎么得到环的长度呢,就是一个指针每次走2步,一个指针每次走一步,他们相遇时的节点肯定就是在环中的某个节点,然后这个节点在环中遍历一圈,回到原点,就可以得到环的长度count。
20942095

2095-
两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口
2096+
两个指针从头出发,第一个指针先走count步,然后两个指针每次都只走一步,相遇的地方就是环的入口
20962097

20972098
```java
20982099
public ListNode EntryNodeOfLoop(ListNode pHead)
@@ -2193,7 +2194,6 @@ public ListNode deleteDuplication(ListNode pHead)
21932194

21942195
ListNode ourHead = new ListNode(0);
21952196
ourHead.next = pHead;
2196-
int temp = pHead.val;
21972197
ListNode preNode = ourHead;
21982198
ListNode currentNode = ourHead.next;
21992199

@@ -2329,7 +2329,7 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
23292329
int flag = 0;//代表当前遍历的是奇数层还是偶数层。区别在于添加子节点的顺序。
23302330
stack1.add(pRoot);
23312331
while ((flag == 0 && stack1.size()>0) || (flag == 1 && stack2.size()>0)) {
2332-
if (flag==0) {
2332+
if (flag==0) {//代表是偶数层
23332333
ArrayList<Integer> array = new ArrayList<Integer>();
23342334
while (stack1.size()>0) {
23352335
TreeNode node = stack1.pop();
@@ -2439,7 +2439,7 @@ ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24392439

24402440
就是递归遍历每一个节点,遍历时传入深度depth,将节点加入到ArrayList中特定深度对应的数组中去。
24412441

2442-
这种方法也可以用来进行二叉树深度遍历,遍历完之后将嵌套数组拆分成单层的数组。
2442+
这种方法也可以用来进行二叉树先序遍历,遍历完之后将嵌套数组拆分成单层的数组。
24432443

24442444
```java
24452445
ArrayList<ArrayList<Integer>> Print2(TreeNode pRoot) {
@@ -2738,7 +2738,7 @@ boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int c
27382738
return false;
27392739
}
27402740
if (charIndex==str.length-1) { return true;}
2741-
2741+
//用来记录这个位置是否在路径中
27422742
flag[index]=true;
27432743
if (judge(matrix,rows,cols,i+1, j, str, charIndex+1, flag)
27442744
||judge(matrix,rows,cols,i-1, j, str, charIndex+1, flag)
@@ -2846,10 +2846,6 @@ public int cutRope(int target) {
28462846
}
28472847
```
28482848

2849-
2850-
2851-
2852-
28532849
# 基础篇
28542850

28552851
## 二分查找

docs/JavaJVM.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@
125125

126126
![在这里插入图片描述](../static/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzI5NDY4NTcz,size_16,color_FFFFFF,t_70.jpeg)
127127

128-
**Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在32位系统占4字节,在64位系统中占8字节。
128+
**Class Pointer**:用来指向对象对应的Class对象(其对应的元数据对象)的内存地址。在开启了指针压缩时,占4字节。(默认是开启的)
129129

130130
**Length**:如果是数组对象,还有一个保存数组长度的空间,占4个字节。
131131

@@ -137,6 +137,10 @@
137137

138138
因为HotSpot虚拟机的自动内存管理系统要求对象起始地址是8字节的整数倍,所以任何对象的大小必须是8字节的整数倍,而对象头部分一般是8字节的倍数,如果实力数据部分不是8字节的整数倍,需要对齐填充来补全。
139139

140+
### 如何计算一个对象在内存中占多少个字节?
141+
142+
由于默认是开启了指针压缩的,现在普遍的机器位数都是64位,如果对象是普通对象,非数组类型,那么就是对象头部分就是12字节(类型指针4字节,Mark Word 8字节),假设这个对象只有一个Integer变量,那么在实例数据部分就是一个引用变量的空间4字节,总共是16字节,由于正好是8的倍数,就不需要进行对齐填充了。
143+
140144

141145
### 垃圾回收有哪些特点?
142146

@@ -1266,3 +1270,27 @@ public class Test018 {
12661270

12671271
![img](../static/640-20200726210058837.jpeg)
12681272

1273+
### happens-before指的是什么?
1274+
happens-before主要是为保证Java多线程操作时的有序性和可见性,防止了编译器重排序对程序结果的影响。
1275+
因为当一个变量出现一写多读,多读多写的情况时,就是多个线程读这个变量,然后至少一个线程写这个变量,因为编译器在编译时会对指令进行重排序,如果没有happens-before原则对重排序进行一些约束,就有可能造成程序执行不正确的情况。
1276+
1277+
具体有8条规则:
1278+
1.**程序次序性规则**:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。一段代码在单线程中执行的结果是有序的。(只在单线程有效,一写多读时,写的变量如果是没有使用volatile修饰时,是没法保证其他线程读到的变量是最新的值)
1279+
1280+
2.**锁定规则**:一个锁的解锁操作总是要在加锁操作之前。
1281+
1282+
3.**volatile规则**:如果一个写操作去写一个volatile变量,一个读操作去读volatile变量,那么写操作一定是在读操作之前。
1283+
1284+
4.**传递规则**:操作A happens before 操作B, B happens before C,那么A一定是happens before C的。
1285+
1286+
5.**线程启动规则**:线程A执行过程中修一些共享变量,然后再调用threadB.start()方法来启动线程B,那么A对那些变量的修改对线程B一定是可见的。
1287+
1288+
6.**线程终结规则**:线程A执行过程中调用了threadB.join()方法来等待线程B执行完毕,那么线程B在执行过程中对共享变量的修改,在join()方法返回后,对线程A可见。
1289+
1290+
7.**线程中断规则**:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
1291+
1292+
8.**对象终结规则**:一个对象的初始化完成先行发生于他的finalize()方法的开始;
1293+
1294+
https://www.cnblogs.com/chenssy/p/6393321.html
1295+
1296+
http://ifeve.com/java-%e4%bd%bf%e7%94%a8-happen-before-%e8%a7%84%e5%88%99%e5%ae%9e%e7%8e%b0%e5%85%b1%e4%ba%ab%e5%8f%98%e9%87%8f%e7%9a%84%e5%90%8c%e6%ad%a5%e6%93%8d%e4%bd%9c/#more-38824

docs/JavaMultiThread.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,9 +1454,13 @@ public ThreadPoolExecutor(int corePoolSize,
14541454

14551455
从阻塞队列取任务时,如果阻塞队列为空:
14561456

1457-
**核心线程**的会一直卡在`workQueue.take`方法,被阻塞并挂起,不会占用CPU资源。
1457+
**核心线程**的会一直卡在`workQueue.take`方法,这个take方法每种等待队列的实现各不相同,以LinkedBlockingQueue为例,
14581458

1459-
**非核心线程**会调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)方法取任务 ,如果超过keepAliveTime时间后还没有拿到,下一次循环判断**compareAndDecrementWorkerCount**就会返回`null`,Worker对象的`run()`方法循环体的判断为`null`,任务结束,然后线程被系统回收)
1459+
在这个方法里会调用notEmpty队列(这是Condition实例)的await()方法一直阻塞并挂起,不会占用CPU资源。
1460+
1461+
**非核心线程**会调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)方法取任务 ,这个poll方法每种等待队列的实现各不相同,以LinkedBlockingQueue为例,
1462+
1463+
在这个方法里面会调用notEmpty队列(这是Condition实例)的awaitNanos()方法进行超时等待,如果超过keepAliveTime时间后还没有新的任务,就会返回`null`,Worker对象的`run()`方法循环体的判断为`null`,任务结束,然后线程被系统回收)
14601464

14611465
##### 2.maximumPoolSize 最大线程数
14621466

@@ -1821,14 +1825,19 @@ final int internalNextInt(int origin, int bound) {
18211825
}
18221826
```
18231827

1828+
### 僵尸进程,孤儿进程,守护进程是什么?
18241829

1830+
僵尸进程:通常来说,使用fork()系统调用从一个父进程创建出一个子进程,子进程退出,是需要父进程调用wait()或者是waitpid()函数来回收子进程的资源,如果父进程没有调用,子进程的信息就会一直在内存中,而不会被回收,变成僵尸进程。
18251831

1826-
### BlockingQueue的原理是怎么样的?
1832+
孤儿进程:就是父进程先退出了,它的子进程会被init进程接管,由它来收集子进程的状态。(init进程是内核启动时,创建出来的进程,是一个以root身份运行的普通用户进程,是永远不会停止的。)
18271833

1828-
https://www.cnblogs.com/tjudzj/p/4454490.html
1834+
守护进程是脱离于终端并且在后台运行的进程,脱离终端是为了避免将在执行的过程中的信息打印在终端上,并且进程也不会被任何终端所产生的终端信息所打断。
18291835

1836+
### BlockingQueue的原理是怎么样的?
18301837

1838+
https://www.cnblogs.com/tjudzj/p/4454490.html
18311839

18321840
### 进程间通信的方式
18331841

1834-
https://network.51cto.com/art/201911/606827.htm?mobile
1842+
https://network.51cto.com/art/201911/606827.htm?mobile
1843+

0 commit comments

Comments
 (0)