Skip to content

Commit 8c19189

Browse files
author
haoyang.shi
committed
add
1 parent ffdc3eb commit 8c19189

7 files changed

Lines changed: 325 additions & 0 deletions

File tree

SUMMARY.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,17 @@
176176
- [矩阵中的路径](offer/hasPath.md)
177177
- [机器人的运动范围](offer/MovingCount.md)
178178
- [剪绳子](offer/CutRope.md)
179+
- [正则表达式匹配](offer/PatternMatch.md)
180+
- [表示数值的字符串](offer/IsNumeric.md)
181+
- [链表中环的入口](offer/EntryNodeOfLoop.md)
182+
- [对称二叉树](offer/IsSymmetrical.md)
183+
- [序列化二叉树](offer/SerializeTree.md)
184+
- [数据流中的中位数](offer/StreamMid.md)
185+
- [数字序列中的某一位的数字]()
186+
- [把数字翻译成字符串]()
187+
- [礼物的最大价值]()
188+
- [最长不含重复字符的子字符串]()
189+
- [在排序数组中查找数字]()
190+
- [二叉搜索树的第K大节点]()
191+
- [滑动窗口的最大值]()
192+
- [股票的最大利润]()

offer/EntryNodeOfLoop.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# 链表中环的入口结点
2+
3+
## 题目
4+
5+
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
6+
7+
8+
## 解题思路
9+
10+
1. 首先通过 **快慢指针**(快:每次走两步;慢:每次走一步)确定是否有环
11+
2. 当有环时,再从头节点出发,与快指针按 **相同速度** 向前移动,当 `cursor = fast` 则找到环入口
12+
13+
```
14+
public ListNode EntryNodeOfLoop(ListNode pHead) {
15+
if (pHead == null || pHead.next == null) return null;
16+
17+
ListNode fast = pHead, slow = pHead;
18+
19+
while (fast.next != null) {
20+
slow = slow.next;
21+
fast = fast.next.next;
22+
23+
if (fast == slow) break;
24+
}
25+
26+
if (fast != slow) return null;
27+
28+
ListNode cursor = pHead;
29+
while (cursor != fast) {
30+
cursor = cursor.next;
31+
fast = fast.next;
32+
}
33+
34+
return cursor;
35+
}
36+
```

offer/IsNumeric.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# 表示数值的字符串
2+
3+
## 题目
4+
5+
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
6+
7+
8+
## 解题思路
9+
10+
1. 数字符合 `A[.[B]][e|EC]``.B[e|EC]` 的表达式,其中 `A` 表示整数部分,`B` 表示小数部分,`C` 表示指数部分
11+
2. `A` 可以有正负,但是 `B` 没有
12+
3. `e|E` 之前、之后都必须有数字
13+
14+
```
15+
public boolean isNumeric(char[] str) {
16+
if (str == null || str.length == 0) return false;
17+
18+
int index = scanInteger(str, 0);
19+
20+
boolean numeric = index != 0;
21+
22+
//小数
23+
if (index < str.length && str[index] == '.') {
24+
index++;
25+
int pre = index;
26+
index = scanUnsignedInteger(str, index);
27+
28+
//1. 小数可以没有整数部分
29+
//2. 小数后可以没有数字
30+
//3. 小数后可以有数字
31+
numeric |= index != pre;
32+
}
33+
34+
//指数
35+
if (index < str.length && (str[index] == 'e' || str[index] == 'E')) {
36+
index++;
37+
int pre = index;
38+
index = scanInteger(str, index);
39+
40+
//1. e或者E之前必须有数字
41+
//2. e或者E之后必须有数字
42+
numeric &= index != pre;
43+
}
44+
45+
return numeric && index == str.length;
46+
}
47+
48+
private int scanInteger(char[] str, int s) {
49+
if (s < str.length && (str[s] == '+' || str[s] == '-'))
50+
s++;
51+
52+
return scanUnsignedInteger(str, s);
53+
}
54+
55+
56+
private int scanUnsignedInteger(char[] str, int s) {
57+
while (s < str.length && str[s] >= '0' && str[s] <= '9') {
58+
s++;
59+
}
60+
61+
return s;
62+
}
63+
```

offer/IsSymmetrical.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# 对称的二叉树
2+
3+
## 题目
4+
5+
请实现一个函数,用来判断一颗二叉树是不是对称的。注意,如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。
6+
7+
## 解题思路
8+
9+
1. 定义一个对称的前序遍历,即`root -> right -> left` 与普通的前序遍历进行对比
10+
2. 相同则认为树是对称的
11+
12+
```
13+
boolean isSymmetrical(TreeNode pRoot) {
14+
LinkedList<Integer> scanner = new LinkedList<>();
15+
LinkedList<Integer> symmetricalScanner = new LinkedList<>();
16+
17+
preScanner(scanner, pRoot);
18+
symmetricalPreScanner(symmetricalScanner, pRoot);
19+
20+
return scanner.equals(symmetricalScanner);
21+
}
22+
23+
/**
24+
* 普通的前序遍历
25+
* @param res
26+
* @param root
27+
*/
28+
private void preScanner(LinkedList<Integer> res, TreeNode root) {
29+
if (root == null) {
30+
res.addLast(null);
31+
return;
32+
}
33+
34+
res.addLast(root.val);
35+
36+
preScanner(res, root.left);
37+
preScanner(res, root.right);
38+
}
39+
40+
/**
41+
* 先右再左的前序遍历
42+
* @param res
43+
* @param root
44+
*/
45+
private void symmetricalPreScanner(LinkedList<Integer> res, TreeNode root) {
46+
if (root == null) {
47+
res.addLast(null);
48+
return;
49+
}
50+
51+
res.addLast(root.val);
52+
53+
symmetricalPreScanner(res, root.right);
54+
symmetricalPreScanner(res, root.left);
55+
}
56+
57+
```

offer/PatternMatch.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# 正则表达式匹配
2+
3+
请实现一个函数用来匹配包括`'.'``'*'`的正则表达式。模式中的字符`'.'`表示任意一个字符,而`'*'`表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串`"aaa"`与模式`"a.a"``"ab*ac*a"`匹配,但是与`"aa.a"``"ab*a"`均不匹配
4+
5+
6+
## 解题思路
7+
8+
1. 对于 `*` 有三种匹配模式:匹配0次,1次以及多次
9+
2. 对于 `.` 只有一种匹配模式
10+
11+
```
12+
public boolean match(char[] str, char[] pattern) {
13+
if (str.length == 0 && new String(pattern).replaceAll(".\\*", "").length() == 0) {
14+
return true;
15+
}
16+
17+
return match(str, 0, pattern, 0);
18+
}
19+
20+
private boolean match(char[] str, int i, char[] pattern, int j) {
21+
if (i == str.length && j == pattern.length) {
22+
return true;
23+
}
24+
25+
if (j >= pattern.length) return false;
26+
27+
if (j + 1 < pattern.length && pattern[j + 1] == '*') {
28+
if ((i < str.length && pattern[j] == str[i]) || (pattern[j] == '.' && i != str.length)) {
29+
return match(str, i + 1, pattern, j + 2)
30+
|| match(str, i + 1, pattern, j)
31+
|| match(str, i, pattern, j + 2);
32+
} else {
33+
return match(str, i, pattern, j + 2);
34+
}
35+
}
36+
37+
if ((i < str.length && pattern[j] == str[i])
38+
|| (pattern[j] == '.' && i != str.length)) {
39+
return match(str, i + 1, pattern, j + 1);
40+
}
41+
42+
return false;
43+
}
44+
```

offer/SerializeTree.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# 序列化二叉树
2+
3+
## 题目
4+
5+
请实现两个函数,分别用来序列化和反序列化二叉树
6+
7+
## 解题思路
8+
9+
1. 通过前序遍历,进行序列化和反序列化
10+
2. 对于空节点用 `$` 来代替
11+
12+
```
13+
String Serialize(TreeNode root) {
14+
if (root==null) return "";
15+
16+
LinkedList<String> res = new LinkedList<>();
17+
18+
serialize(res, root);
19+
20+
StringBuilder builder = new StringBuilder();
21+
res.forEach(v-> builder.append(v).append(","));
22+
23+
return builder.toString();
24+
}
25+
26+
27+
private void serialize(LinkedList<String> res, TreeNode root) {
28+
if (root == null) {
29+
res.addLast("$");
30+
return;
31+
}
32+
33+
res.addLast(String.valueOf(root.val));
34+
serialize(res, root.left);
35+
serialize(res, root.right);
36+
}
37+
38+
TreeNode Deserialize(String str) {
39+
if (str == null || str.length() == 0) return null;
40+
41+
return deserialize(str.split(","), new int[]{0});
42+
}
43+
44+
45+
private TreeNode deserialize(String[] str, int[] index) {
46+
if (index[0] >= str.length) return null;
47+
48+
String c = str[index[0]++];
49+
if (c.equals("$")) return null;
50+
51+
TreeNode node = new TreeNode(Integer.valueOf(c));
52+
node.left = deserialize(str, index);
53+
node.right = deserialize(str, index);
54+
55+
return node;
56+
}
57+
```

offer/StreamMid.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# 数据流中的中位数
2+
3+
## 题目
4+
5+
[牛客网](https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1?tpId=13&tqId=11216&tPage=4&rp=2&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking)
6+
7+
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用 `Insert()` 方法读取数据流,使用 `GetMedian()` 方法获取当前读取数据的中位数。
8+
9+
10+
## 解题思路
11+
12+
1. 同两个堆来表示中位数的左右两部分,左边是大根堆,右边是小根堆
13+
2. 在插入元素时,两边元素个数最多只能相差1,并且要保证左边的元素均小于右边的元素
14+
3. 当插入大堆的元素大于部分小堆元素时,需要将大堆的 top 元素移动到小堆,反之亦然
15+
16+
```
17+
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> -o1.compareTo(o2));
18+
19+
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();
20+
21+
private int size = 0;
22+
23+
24+
public void Insert(Integer num) {
25+
if (size % 2 == 0) {
26+
maxHeap.add(num);
27+
28+
if (minHeap.isEmpty() || num > minHeap.peek()) {
29+
minHeap.add(maxHeap.poll());
30+
}
31+
32+
} else {
33+
minHeap.add(num);
34+
35+
if (maxHeap.isEmpty() || num < maxHeap.peek()) {
36+
maxHeap.add(minHeap.poll());
37+
}
38+
}
39+
40+
size++;
41+
}
42+
43+
public Double GetMedian() {
44+
if (maxHeap.isEmpty() && minHeap.isEmpty()) return 0.0;
45+
if (maxHeap.isEmpty()) return minHeap.peek() * 1.0;
46+
if (minHeap.isEmpty()) return maxHeap.peek() * 1.0;
47+
48+
if (maxHeap.size() == minHeap.size()) {
49+
return (maxHeap.peek() + minHeap.peek()) / 2.0;
50+
}
51+
52+
return maxHeap.size() > minHeap.size() ? maxHeap.peek() * 1.0 : minHeap.peek() * 1.0;
53+
}
54+
```

0 commit comments

Comments
 (0)