# -*- coding:utf8 -*-
# [email protected]
# Python 单向链表,支持负数索引、切片索引,支持迭代访问,支持闭环检测
# https://blog.csdn.net/liuqixuan1994/article/details/103789486
from collections import Iterable
from warnings import warn
class ListNode(object):
"""链表结点"""
def __init__(self, val, next=None):
self.val = val
self.next = next
def __repr__(self):
return str(self.val)
class LinkedList(object):
"""Python单向链表"""
def __init__(self, data=None):
if data is None:
self.head = None
self.__length = 0 # 长度为私有属性,外部不可修改,仅能通过len()运算符读取
elif isinstance(data, ListNode):
self.head = data
self.__length = 1
elif isinstance(data, Iterable):
self.head = None
self.__length = 0
for v in data:
self.append(v)
else:
self.head = ListNode(data)
self.__length = 1
def isEmpty(self):
return (self.__length == 0)
def append(self, valOrNode):
"""在当前链表的末尾追加元素。"""
elem = None
if isinstance(valOrNode, ListNode):
if valOrNode.next is not None:
warn("valOrNode.next is not None, new ListNode will be cloned. "
"If you want to append all follow-up nodes, please use 'extend'.")
elem = ListNode(valOrNode.val)
else:
elem = valOrNode
else:
elem = ListNode(valOrNode)
elem.next = None # 截断可能存在的后续
if self.head is None:
self.head = elem
else:
node = self.head
while node.next:
node = node.next
node.next = elem
self.__length += 1
def extend(self, data):
"""将data附加到当前链表之后。
data可以是:1.(有后继的)结点;2.另一个链表;3.可迭代对象。
"""
# 将data整理成以in_head为起点的链表
in_len, in_head = 0, None
if isinstance(data, ListNode):
in_crt = in_head = data
while in_crt is not None:
in_crt = in_crt.next
in_len += 1
elif isinstance(data, LinkedList):
in_crt = in_head = data.head
in_len = len(data)
elif isinstance(data, Iterable):
in_crt = in_head = ListNode(0) # 为方便构建添加临时头结点
for val in data:
in_crt.next = ListNode(val)
in_crt = in_crt.next
in_len += 1
in_head = in_head.next # 删除临时头结点
# 将in_head挂载到当前链表的末尾,注意此函数不做闭环检查
if self.head is None:
self.head = in_head
else:
this_last, _ = self.__findnode(self.__length - 1)
this_last.next = in_head
self.__length += in_len
return self
def __checkidx(self, index):
"""索引校正。"""
if self.isEmpty():
raise IndexError("this LinkedList is empty.")
if index < 0: # 转换负数索引
index += self.__length
if index < 0 or index >= self.__length:
raise IndexError('index out of range.')
return index
def __findnode(self, index):
crt, nxt = self.head, self.head.next
for _ in range(index):
crt, nxt = nxt, nxt.next
return crt, nxt
def __findprev(self, index):
prev, crt = None, self.head
for _ in range(index):
prev, crt = crt, crt.next
return prev, crt
def pop(self, index=0):
"""弹出指定位置的元素,默认以队列的方式弹出第0个元素。"""
index = self.__checkidx(index)
if index == 0:
ans = self.head.val
self.head = self.head.next
else:
prev, crt = self.__findprev(index)
ans = crt.val
prev.next = crt.next
self.__length -= 1
return ans
def delete(self, index):
index = self.__checkidx(index)
if index == 0:
self.head = self.head.next
else:
prev, crt = self.__findprev(index)
prev.next = crt.next
self.__length -= 1
def insert(self, before_index, valOrNode):
"""在指定位置之前插入元素。
before_index最大可以等于当前链表length,此时相当于在最后追加元素。"""
if before_index == self.__length:
self.append(valOrNode)
return
before_index = self.__checkidx(before_index)
if isinstance(valOrNode, ListNode):
if valOrNode.next is not None:
warn("valOrNode.next is not None, new ListNode will be cloned.")
elem = ListNode(valOrNode.val)
else:
elem = valOrNode
else:
elem = ListNode(valOrNode)
if before_index == 0:
elem.next = self.head
self.head = elem
else:
prev, crt = self.__findprev(before_index)
elem.next = crt
prev.next = elem
self.__length += 1
def update(self, index, val):
"""更新指定位置处元素的值。"""
index = self.__checkidx(index)
crt, _ = self.__findnode(index)
crt.val = val
def getItem(self, index):
index = self.__checkidx(index)
crt, _ = self.__findnode(index)
return crt
def getIndex(self, val):
"""获取第一个值为val的元素的位置。"""
if self.isEmpty():
raise IndexError("this linked list is empty.")
i = 0
crt = self.head
while i < self.__length:
if crt.val == val:
return i
crt = crt.next
i += 1
return -1
def clear(self):
self.head = None
self.__length = 0
def clone(self):
"""深度克隆当前链表。修改当前链表不会影响克隆链表。"""
new = LinkedList(0)
this_crt = self.head
new_crt = new.head
while this_crt:
new_crt.next = ListNode(this_crt.val)
new_crt, this_crt = new_crt.next, this_crt.next
new.head = new.head.next
new.length = self.__length
return new
def checkclosedcycle(self, dismiss=False):
"""检查当前链表是否存在闭环,获取闭环的位置及长度。
无闭环返回 (False, 0),否则返回 (闭环位置, 闭环长度)。
若 dissmiss=True,则解除闭环。
注意:解除闭环可能导致链表长度发生变化!"""
slow = fast = self.head
exist = False
ciclen = 0
while fast and fast.next:
slow = slow.next
fast = fast.next.next
ciclen += 1
if slow is fast and fast is not None:
exist = True
break
if exist: # 获取闭环位置
seeker, end = self.head, slow
idx = 0
while seeker is not slow:
seeker = seeker.next
end = slow
slow = slow.next
idx += 1
if dismiss:
end.next = None
self.__length = idx + ciclen
return (idx, ciclen)
else:
return (False, 0)
def __repr__(self):
if self.isEmpty():
return "{}"
node = self.head
ans = "{"
for _ in range(self.__length): # 采用长度判断而非node is not None,防止死循环
ans += str(node.val) + ','
node = node.next
ans = ans[:-1] + "}"
return ans
def __getitem__(self, ind):
if isinstance(ind, int):
return self.getItem(ind)
elif isinstance(ind, slice): # 切片检索
ans = []
idxs = list(range(self.__length)[ind])
if ind.step < 0:
idxs.reverse()
i, crt = 0, self.head
while i < self.__length and idxs:
if i == idxs[0]:
ans.append(crt)
idxs = idxs[1:]
i, crt = i + 1, crt.next
if ind.step < 0:
ans.reverse()
return ans
def __setitem__(self, ind, val):
self.update(ind, val)
def __len__(self):
return self.__length
def __iter__(self):
return LinkedListIterator(self)
def __add__(self, operand):
ans = self.clone()
ans.extend(operand)
return ans
class LinkedListIterator:
"""迭代器,主要用作for循环访问LinkedList。"""
def __init__(self, llist):
self.crt = llist.head
self.__length = len(llist)
def __iter__(self):
return self
def __next__(self):
if self.crt is not None:
prev = self.crt
self.crt = self.crt.next
return prev
else:
raise StopIteration
Serie de estructura de datos de Python: lista vinculada de una sola vía de Python, admite índice negativo, índice de división, para acceso iterativo, búsqueda de posición de circuito cerrado, adecuado para la práctica de LintCode
Guess you like
Origin blog.csdn.net/liuqixuan1994/article/details/103789486
Recommended
Ranking