1
1
# 链式结构
2
2
3
- 上一节讲到了支持随机访问的线性结构,这次我们开始讲链式结构。最常见的就是单链表和双链表 。
3
+ 上一节讲到了支持随机访问的线性结构,这次我们开始讲链式结构, 视频里我会说下这两种结构的区别,然后讲解最常见的单链表和双链表 。
4
4
之前在专栏文章[ 那些年,我们一起跪过的算法题[ 视频]] ( https://zhuanlan.zhihu.com/p/35175401 ) 里实现过一个 lru_cache,
5
5
使用到的就是循环双端链表,如果感觉这篇文章有点难理解,我们这里将会循序渐进地来实现。
6
- 后边讲到哈希表的冲突解决方式的时候,我们会再次使用链接表 。
6
+ 后边讲到哈希表的冲突解决方式的时候,我们会再次提到链表 。
7
7
8
8
上一节我们分析了 list 的各种操作是如何实现的,如果你还有印象的话,list
9
9
在头部进行插入是个相当耗时的操作(需要把后边的元素一个一个挪个位置)。假如你需要频繁在数组两头增删,list 就不太合适。
10
- 今天我们介绍的链式结构将摆脱这个缺陷,当然了链式结构本身也有缺陷,比如你不能像数组一样随机根据下标访问。
10
+ 今天我们介绍的链式结构将摆脱这个缺陷,当然了链式结构本身也有缺陷,比如你不能像数组一样随机根据下标访问,你想查找一个元素只能老老实实从头遍历 。
11
11
所以嘛,学习和了解数据结构的原理和实现你才能准确地选择到底什么时候该用什么数据结构,而不是瞎选导致代码性能很差。
12
12
13
13
@@ -31,24 +31,24 @@ class LinkedList(object):
31
31
[root] -> [node0] -> [node1] -> [node2]
32
32
"""
33
33
```
34
- 实现我们会在视频中用画图来模拟并且手动代码实现。
34
+ 实现我们会在视频中用画图来模拟并且手动代码实现,代码里我们会标识每个步骤的时间复杂度.
35
35
36
36
来看下时间复杂度:
37
37
38
- 操作 | 平均时间复杂度 |
39
- --------------------------|----------------|
40
- linked_list.append(value) | O(1) |
38
+ 操作 | 平均时间复杂度 |
39
+ ------------------------------ |----------------|
40
+ linked_list.append(value) | O(1) |
41
41
linked_list.appendleft(value) | O(1) |
42
- linked_list.find(value) | O(n) |
43
- linked_list.remove(value) | O(n) |
42
+ linked_list.find(value) | O(n) |
43
+ linked_list.remove(value) | O(n) |
44
44
45
45
46
46
# 双链表
47
47
上边我们亲自实现了一个单链表,但是能看到很明显的问题,单链表虽然 append 是 O(1),但是它的 find 和 remove 都是 O(n)的,
48
48
因为删除你也需要先查找,而单链表查找只有一个方式就是从头找到尾,中间找到才退出。
49
- 这里我之前提到过如果要实现一个 lru
50
- 缓存(访问时间最久的踢出),我们需要在一个链表里能高效的删除元素, 并把它追加到访问表的最后一个位置,这个时候单链表就满足不了了,
51
- 因为缓存在dict 里查找的时间是 O(1),你更新访问顺序就 O(n)了,缓存就没了优势。
49
+ 这里我之前提到过如果要实现一个 lru 缓存(访问时间最久的踢出),我们需要在一个链表里能高效的删除元素,
50
+ 并把它追加到访问表的最后一个位置,这个时候单链表就满足不了了,
51
+ 因为缓存在 dict 里查找的时间是 O(1),你更新访问顺序就 O(n)了,缓存就没了优势。
52
52
53
53
这里就要使用到双链表了,相比单链表来说,每个节点既保存了指向下一个节点的指针,同时还保存了上一个节点的指针。
54
54
@@ -63,14 +63,13 @@ class Node(object):
63
63
64
64
- 看似我们反过来遍历双链表了。反过来从哪里开始呢?我们只要让 root 的 prev 指向 tail 节点,不就串起来了吗?
65
65
- 直接删除节点,当然如果给的是一个值,我们还是需要查找这个值在哪个节点? - 但是如果给了一个节点,我们把它拿掉,直接让它的前后节点互相指过去不就行了?哇欧,删除就是 O(1) 了,两步操作就行啦
66
- - 是不是可以很方便地
67
66
68
67
好,废话不多说,我们在视频里介绍怎么实现一个双链表 ADT。
69
68
70
69
71
70
# 小问题:
72
71
- 这里单链表我没有实现 insert 方法,你能自己尝试实现吗? insert(value, new_value),我想在某个值之前插入一个值。你同样需要先查找,所以这个步骤也不够高效。
73
- - 你能尝试自己实现个 lru cache 吗?
72
+ - 你能尝试自己实现个 lru cache 吗?需要使用到我们这里提到的循环双端队列
74
73
- python 内置库的哪些数据结构使用到了本章讲的链式结构?
75
74
76
75
# 相关阅读
0 commit comments