Skip to content

Commit da1e870

Browse files
committed
add new solutions
1 parent 5629fe8 commit da1e870

File tree

7 files changed

+93
-168
lines changed

7 files changed

+93
-168
lines changed

栈/316. 去除重复字母.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ class Solution:
4141

4242
Tips
4343

44-
和402一脉相承,单调栈+贪心。原理来源于高位的字符越小整体的字典序越小,所以对于存在重复的字符,只要该字符在栈尾,且新的元素比它更小,就自动pop
44+
1. 和402一脉相承,单调栈+贪心。原理来源于高位的字符越小整体的字典序越小,所以对于存在重复的字符,只要该字符在栈尾,且新的元素比它更小,就自动pop
45+
2. 这里有个两个计数存储,seen用来判断是否入栈,已经在栈内的元素不入栈,hash的计数用来判断是否出栈,如果没有剩余元素则不能出栈

栈/32. 最长有效括号.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ class Solution:
4949

5050
Tips
5151

52-
有效括号的升级版。计算最长的连续有效括号,计算括号是否有效通过左括号入栈,右括号弹出的操作可以实现,但是最长连续这里又一点tricky。其实是通过右括号也入栈来对有效的括号进行隔断。如果只有右括号,则右括号自动隔断前面的所有有效括号,这里stack=[-1]的初始位置是精髓
52+
有效括号的升级版。计算最长的连续有效括号,计算括号是否有效通过左括号入栈,右括号弹出的操作可以实现,但是最长连续这里又一点tricky。
53+
1. 左括号多:左括号本身不会被消掉,所以左index+1
54+
2. 右括号多:没有左括号与之匹配,所以右括号入栈形成隔断

链表/0.链表总结.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@
2929
- 234 回文链表:中间节点+翻转链表+遍历
3030
- 设计类
3131
- 146 LRU:双向链表,插入head(先建立node左右link,再插入),超过长度移除tail.prev, 移除是把元素左右node链接
32-
- 460 LFU:
3332
- 707 设计链表
3433
- 技巧题
35-
- 160 相交链表:
36-
- 382 链表随机节点:蓄水池算法,第K个节点有1/K的概率overwrite之前sample的元素
34+
- 160 相交链表:不用技巧,常规解法是把两个链表拼接到相同长度,然后开始遍历寻找相同节点
35+
- 382 链表随机节点:蓄水池算法,第K个节点有1/K的概率被采样

链表/143. 重排链表v.md

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -52,49 +52,50 @@ class Solution:
5252
"""
5353
Do not return anything, modify head in-place instead.
5454
"""
55+
5556
if not head:
56-
return
57-
middle= self.find_middle(head)
58-
l2 = middle.next
59-
l1 = head
60-
middle.next = None
61-
l2 = self.reverse(l2)
62-
result = self.merge_linklist(l1, l2)
63-
64-
@staticmethod
65-
def find_middle(head):
66-
slow =head
67-
fast = head
68-
while fast.next and fast.next.next:
69-
fast = fast.next.next
70-
slow = slow.next
71-
return slow
72-
73-
@staticmethod
74-
def reverse(head):
75-
cur = head
76-
newnode = None
77-
while cur:
78-
tmp = cur.next
79-
cur.next = newnode
80-
newnode = cur
81-
cur = tmp
82-
return newnode
83-
84-
@staticmethod
85-
def merge_linklist(head1, head2):
86-
while head1 and head2:
87-
tmp1 = head1.next
88-
tmp2 = head2.next
89-
90-
head1.next = head2
91-
92-
head1 = tmp1
93-
head2 = tmp2
57+
return None
58+
59+
def reverse(head):
60+
newnode = None
61+
cur = head
62+
while cur:
63+
nextnode = cur.next
64+
cur.next=newnode
65+
newnode = cur
66+
cur = nextnode
67+
return newnode
68+
69+
def getmid(head):
70+
slow, fast = head, head
71+
while fast.next and fast.next.next:
72+
slow = slow.next
73+
fast = fast.next.next
74+
return slow
75+
76+
def merge(l1, l2):
77+
while l1 and l2:
78+
l1_tmp = l1.next
79+
l2_tmp = l2.next
80+
81+
l1.next = l2
82+
l1 = l1_tmp
83+
84+
l2.next = l1
85+
l2 = l2_tmp
86+
ptr1 = head
87+
left_mid = getmid(head)
88+
mid = left_mid.next
89+
left_mid.next=None # 注意这一步
90+
91+
ptr2 = reverse(mid)
92+
merge(ptr1, ptr2)
9493
```
9594

9695

9796

9897
Tips
9998

100-
1. 这道题融合了三道题,快慢指针找中点,反转链表,合并两个链表
99+
1. 这道题融合了三道题,快慢指针找中点,反转链表,合并两个链表
100+
2. 比148多了融合这一步,融合是在原地进行的只改变link不改变value
101+
3. 需要注意的是这里find_mid找的是中点的左边界,因为需要设置mid.next=None, 把前半部分隔出来

链表/2. 两数相加.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,6 @@ class Solution:
6868
Tips:
6969

7070
1. 注意链表的赋值一般都是对next,这样可以有效避免创建空节点。如果每次val都是赋值给当前节点,则需要额外判断l1和l2是否为空决定是否创建链表的next节点
71+
2. 注意审题,是顺序向后加,remainder也是加在后面
72+
7173

链表/382. 链表随机节点.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,7 @@ Tips
4141

4242
蓄水池抽样
4343

44-
N个候选,第K个候选被采用的概率是1/K,
45-
4644
- K=1, p=1
4745
- K=2, p=1/2,覆盖K=1的概率是1/2
48-
- K=3, p=1/3, 覆盖之前的概率是2/3,所以K=2的元素被选择的概率变成1/2 * 2/3 = 1/3
46+
- K=3, p=1/3, 第3个节点本身被采样的概率论是1/3,保留之前随机数的概率是2/3,所以K=2的元素被选择的概率变成1/2 * 2/3 = 1/3
4947
-

链表总结.md

Lines changed: 43 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@
3030
- 234 回文链表:中间节点+翻转链表+遍历
3131
- 设计类
3232
- 146 LRU:双向链表,插入head(先建立node左右link,再插入),超过长度移除tail.prev, 移除是把元素左右node链接
33-
- 460 LFU:
3433
- 707 设计链表
3534
- 技巧题
36-
- 160 相交链表:可以用常规接发, 找到长度,拼接成相同长度,然后遍历判断是否有相交节点
37-
- 382 链表随机节点:蓄水池算法,第K个节点有1/K的概率overwrite之前sample的元素
35+
- 160 相交链表:不用技巧,常规解法是把两个链表拼接到相同长度,然后开始遍历寻找相同节点
36+
- 382 链表随机节点:蓄水池算法,第K个节点有1/K的概率被采样
37+
3838

3939
### 138. 复制带随机指针的链表.md
4040

@@ -291,6 +291,7 @@ $$
291291
也就是当slow和fast在C点相遇后,只要有一个ptr从head开始,和slow一起向前跑,当slow再次走到C,并绕着环跑了n-1圈后,两者会在B相遇
292292

293293
### 143. 重排链表v.md
294+
294295
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
295296

296297
L0 → L1 → … → Ln - 1 → Ln
@@ -320,77 +321,60 @@ L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
320321
链表的长度范围为 [1, 5 * 104]
321322
1 <= node.val <= 1000
322323

323-
324-
325-
326-
327-
328324
```python
329-
# Definition for singly-linked list.
330-
class ListNode:
331-
def __init__(self, val=0, next=None):
332-
self.val = val
333-
self.next = next
334-
335-
def __str__(self):
336-
res = []
337-
cur = self
338-
while cur:
339-
res.append(str(cur.val))
340-
cur = cur.next
341-
return '->'.join(res)
342-
343325
class Solution:
344326
def reorderList(self, head: ListNode) -> None:
345327
"""
346328
Do not return anything, modify head in-place instead.
347329
"""
330+
348331
if not head:
349-
return
350-
middle= self.find_middle(head)
351-
l2 = middle.next
352-
l1 = head
353-
middle.next = None
354-
l2 = self.reverse(l2)
355-
result = self.merge_linklist(l1, l2)
356-
357-
@staticmethod
358-
def find_middle(head):
359-
slow =head
360-
fast = head
361-
while fast.next and fast.next.next:
362-
fast = fast.next.next
363-
slow = slow.next
364-
return slow
332+
return None
365333

366-
@staticmethod
367-
def reverse(head):
368-
cur = head
369-
newnode = None
370-
while cur:
371-
tmp = cur.next
372-
cur.next = newnode
373-
newnode = cur
374-
cur = tmp
375-
return newnode
334+
def reverse(head):
335+
newnode = None
336+
cur = head
337+
while cur:
338+
nextnode = cur.next
339+
cur.next=newnode
340+
newnode = cur
341+
cur = nextnode
342+
return newnode
343+
344+
def getmid(head):
345+
slow, fast = head, head
346+
while fast.next and fast.next.next:
347+
slow = slow.next
348+
fast = fast.next.next
349+
return slow
350+
351+
def merge(l1, l2):
352+
while l1 and l2:
353+
l1_tmp = l1.next
354+
l2_tmp = l2.next
376355

377-
@staticmethod
378-
def merge_linklist(head1, head2):
379-
while head1 and head2:
380-
tmp1 = head1.next
381-
tmp2 = head2.next
382-
383-
head1.next = head2
356+
l1.next = l2
357+
l1 = l1_tmp
358+
359+
l2.next = l1
360+
l2 = l2_tmp
361+
ptr1 = head
362+
left_mid = getmid(head)
363+
mid = left_mid.next
364+
left_mid.next=None # 注意这一步
384365

385-
head1 = tmp1
386-
head2 = tmp2
366+
ptr2 = reverse(mid)
367+
merge(ptr1, ptr2)
387368
```
388369

389370

390371

391372
Tips
392373

393374
1. 这道题融合了三道题,快慢指针找中点,反转链表,合并两个链表
375+
2. 比148多了融合这一步,融合是在原地进行的只改变link不改变value
376+
3. 需要注意的是这里find_mid找的是中点的左边界,因为需要设置mid.next=None, 把前半部分隔出来
377+
394378
### 146. LRU 缓存机制.md
395379
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
396380

@@ -564,6 +548,7 @@ Tips
564548
- 插入搜索:如果比当前位置大直接插入,如果小就会到链表的起点进行重新搜索
565549

566550
- 注意比较时只能比较next节点,如果比较当前节点无法插入
551+
567552
### 148. 排序链表.md
568553
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
569554

@@ -1500,73 +1485,10 @@ Tips
15001485

15011486
蓄水池抽样
15021487

1503-
N个候选,第K个候选被采用的概率是1/K,
1504-
15051488
- K=1, p=1
15061489
- K=2, p=1/2,覆盖K=1的概率是1/2
1507-
- K=3, p=1/3, 覆盖之前的概率是2/3,所以K=2的元素被选择的概率变成1/2 * 2/3 = 1/3
1490+
- K=3, p=1/3, 第3个节点本身被采样的概率论是1/3,保留之前随机数的概率是2/3,所以K=2的元素被选择的概率变成1/2 * 2/3 = 1/3
15081491
-
1509-
### 460. LFU 缓存.md
1510-
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。
1511-
1512-
实现 LFUCache 类:
1513-
1514-
LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
1515-
int get(int key) - 如果键存在于缓存中,则获取键的值,否则返回 -1。
1516-
void put(int key, int value) - 如果键已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近最久未使用 的键。
1517-
1518-
注意「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。
1519-
1520-
为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。
1521-
1522-
当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。
1523-
1524-
1525-
1526-
示例:
1527-
1528-
输入:
1529-
["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
1530-
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]
1531-
输出:
1532-
[null, null, null, 1, null, -1, 3, null, -1, 3, 4]
1533-
1534-
解释:
1535-
// cnt(x) = 键 x 的使用计数
1536-
// cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
1537-
LFUCache lFUCache = new LFUCache(2);
1538-
lFUCache.put(1, 1); // cache=[1,_], cnt(1)=1
1539-
lFUCache.put(2, 2); // cache=[2,1], cnt(2)=1, cnt(1)=1
1540-
lFUCache.get(1); // 返回 1
1541-
// cache=[1,2], cnt(2)=1, cnt(1)=2
1542-
lFUCache.put(3, 3); // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
1543-
// cache=[3,1], cnt(3)=1, cnt(1)=2
1544-
lFUCache.get(2); // 返回 -1(未找到)
1545-
lFUCache.get(3); // 返回 3
1546-
// cache=[3,1], cnt(3)=2, cnt(1)=2
1547-
lFUCache.put(4, 4); // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
1548-
// cache=[4,3], cnt(4)=1, cnt(3)=2
1549-
lFUCache.get(1); // 返回 -1(未找到)
1550-
lFUCache.get(3); // 返回 3
1551-
// cache=[3,4], cnt(4)=1, cnt(3)=3
1552-
lFUCache.get(4); // 返回 4
1553-
// cache=[3,4], cnt(4)=2, cnt(3)=3
1554-
1555-
1556-
1557-
提示:
1558-
1559-
0 <= capacity, key, value <= 104
1560-
最多调用 105 次 get 和 put 方法
1561-
1562-
1563-
1564-
1565-
进阶:你可以为这两种操作设计时间复杂度为 O(1) 的实现吗?
1566-
1567-
```
1568-
```
1569-
15701492

15711493
### 61. 旋转链表.md
15721494
####

0 commit comments

Comments
 (0)