算法学习【第三篇】:链表
链表
为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间,而在进行扩充时又需要进行数据的搬迁,所以 使用起来并不是很灵活。 链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。 链表的定义 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是不像顺序表一样连续存储数据,而是 在每一个节点(数据存储单元)里存放下一个节点的位置信息(即地址)
单向链表
单向链表也叫单链表,是链表中最简单的一种形式,它的每个节点包含两个域,一个信息域(元素域)和一 个链接域。这个链接指向链表中的下一个节点,而最后一个节点的链接域则指向一个空值
·表元素域elem用来存放具体的数据。 ·链接域next用来存放下一个节点的位置(python中的标识) ·变量p指向链表的头节点(首节点)的位置,从p出发能找到表中的任意节点。
节点实现
class SingleNode(object): ““单链表的结点" def_init_(self,item): #_item存放数据元素 self.item=item #_next是下一个节点的标识 self.next=None
单链表的操作(其他链表大致相同)
·is_empty0链表是否为空 ·length0链表长度 ·travel0遍历整个链表 ·add(item)链表头部添加元素 ·append(item)链表尾部添加元素 ·insert(pos,item)指定位置添加元素 ·remove(item)删除节点 ·search(item)查找节点是否存在
单链表的实现
# coding:utf-8 class Node(object): """节点Node""" def __init__(self, elem): self.elem = elem self.next = None class SingleLinkList(object): """单链表""" def __init__(self, node=None): self.__head = None def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print('') def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) node.next = self.__head self.__head = node def append(self,item): '''链表尾部添加元素,尾插法''' node = Node(item) if self.is_empty(): self.__head = node else: cur = self.__head while cur.next != None: cur = cur.next cur.next = node def insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: pre = self.__head count = 0 while count < (pos - 1): count += 1 pre = pre.next # 当循环退出后,pre指向pos-1位置 node = Node(item) node.next = pre.next pre.next = node def remove(self, item): '''删除节点''' cur = self.__head pre = None while cur != None: if cur.elem == item: # 先判断是否是头节点 # 头节点 if cur == self.__head: self.__head = cur.next else: pre.next = cur.next break else: pre = cur cur = cur.next def search(self, item): '''查找节点是否存在''' cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': l1 = SingleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 '''
单向循环链表
单链表的一个变形是单向循环链表,链表中最后一个节点的next域不再为None,而是指向链表的头节点
# coding:utf-8 class Node(object): """结点Node""" def __init__(self, elem): self.elem = elem self.next = None self.prev = None class DoubuleLinkList(object): """单向循环链表""" def __init__(self, node=None): self.__head = node if node: node.next = node def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' if self.is_empty(): return 0 # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 1 while cur.next != self.__head: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' if self.is_empty(): return cur = self.__head while cur.next != self.__head: print(cur.elem, end=" ") cur = cur.next # 退出循环,cur指向尾结点,但尾结点的元素未打印 print(cur.elem) def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next # 退出循环,cur指向尾结点 node.next = self.__head self.__head = node # cur.next = node cur.next = self.__head def append(self, item): '''链表尾部添加元素,尾插法''' node = Node(item) if self.is_empty(): self.__head = node node.next = node else: cur = self.__head while cur.next != self.__head: cur = cur.next # node.next=cur.next node.next = self.__head cur.next = node def insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: pre = self.__head count = 0 while count < (pos - 1): count += 1 pre = pre.next # 当前循环退出后,pre指向pos-1位置 node = Node(item) node.next = pre.next pre.next = node def remove(self, item): '''删除节点''' cur = self.__head pre = None while cur.next != self.__head: if cur.elem == item: # 先判断是否是头节点 if cur == self.__head: # 头结点情况 # 找尾结点 rear = self.__head while rear.next != self.__head: rear = rear.next self.__head = cur.next rear.next = self.__head else: # 中间结点 pre.next = cur.next return else: pre = cur cur = cur.next # 退出循环,cur指向尾结点 if cur.elem == item: if cur == self.__head: # 链表只有一个结点 self.__head = None else: # pre.next=cur.next pre.next = self.__head def search(self, item): '''查找节点是否存在''' if self.is_empty(): return False cur = self.__head while cur.next != self.__head: if cur.elem == item: return True else: cur = cur.next # 退出循环,cur指向尾结点 if cur.elem == item: return True return False if __name__ == '__main__': l1 = DoubuleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 '''
双向链表
一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个链接:一个指向前一个节点,当此节点为第
一个节点时,指向空值;而另一个指向下一个节点,当此节点为最后一个节点时,指向空值
# coding:utf-8 class Node(object): """结点Node""" def __init__(self, elem): self.elem = elem self.next = None self.prev=None class DoubuleLinkList(object): """双链表""" def __init__(self, node=None): self.__head = None def is_empty(self): '''链表是否为空''' return self.__head == None def length(self): '''链表长度''' # cur游标,用来移动遍历节点 cur = self.__head # count记录数量 count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): '''遍历整个链表''' cur = self.__head while cur != None: print(cur.elem, end=" ") cur = cur.next print('') def add(self, item): '''链表头部添加元素,头插法''' node = Node(item) node.next = self.__head self.__head = node node.next.prev=node def append(self,item): '''链表尾部添加元素,尾插法''' node = Node(item) if self.is_empty(): self.__head = node else: cur = self.__head while cur.next != None: cur = cur.next cur.next = node node.prev=cur def insert(self, pos, item): '''指定位置添加元素 pos 从0开始 ''' if pos <= 0: self.add(item) elif pos > (self.length() - 1): self.append(item) else: cur=self.__head count = 0 while count < pos: count += 1 cur = cur.next # 当循环退出后,cur指向pos位置 node = Node(item) node.next=cur node.prev=cur.prev cur.prev.next=node cur.prev=node def remove(self, item): '''删除节点''' cur = self.__head while cur != None: if cur.elem == item: # 先判断是否是头节点 # 头节点 if cur == self.__head: if cur.next: #判断链表是否只有一个结点 cur.next.prev=None else: cur.prev.next=cur.next if cur.next: cur.next.prev = cur.prev break else: cur = cur.next def search(self, item): '''查找节点是否存在''' cur = self.__head while cur != None: if cur.elem == item: return True else: cur = cur.next return False if __name__ == '__main__': l1 = DoubuleLinkList() print(l1.is_empty()) print(l1.length()) l1.append(1) print(l1.is_empty()) print(l1.length()) l1.append(2) l1.add(8) l1.append(3) l1.append(4) l1.append(5) l1.append(6) l1.insert(-1, 9) l1.travel() l1.insert(3, 100) l1.travel() l1.insert(10, 200) l1.travel() l1.remove(100) l1.travel() l1.remove(9) l1.travel() l1.remove(200) l1.travel() ''' True 0 False 1 9 8 1 2 3 4 5 6 9 8 1 100 2 3 4 5 6 9 8 1 100 2 3 4 5 6 200 9 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 200 8 1 2 3 4 5 6 '''
双向循环链表
# 链表的节点 class Node(object): def __init__(self, item): self.item = item # 节点数值 self.prev = None # 用于指向前一个元素 self.next = None # 用于指向后一个元素 # 双向循环链表 class DoubleCircleLinkList(object): def __init__(self): self.__head = None # 初始化的时候头节点设为空、 # 判断链表是否为空,head为None 的话则链表是空的 def is_empty(self): return self.__head is None # 头部添加元素的方法 def add(self, item): node = Node(item) # 新建一个节点node 里面的值是item # 如果链表是空的,则node的next和prev都指向自己(因为是双向循环),head指向node if self.is_empty(): self.__head = node node.next = node node.prev = node # 否则链表不空 else: node.next = self.__head # node的next设为现在的head node.prev = self.__head.prev # node的prev 设为现在head的prev self.__head.prev.next = node # 现在head的前一个元素的next设为node self.__head.prev = node # 现在head的前驱 改为node self.__head = node # 更改头部指针 # 尾部添加元素方法 def append(self, item): # 如果当前链表是空的 那就调用头部插入方法 if self.is_empty(): self.add(item) # 否则链表不为空 else: node = Node(item) # 新建一个节点node # 因为是双向循环链表,所以head的prev其实就是链表的尾部 node.next = self.__head # node的下一个设为头 node.prev = self.__head.prev # node的前驱设为现在头部的前驱 self.__head.prev.next = node # 头部前驱的后继设为node self.__head.prev = node # 头部自己的前驱改为node # 获得链表长度 节点个数 def length(self): # 如果链表是空的 就返回0 if self.is_empty(): return 0 # 如果不是空的 else: cur = self.__head # 临时变量cur表示当前位置 初始化设为头head count = 1 # 设一个计数器count,cur每指向一个节点,count就自增1 目前cur指向头,所以count初始化为1 # 如果cur.next不是head,说明cur目前不是最后一个元素,那么count就1,再让cur后移一位 while cur.next is not self.__head: count += 1 cur = cur.next # 跳出循环说明所有元素都被累加了一次 返回count就是一共有多少个元素 return count # 遍历链表的功能 def travel(self): # 如果当前自己是空的,那就不遍历 if self.is_empty(): return # 链表不空 else: cur = self.__head # 临时变量cur表示当前位置,初始化为链表的头部 # 只要cur的后继不是头说明cur不是最后一个节点,我们就输出当前值,并让cur后移一个节点 while cur.next is not self.__head: print(cur.item, end=" ") cur = cur.next # 当cur的后继是head的时候跳出循环了,最后一个节点还没有打印值 在这里打印出来 print(cur.item) # 置顶位置插入节点 def insert(self, pos, item): # 如果位置<=0 则调用头部插入方法 if pos <= 0: self.add(item) # 如果位置是最后一个或者更大 就调用尾部插入方法 elif pos > self.length() - 1: self.append(item) # 否则插入位置就是链表中间 else: index = 0 # 设置计数器,用于标记我们后移了多少步 cur = self.__head # cur标记当前所在位置 # 让index每次自增1 ,cur后移,当index=pos-1的时候说明cur在要插入位置的前一个元素,这时候停下 while index < pos - 1: index += 1 cur = cur.next # 跳出循环,cur在要插入位置的前一个元素,将node插入到cur的后面 node = Node(item) # 新建一个节点 node.next = cur.next # node的后继设为cur的后继 node.prev = cur # node的前驱设为cur cur.next.prev = node # cur后继的前驱改为node cur.next = node # cur后继改为node # 删除节点操作 def remove(self, item): # 如果链表为空 直接不操作 if self.is_empty(): return # 链表不为空 else: cur = self.__head # 临时变量标记位置,从头开始 # 如果头结点就是 要删除的元素 if cur.item == item: # 如果只有一个节点 链表就空了 head设为None if self.length() == 1: self.__head = None # 如果多个元素 else: self.__head = cur.next # 头指针指向cur的下一个 cur.next.prev = cur.prev # cur后继的前驱改为cur的前驱 cur.prev.next = cur.next # cur前驱的后继改为cur的后继 # 否则 头节点不是要删除的节点 我们要向下遍历 else: cur = cur.next # 把cur后移一个节点 # 循环让cur后移一直到链表尾元素位置,期间如果找得到就删除节点,找不到就跳出循环, while cur is not self.__head: # 找到了元素cur就是要删除的 if cur.item == item: cur.prev.next = cur.next # cur的前驱的后继改为cur的后继 cur.next.prev = cur.prev # cur的后继的前驱改为cur的前驱 cur = cur.next # 搜索节点是否存在 def search(self, item): # 如果链表是空的一定不存在 if self.is_empty(): return False # 否则链表不空 else: cur = self.__head # 设置临时cur从头开始 # cur不断后移,一直到尾节点为止 while cur.next is not self.__head: # 如果期间找到了就返回一个True 结束运行 if cur.item == item: return True cur = cur.next # 从循环跳出来cur就指向了尾元素 看一下为元素是不是要找的 是就返回True if cur.item == item: return True # 所有元素都不是 就返回False 没找到 return False
链表与顺序表的对比
链表失去了顺序表随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大,但对存储空间的
使用要相对灵活。
链表与顺序表的各种操作复杂度如下所示:
注意虽然表面看起来复杂度都是O(n),但是链表和顺序表在插入和删除时进行的是完全不同的操作。链表的主要耗时操作是遍历查找,删除和插入操作本身的复杂度是O(1)。顺序表查找很快,主要耗时的操作是拷贝覆盖。因为除了目标元素在尾部的特殊情况,顺序表进行插入和删除时需要对操作点之后的所有元素进行前 后移位操作,只能通过拷贝和覆盖的方法进行。