# -*- 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
Python数据结构系列——Python 单向链表,支持负数索引、切片索引、for迭代访问、闭环位置查找,适用LintCode刷题练习
猜你喜欢
转载自blog.csdn.net/liuqixuan1994/article/details/103789486
今日推荐
周排行