假设我们把计算机的内存可视化我们会得到下面这张图(一格代表一个字节):
假设我们已经存储了一个int(int = 2),其位置从112开始占了4个字节:
Array
如果我们要储存一个Array,Array中有3个数字 [7, 8, 3],假设其位置从100开始:
那么,针对这个数组,如果我们想要在数组最后新增一个数字9,计算机会怎么做?
由于数组中数字在空间上必须是连续的,也就是说,如果要新增一个数字,那么当前数组的数字变成了4个,那么所占字节就从12个变成了16个,因此我们需要16个连续的字节空间。
但是,如图所示,位置112到位置115已经被数字2占用了,因此在原位置无法得到一个有16个字节的连续空间,因此计算机只能在其他位置找一个有连续16个字节的空间,比如从位置116到131。
运行时间说明
如果在一个数组尾部删除一个数字,我们只需要将最后一个数字删除即操作次数为1次。如果要在一个数组头部删除一个数字,我们需要将第一个数字删除同时将后面数字的位置向前挪动一位,即如果数组有n个数字,那么操作次数为n次。同理其他操作我们可以得到下面这个表格:
由上可知,由于array数组必须占用连续的空间,而且在删除第一个元素后还要挨个移动剩余元素的位置,因此在对array进行操作时会产生非常多的冗余的步骤。而linked list则提供了相对而言更加灵活的方法。
Singly linked list
Linked list 和Array的最大区别就是,linked list 不需要连续的空间。
假设linked list当前有数字 7,8,3。 那么计算机在数字7,8,3之间是如何建立联系的呢?
答案就是:除了储存数字本身之外,计算机还要占用部分字节用来储存下一个数字的位置信息。
假设我们要在8的后面增加数字9,即将数列变成 7,8,9,3。首先我们把数字本身叫做key,储存下一个位置信息的空间叫做pointer,将数字本身和记录下一个数字位置的组合叫做Node。
因此如果要在8后面新增数字9,意味着计算机需要增加一个node,同时将数字8对应的pointer指向新增的node,然后将新增node的pointer指向数字3。
运行时间说明
Singly linked list | 操作 | 操作时间 |
PushFront(Key) | add to front 在头部新增 | O(1) |
TopFront() | return front item 返回第一个元素 | O(1) |
PopFront() | remove front item 移除第一个元素 | O(1) |
PushBack(key) | add to back 在尾部新增 | O(1) |
TopBack() | return back item 返回最后一个元素 | O(1) |
PopBack() | remove back item 移除最后一个元素 | O(n) |
Find(Key) | is key in list? 数字是否在列表里? | O(n) |
Erase(Key) | remove key from list 从列表中移除元素 | O(n) |
Empty(Key) | empty list? 列表是否为空? | O(1) |
AddBefore(Node, Key) | adds key before node 在位置前新增元素 | O(n) |
AddAfter(Node, Key) | adds key after node 在位置后新增元素 | O(1) |
Doubly linked list
与singly linked list相比,doubly linked list多了一个记录 previous pointer 的空间:
由上可知,一个Node中就包含了3个信息:
1. key(数字本身)
2. next pointer
3. prev pointer
运行时间说明
红色为和singly linked list不同的时间。
Doubly linked list | 操作 | 操作时间 |
PushFront(Key) | add to front 在头部新增 | O(1) |
TopFront() | return front item 返回第一个元素 | O(1) |
PopFront() | remove front item 移除第一个元素 | O(1) |
PushBack(key) | add to back 在尾部新增 | O(1) |
TopBack() | return back item 返回最后一个元素 | O(1) |
PopBack() | remove back item 移除最后一个元素 | O(1) |
Find(Key) | is key in list? 数字是否在列表里? | O(n) |
Erase(Key) | remove key from list 从列表中移除元素 | O(n) |
Empty(Key) | empty list? 列表是否为空? | O(1) |
AddBefore(Node, Key) | adds key before node 在位置前新增元素 | O(1) |
AddAfter(Node, Key) | adds key after node 在位置后新增元素 | O(1) |