线性存储
数组和链表都是一维结构,以线性结构存储数据。
数组
数组和计算机内存结构保持一致,现有的编程语言提供了语言层面对数组的支持,数组支持随机访问,可以按照索引高效读取和存储。
一般来说,数组在初始化时,已固定大小。当数组存储已满,需要再存储额外的数据,则需要申请更大的一个数组,把原来的数据全部搬到新数组。 这在某些场景下是不合理的,从而引出另一种存储结构:链表
单链表
单链表是另一种基本的链式存储结构,很多其它的数据结构均以它为基础构建。
仅保存head指针
仅保存head节点指针的单链表,当遍历时候,可以从head节点开始访问遍历,可以在头节点添加和删除。
保存head指针和tail指针
多记录一个tail指针,可以方便的在链表的尾部增加节点。(但在尾部删除节点仍然不行,由于删除时需要用到当前节点的前一个节点)
单循环链表
单循环链表仅仅是把尾部节点的下一个节点指向了头结点。
双循环链表
双循环链表是比较通用且功能丰富的数据结构,实际应用场景比较多。图如下:
第一个节点是一个哨兵节点,头结点的数据域可以用来存储某些数据,也可以不用。哨兵节点的关键作用是,使得操纵链表时的代码逻辑比较统一,如果没有哨兵节点,则在添加和删除节点的时候,需要特殊处理头结点为空的情况。通过添加一个额外的哨兵节点,简洁实用。
双循环链表-初始状态
初始状态,头结点的prev和next指针均指向自身。这也是判断双循环链表为空的逻辑。
双循环链表-头部添加
在双循环链表头部添加数据流程:
1. 构造一个新节点,首先设置该新节点的数据域和指针,设置新节点的next指针指向head的next,prev指针指向head
2. 设置head->next的prev指向新节点
3. 设置head的next指向当前节点
可以看到,即使链表为空(即仅包含head节点时),上述添加流程也能正常工作,这体现了哨兵节点的作用。
比如从头部依次添加整数序列,[1, 2, 3, 4, 5],到链表中,链表的结构如下:
head -> 5 -> 4 -> 3 -> 2 -> 1 ->
双循环链表-尾部添加
尾部添加流程和上述的头部添加类似, 但最终数据顺序和原来相反,
比如从尾部依次添加整数序列,[1, 2, 3, 4, 5],到链表中,链表的结构如下:
1 -> 2 -> 3 -> 4 -> 5 -> head ->
可见不管是从链表头部添加,还是从尾部添加,最终我们都可以通过从head节点开始遍历,或者通过next指针遍历,或者通过prev指针开始遍历,都可以方便的遍历整个链表。
双循环链表-头部删除
逻辑看指针图即可,不再赘述。
双循环链表-尾部删除
逻辑看指针图即可,不再赘述。
双循环链表 -> 其它数据结构
可以看到双循环链表可以在头部和尾部进行添加和删除,且时间复杂度为O(1), 空间复杂度为O(1),这是个好兆头。 正好可以用来实现栈和队列。
Python参考实现
详细源码可参见github
class DNode(object):
def __init__(self, v):
self.v = v
self.next = self
self.prev = self
class DoubleCircleList(object):
'''双循环链表'''
def __init__(self):
self.__head = DNode(None)
def add_head(self, v):
node = DNode(v)
self.insert_after(self.__head, node)
def add_tail(self, v):
node = DNode(v)
self.insert_before(self.__head, node)
def del_head(self):
if self.__head.next != self.__head:
return self.del_node(self.__head.next)
def del_tail(self):
if self.__head.next != self.__head:
return self.del_node(self.__head.prev)
def get_head(self):
if self.__head.next != self.__head:
return self.__head.next
def get_tail(self):
if self.__head.next != self.__head:
return self.__head.prev
def insert_after(self, node1, node2):
node2.next = node1.next
node2.prev = node1
node1.next.prev = node2
node1.next = node2
def insert_before(self, node1, node2):
node2.next = node1
node2.prev = node1.prev
node1.prev.next = node2
node1.prev = node2
def del_node(self, node):
node.next.prev = node.prev
node.prev.next = node.next
return node
def is_empty(self):
return self.__head.next == self.__head
def iterator(self):
class Iter(object):
def __init__(self, head):
self.__head = head
def __iter__(self):
node = self.__head.next
while node != self.__head:
yield node.v
node = node.next
return Iter(self.__head)
def reverse_iterator(self):
class Iter(object):
def __init__(self, head):
self.__head = head
def __iter__(self):
node = self.__head.prev
while node != self.__head:
yield node.v
node = node.prev
node = self.__head.next
return Iter(self.__head)