Skip to content

Commit 871c1b3

Browse files
committed
change image
1 parent f625e26 commit 871c1b3

12 files changed

+939
-365
lines changed

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ Github项目主页:https://github.com/NotFound9/interviewGuide
6767
- [3.Java中单例有哪些写法?](docs/JavaMultiThread.md#Java中单例有哪些写法?)
6868
- [4.Java中创建线程有哪些方式?](docs/JavaMultiThread.md#Java中创建线程有哪些方式?)
6969
- [5.如何解决序列化时可以创建出单例对象的问题?](docs/JavaMultiThread.md#如何解决序列化时可以创建出单例对象的问题?)
70-
- [6.悲观锁和乐观锁是什么?](docs/JavaMultiThread.md#悲观锁和乐观锁是什么?)
71-
- [7.volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?](docs/JavaMultiThread.md#volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?)
72-
- [8.Java中线程的状态是怎么样的?](docs/JavaMultiThread.md#Java中线程的状态是怎么样的?)
73-
- [9.wait(),join(),sleep()方法有什么作用?](docs/JavaMultiThread.md#wait(),join(),sleep()方法有什么作用?)
74-
- [10.Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?](docs/JavaMultiThread.md#Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?)
75-
- [11.谈一谈你对线程中断的理解?](docs/JavaMultiThread.md#谈一谈你对线程中断的理解?)
76-
- [12.线程间怎么通信?](docs/JavaMultiThread.md#线程间怎么通信?)
77-
- [13.怎么实现实现一个生产者消费者?](docs/JavaMultiThread.md#怎么实现实现一个生产者消费者?)
78-
- [14.谈一谈你对线程池的理解?](docs/JavaMultiThread.md#谈一谈你对线程池的理解?)
79-
- [15.线程池有哪些状态?](docs/JavaMultiThread.md#线程池有哪些状态?)
70+
- [6.volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?](docs/JavaMultiThread.md#volatile关键字有什么用?怎么理解可见性,一般什么场景去用可见性?)
71+
- [7.Java中线程的状态是怎么样的?](docs/JavaMultiThread.md#Java中线程的状态是怎么样的?)
72+
- [8.wait(),join(),sleep()方法有什么作用?](docs/JavaMultiThread.md#wait(),join(),sleep()方法有什么作用?)
73+
- [9.Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?](docs/JavaMultiThread.md#Thread.sleep(),Object.wait(),LockSupport.park()有什么区别?)
74+
- [10.谈一谈你对线程中断的理解?](docs/JavaMultiThread.md#谈一谈你对线程中断的理解?)
75+
- [11.线程间怎么通信?](docs/JavaMultiThread.md#线程间怎么通信?)
76+
- [12.怎么实现实现一个生产者消费者?](docs/JavaMultiThread.md#怎么实现实现一个生产者消费者?)
77+
- [13.谈一谈你对线程池的理解?](docs/JavaMultiThread.md#谈一谈你对线程池的理解?)
78+
- [14.线程池有哪些状态?](docs/JavaMultiThread.md#线程池有哪些状态?)
8079
- [锁相关](docs/Lock.md)
8180
- [1.sychronize的实现原理是怎么样的?](docs/Lock.md#sychronize的实现原理是怎么样的?)
8281
- [2.AbstractQueuedSynchronizer(缩写为AQS)是什么?](docs/Lock.md#AbstractQueuedSynchronizer(缩写为AQS)是什么?)
82+
- [3.悲观锁和乐观锁是什么?](docs/Lock.md#悲观锁和乐观锁是什么?)
8383
* Redis
8484
- [基础](docs/RedisBasic.md)
8585
- [1.Redis是什么?](docs/RedisBasic.md#Redis是什么?)

docs/CodingInterviews.md

Lines changed: 19 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@
7474
#### [题065机器人的运动范围](#题065)
7575
#### [题066剪绳子](#题066)
7676

77-
7877
### 题003 二维数组中的查找
7978

8079
##### 题目内容:
8180

81+
![image-20201202111714727](../static/image-20201202111714727.png)
82+
8283
在一个二维[数组](https://cuijiahua.com/blog/tag/数组/)中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维[数组](https://cuijiahua.com/blog/tag/数组/)和一个整数,判断数组中是否含有该整数。
8384

8485
如果在一个二维数组中找到数字7,则返回true,如果没有找到,则返回false。
@@ -830,7 +831,7 @@ public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
830831

831832
也可以使用递归实现,深度遍历递归实现
832833

833-
```
834+
```java
834835
ArrayList<TreeNode> list = new ArrayList<TreeNode>();
835836
void deepTranverse(TreeNode node) {
836837
if(node!=null) {
@@ -1974,7 +1975,7 @@ public int Add(int num1,int num2) {
19741975

19751976
-2的31次方 2的31次方-1
19761977

1977-
```
1978+
```java
19781979
public static int StrToInt(String str) {
19791980
if (str==null || str.length()==0) {
19801981
return 0;
@@ -2041,7 +2042,7 @@ public boolean duplicate(int numbers[],int length,int [] duplication) {
20412042

20422043
## 题050 构建乘积数组
20432044

2044-
```
2045+
```java
20452046
就是B[i] = A[0]A[1]...A[i-1] A[i+1]...*A[n-1],通过拆分成两部分,
20462047
C[i] = A[0]A[1]...A[i-1]
20472048
D[i] = A[i+1]...*A[n-1]
@@ -2074,7 +2075,7 @@ public int[] multiply(int[] A) {
20742075

20752076
就是使用一个数组来记录字符出现的次数。
20762077

2077-
```
2078+
```java
20782079
StringBuffer str = new StringBuffer();
20792080
int[] table = new int[256];//记录出现次数,0代表0次,1代表1次,2代表2次及2次以上
20802081
public void Insert(char ch)
@@ -2260,7 +2261,7 @@ public ListNode deleteDuplication(ListNode pHead)
22602261

22612262
​ 父节点是右子树,一直向上遍历,直到找到一个父节点,他是祖先节点是左节点的,找到就返回祖先节点,找不到就返回空。
22622263

2263-
```
2264+
```java
22642265
public TreeLinkNode GetNext(TreeLinkNode pNode)
22652266
{
22662267
//这个节点有右子树
@@ -2307,7 +2308,7 @@ public TreeLinkNode GetNext(TreeLinkNode pNode)
23072308

23082309
假设有另外一种遍历是根节点,右子树,左子树,如果二叉树是对称,那么这两种遍历的结果是一样的,所以使用递归来进行两种遍历,然后在过程中判断两种遍历结果是否一样。
23092310

2310-
```
2311+
```java
23112312
boolean isSymmetrical(TreeNode pRoot)
23122313
{
23132314
return isSymmetrical(pRoot,pRoot);
@@ -2426,7 +2427,7 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24262427

24272428
就是使用一个队列queue,一开始将根节点加入queue,并且加入一个null元素到队列中作为标志元素,用来分割每一层,标志这一层的节点都在标志元素的前面。然后对queue中元素出列,每个进行打印,直到出列的元素是null,表示这一层已经结束了,如果queue中还有元素,那么在后面加入null标志元素分割,并且进行换行,打印下一行,如果queue中没有元素就结束循环
24282429

2429-
```
2430+
```java
24302431
ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24312432
ArrayList<ArrayList<Integer>> arrayLists= new ArrayList<ArrayList<Integer>>();
24322433
if (pRoot == null) return arrayLists;
@@ -2457,7 +2458,7 @@ ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24572458

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

2460-
```
2461+
```java
24612462
ArrayList<ArrayList<Integer>> Print2(TreeNode pRoot) {
24622463
ArrayList<ArrayList<Integer>> arrayLists = new ArrayList<ArrayList<Integer>>();
24632464
if (pRoot==null)return arrayLists;
@@ -2494,7 +2495,7 @@ void find(TreeNode pRoot, int depth, ArrayList<ArrayList<Integer>> arrayLists) {
24942495

24952496
4.重复步骤3,直到队列元素个数为空。
24962497

2497-
```
2498+
```java
24982499
String Serialize(TreeNode root) {
24992500
StringBuffer stringBuffer = new StringBuffer();
25002501
if (root == null) {return stringBuffer.toString();}
@@ -2567,7 +2568,7 @@ Integer convert(String str) {
25672568

25682569
由于前序遍历是先左子树,根节点,右子树的顺序,所以前序遍历的结果,就是二叉搜索树中元素按递增顺序排列的结果,所以按照前序遍历到第K个元素就是第K小的节点。
25692570

2570-
```
2571+
```java
25712572
Integer index = 0;
25722573
TreeNode kNode = null;
25732574

@@ -2599,7 +2600,7 @@ void find(TreeNode node, Integer k) {
25992600

26002601

26012602

2602-
```
2603+
```java
26032604
ArrayList<Integer> arrayList = new ArrayList<Integer>();
26042605

26052606
public void Insert(Integer num) {
@@ -2633,7 +2634,7 @@ void find(TreeNode node, Integer k) {
26332634

26342635
解法一就是维护一个排序好的数组,数组就是当前滑动窗口排序好的结果,每次滑动时将值插入排序好的队列,并且将过期的值删除,
26352636

2636-
```
2637+
```java
26372638
public ArrayList<Integer> maxInWindows1(int[] num, int size) {
26382639
ArrayList<Integer> arrayList = new ArrayList<Integer>();
26392640
ArrayList<Integer> sortList = new ArrayList<Integer>(size);
@@ -2688,7 +2689,7 @@ ArrayList<Integer> removeValueIntoSorted(ArrayList<Integer> sortList, int curren
26882689
因为这些元素的index比A都小,而且值也比A小,不可能再成为最大值了,
26892690
然后判断队列头结点的最大值是否过期,过期的话也删除
26902691

2691-
```
2692+
```java
26922693

26932694
public ArrayList<Integer> maxInWindows(int[] num, int size) {
26942695
ArrayList<Integer> arrayList = new ArrayList<Integer>();
@@ -2727,7 +2728,7 @@ public ArrayList<Integer> maxInWindows(int[] num, int size) {
27272728

27282729
就是递归去判断就行了。
27292730

2730-
```
2731+
```java
27312732
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
27322733
{
27332734
boolean[] flag = new boolean[matrix.length];
@@ -2782,7 +2783,7 @@ boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int c
27822783

27832784
就是递归去求解,判断每个节点的上,下,左,右节点是否满足需求。
27842785

2785-
```
2786+
```java
27862787
public int movingCount(int threshold, int rows, int cols)
27872788
{
27882789
if (rows<0||cols<0||threshold<0) {return 0;}
@@ -2872,7 +2873,7 @@ public int cutRope(int target) {
28722873

28732874
普通的二分查找
28742875

2875-
```
2876+
```java
28762877
public int findByHalf(int[] array, int target) {
28772878
if (array==null||array.length==0) {
28782879
return -1;
@@ -2895,7 +2896,7 @@ public int findByHalf(int[] array, int target) {
28952896

28962897
二分查找-左边界版本,只要改等于时的判断以及后面的越界判断
28972898

2898-
```
2899+
```java
28992900
public static void main(String[] args) {
29002901
Test002 test002 = new Test002();
29012902
int[] array = new int[]{1,2,2,2,2,4,5,6,7};
@@ -2923,195 +2924,3 @@ public int findByHalf(int[] array, int target) {
29232924
//如果有左边界就返回左边界,没有时,此时的left应该是最接近左边界,并且大于左边界的数的位置
29242925
```
29252926

2926-
2927-
2928-
## LRU算法
2929-
2930-
LRU其实就是Last Recent Used,就是最近使用淘汰策略,所以当空间满了时,就根据最近使用时间来删除。一般是使用一个双向链表来实现,同时为了快速访问节点,会使用一个HashMap来存储键值映射关系。(需要注意的是,为了在内存满时删除最后一个节点时,可以以O(1)时间复杂度从HashMap中删掉这个键值对,每个节点除了存储value以外,还需要存储key)。
2931-
2932-
#### 添加新元素的过程:
2933-
2934-
首先我们增加两个方法,remove()方法用于删除一个节点,addNewNodeToHead()代表添加一个节点到头部。
2935-
2936-
1.判断节点是否已经存在于链表中,是的话,找出节点,
2937-
2938-
1.1更新value,
2939-
2940-
1.2调用remove()方法删除节点,
2941-
2942-
1.3调用addNewNodeToHead()将节点添加到链表头部。
2943-
2944-
2.节点不存在于链表中,那么判断链表长度是否超出限制,
2945-
2946-
2.1是的话remove(lastNode.key)
2947-
2948-
2.2创建一个新节点,调用addNewNodeToHead()将节点添加到链表头部。
2949-
2950-
2951-
2952-
remove()方法的细节,主要是更新node的前后节点的next或pre指针,以及更新后需要判断删除的节点是否是headNode或者lastNode,是的话同时需要更新headNode或者lastNode。
2953-
2954-
addNewNodeToHead()方法细节,主要是要先判断head是否为null,是的话说明链表为空,需要将headNode和lastNode都设置为node,不为null就执行添加操作,将headNode.pre设置为node,node的next设置为headNode,headNode=node;
2955-
2956-
2957-
2958-
```java
2959-
//双向链表
2960-
public static class ListNode {
2961-
String key;
2962-
Integer value;
2963-
ListNode pre = null;
2964-
ListNode next = null;
2965-
ListNode(String key, Integer value) {
2966-
this.key = key;
2967-
this.value = value;
2968-
}
2969-
}
2970-
ListNode headNode;
2971-
ListNode lastNode;
2972-
int limit=4;
2973-
HashMap<String, ListNode> hashMap = new HashMap<String, ListNode>();
2974-
public void put(String key, Integer val) {
2975-
ListNode existNode = hashMap.get(key);
2976-
if (existNode!=null) {//有老的节点,只是更新值,先从链表移除,然后从头部添加
2977-
existNode.value=val;
2978-
remove(key);
2979-
addNewNodeToHead(existNode);
2980-
} else {
2981-
//达到限制,先删除尾节点
2982-
if (hashMap.size() == limit) { remove(lastNode.key); }
2983-
ListNode newNode = new ListNode(key,val);
2984-
addNewNodeToHead(newNode);
2985-
}
2986-
}
2987-
public ListNode get(String key) {
2988-
2989-
ListNode node = hashMap.get(key);
2990-
if(node == null) {
2991-
return null;
2992-
}
2993-
remove(node.key);
2994-
addNewNodeToHead(node);
2995-
return node;
2996-
}
2997-
public void remove(String key) {
2998-
ListNode deleteNode = hashMap.get(key);
2999-
hashMap.remove(key);
3000-
ListNode preNode = deleteNode.pre;
3001-
ListNode nextNode = deleteNode.next;
3002-
//删除操作需要更新pre节点的next指针和next节点的pre指针,以及更新head和last
3003-
if (preNode!=null) { preNode.next = nextNode; }
3004-
if (nextNode!=null) { nextNode.pre = preNode; }
3005-
if (headNode == deleteNode) { headNode = nextNode; }
3006-
if (lastNode == deleteNode) { lastNode = preNode; }
3007-
}
3008-
private void addNewNodeToHead(ListNode node) {
3009-
hashMap.put(node.key,node);
3010-
if (headNode==null||lastNode==null) {
3011-
headNode = node;
3012-
lastNode = node;
3013-
return;
3014-
}
3015-
headNode.pre = node;
3016-
node.next = headNode;
3017-
headNode = node;
3018-
}
3019-
```
3020-
3021-
##### 使用LinkedHashMap实现的算法
3022-
3023-
使用LinkedHashMap实现LRU算法,
3024-
3025-
* LinkedHashMap默认的accessOrder为false,也就是会按照插入顺序排序,
3026-
所以在插入新的键值对时,总是添加在队列尾部,
3027-
如果是访问已存在的键值对,或者是put操作的键值对已存在,那么需要将键值对先移除再添加。
3028-
* 如果是将accessOrder设置为true,get已有键值对时就不需要删除key了,会自动调整顺序,put方法需要在添加或者更新键值对后调用LinkedHashMap#get()访问key,调整顺序。
3029-
3030-
```java
3031-
//accessOrder为false,按照插入顺序排序的写法
3032-
public static class LRUCache {
3033-
int capacity;
3034-
Map<Integer, Integer> map;
3035-
public LRUCache(int capacity) {
3036-
this.capacity = capacity;
3037-
map = new LinkedHashMap<>();
3038-
}
3039-
public int get(int key) {
3040-
if (!map.containsKey(key)) {
3041-
return -1;
3042-
}
3043-
//先删除旧的位置,再放入新位置
3044-
Integer value = map.remove(key);
3045-
map.put(key, value);
3046-
return value;
3047-
}
3048-
3049-
public void put(int key, int value) {
3050-
if (map.containsKey(key)) {
3051-
map.remove(key);
3052-
map.put(key, value);
3053-
return;
3054-
}
3055-
map.put(key, value);
3056-
//超出capacity,删除最久没用的,利用迭代器,删除第一个
3057-
if (map.size() > capacity) {
3058-
map.remove(map.keySet().iterator().next());
3059-
}
3060-
}
3061-
}
3062-
```
3063-
accessOrder为true,按照访问顺序排序的实现方法
3064-
```java
3065-
3066-
public static class LRUCache2 {
3067-
int capacity;
3068-
LinkedHashMap<Integer, Integer> linkedHashMap;
3069-
LRUCache2(int capacity) {
3070-
this.capacity = capacity;
3071-
//如果要修改accessOrder只能使用这种构造器方法来创建LinkedHashMap
3072-
linkedHashMap = new LinkedHashMap<Integer, Integer>(16,0.75f,true);
3073-
}
3074-
public int get(int key) {
3075-
Integer value = linkedHashMap.get(key);
3076-
return value == null ? -1 : value;
3077-
}
3078-
public void put(int key, int val) {
3079-
linkedHashMap.put(key, val);
3080-
if (linkedHashMap.size() > capacity) {
3081-
linkedHashMap.remove(linkedHashMap.keySet().iterator().next());
3082-
}
3083-
}
3084-
//通过调用get()方法访问key来调整顺序
3085-
linkedHashMap.get(key);
3086-
}
3087-
```
3088-
3089-
### 【面试算法题】阿拉伯数字转化为中文读法
3090-
3091-
例如我们要将10100转换为中文,总体流程就是先拿10100/1个亿,发现结果为0,说明不会包含亿这个数量级,然后10100/1万,得到结果result为1,余数remain为100,说明包含万这个数量级,我们的结果肯定是等于 "result的中文表示"+单位"万"+"余数的中文表示",所以就对问题进行了分解,f(n) = f(n/数量级)+数量级单位+f(n%数量级)
3092-
3093-
```java
3094-
static String[] nameArray1 = {"","","","","","","","","",""};
3095-
static String[] nameArray2 = {"","","","","","亿"};
3096-
static int[] intArray = {1,10,100,1000,10000,100000000};
3097-
3098-
public static String numToChinese(int num) {
3099-
for (int i = intArray.length-1; i >= 0; i--) {
3100-
int part1 = num/intArray[i];
3101-
int part2 = num%intArray[i];
3102-
if (i==0) {//到个位了
3103-
return nameArray1[part1];
3104-
}
3105-
if (part1>0) {
3106-
//整除部分,例如10100,整除部分就是十
3107-
String left = numToChinese(part1);
3108-
//整除部分的单位,例如10100,整除部分的单位就是万
3109-
String unitString = nameArray2[i];
3110-
//余数部分,例如10100,余数部分就是一百
3111-
String right = numToChinese(part2);
3112-
return left + unitString + right;
3113-
}
3114-
}
3115-
return "";
3116-
}
3117-
```

0 commit comments

Comments
 (0)