python | 算法-链表相关算法练习
针对b站视频左神算法与数据结构,自己练习对应的python代码
相关链接:
1️⃣b站视频地址
2️⃣视频笔记(其实主要是题目截图)
六、 复制含有随机指针节点的链表
function 1 -> hash表
# 复制含有随机指针节点的链表
# 用hash表
class Node():
def __init__(self, value=None, next=None, rand=None):
self.value = value
self.next = next
self.rand = rand
def CopyList(head):
hash = {} # key-value
h = head
while h is not None:
hash[h] = Node(h.value) # key=old node; value=new node
h = h.next
h = head
while h is not None:
hash[h].next = h.next # new.next = old.next
hash[h].rand = h.rand # new.rand = old.rand
h = h.next
return hash[head] # hash[old head] -> new head
function 2 -> 不用hash表
难点:rand指针的随机性,如何找到复制的链表各个点的rand指针指定的位置?
先依附于原链表创建新节点,这样新表每个节点与原表各个节点之间就有一一对应位置,则根据原表的rand指针指定的位置就可以找到新表对应的位置,就可以先解决rand指针,再依次将新节点从原表中取下,同时连上next指针。
代码:
def CopyList2(head):
if head is None: return None
# step1 create new nodes
h = head
while h is not None:
next = h.next # next -> save next node
h.next = Node(h.value) # h.next -> new node
h.next.next = next # new node -> next
h = next # h move next
# step2 rand part
h = head
while h is not None:
next = h.next.next # next -> save next node of old list
cur_copy = h.next # cur_copy -> new node
cur_copy.rand = h.rand.next if h.rand is not None else None # find rand space or None
h = next
# step3 next part and split
h = head
res = head.next # new list head node
while h is not None:
next = h.next.next
cur_copy = h.next
cur_copy.next = next.next if next is not None else None # new list
h.next = next # old list
h = next
return res
补充知识:Python中如何自己实现hash表?
其中Array是存储结构
Slot是每一对key-value的记录结构
若干Slot构成一张HashTable
# Python中实现hash表结构
class Array(): # Array -> use to save hash table items
def __init__(self, size=32, init=None):
self._size = size
self._items = [init] * size
def __len__(self):
return self._size
def __getitem__(self, index):
return self._items[index]
def __setitem__(self, index, value):
self._items[index] = value
def clear(self, value=None):
for i in range(len(self._items)):
self._items[i] = value
def __iter__(self):
for item in self._items:
yield item
class Slot(): # Slot -> save each key-value
def __init__(self, key, value):
self.key, self.value = key, value
class HashTable():
Unused = None # Unused -> init (means an unused slot)
Empty = Slot(None, None) # an empty slot
def __init__(self):
self._table = Array(size=8, init=HashTable.Unused)
self.length = 0
@property
def _load_factor(self): # how much space has been used
return self.length / float(len(self._table))
def __len__(self): # num of not empty slot
return self.length
def _hash(self, key):
return abs(hash(key)) % len(self._table)
def _find_key(self, key):
index = self._hash(key)
_len = len(self._table)
while self._table[index] is not HashTable.Unused: # an used slot
if self._table[index] is HashTable.Empty: # this slot's key and value have been removed
index = (index * 5 + 1) % _len # search another slot; cpython解决hash冲突的的一种方式。
continue # while -> next loop
elif self._table[index].key == key: # find it
return index
else:
index = (index * 5 + 1) % _len
return None
def _slot_can_insert(self, index):
return (self._table[index] is HashTable.Unused or self._table[index] is HashTable.Empty) # None or Slot(None, None)
def _find_slot_for_insert(self, key):
index = self._hash(key)
_len = len(self._table)
while not self._slot_can_insert(index): # collision
index = (index * 5 + 1) % _len
return index # Dont't worry, read follow -> add and _rehash
def add(self, key, value):
index = self._find_key(key)
if index is not None: # this key already exists
self._table[index].value = value # refresh value of this slot
return False # not new key
else:
index = self._find_slot_for_insert(key)
self._table[index] = Slot(key, value)
self.length += 1
if self._load_factor >= 0.8: # make sure always have space to add
self._rehash()
return True # new key-vlue
def _rehash(self):
old_table = self._table # save old hash table
newsize = len(self._table) * 2
self._table = Array(newsize, init=HashTable.Unused) # reinit
self.length = 0
for slot in old_table:
if slot is not HashTable.Unused and slot is not HashTable.Empty: # not None and not Slot(None, None)
index = self._find_slot_for_insert(slot.key)
self._table[index] = slot
self.length += 1
def get(self, key, default=None):
index = self._find_key(key)
if index is None:
return default # not find return what you want
else:
return self._table[index].value # find key's value
def remove(self, key):
index = self._find_key(key)
if index is None:
raise KeyError() # no this key to remove, so raise error
value = self._table[index].value # save key's value
self._table[index] = HashTable.Empty # this slot -> Slot(None, None)
self.length -= 1
return value
def __iter__(self):
for slot in self._table:
if slot not in (HashTable.Unused, HashTable.Empty):
yield slot.key
# 示例演示效果
def test_hash_table():
h = HashTable()
h.add('i', 0)
h.add('miss', 1)
h.add('u', 2)
assert len(h) == 3
assert h.get("i") == 0
assert h.get("miss") == 1
assert h.get("him", "No!") == "No!"
h.remove('i')
assert h.get('i') is None
assert len(h) == 2
assert sorted(h) == ["miss", "u"]
n = 50
for i in range(n):
h.add(i, i)
print(len(h)) # 52
for i in range(n):
assert h.get(i) == i
if __name__ == '__main__':
test_hash_table()
💡 其中涉及的小知识:
2 -> Python yield 使用浅析
七、判断单链表是否有环
# 判断单链表有没有环,有环则返回第一个入环节点
class Node():
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def getLoopNode(head):
if head is None or head.next is None or head.next.next is None:
return None
slow, fast = head.next, head.next.next
while fast != slow:
if fast.next is None or fast.next.next is None:
return None
slow = slow.next
fast = fast.next.next
fast = head # walk again from head node
while fast != slow:
slow = slow.next
fast = fast.next
return slow
def test_getLoopNode():
n1 = Node(1)
n2 = Node(2)
n3 = Node(3)
n4 = Node(4)
n5 = Node(5)
n6 = Node(6)
n7 = Node(7)
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n6
n6.next = n7
n7.next = n4
res = getLoopNode(n1)
print(res.data if res is not None else None)
if __name__ == '__main__':
test_getLoopNode() # 4
八、两个单链表相交的问题
class Node():
def __init__(self, data=None, next=None):
self.data = data
self.next = next
def getLoopNode(head):
if head is None or head.next is None or head.next.next is None:
return None
slow, fast = head.next, head.next.next
while fast != slow:
if fast.next is None or fast.next.next is None:
return None
slow = slow.next
fast = fast.next.next
fast = head # walk again from head node
while fast != slow:
slow = slow.next
fast = fast.next
return slow
def getIntersectNode(head1, head2):
if head1 is None or head2 is None:
return None
# have loop or not
loop1 = getLoopNode(head1)
loop2 = getLoopNode(head2)
if loop1 is None and loop2 is None:
return noLoop(head1, head2)
elif loop1 is not None and loop2 is not None:
return bothLoop(head1, loop1, head2, loop2)
else:
return None
def noLoop(head1, head2):
h1, h2, n = head1, head2, 0
while h1 is not None:
n += 1
h1 = h1.next # head1 -> tail1
while h2 is not None:
n -= 1
h2 = h2.next # head2 -> tail2
if h1 != h2: # tail1 != tail2
return None
h1 = head1 if n > 0 else head2 # h1 -> first go
h2 = head2 if h1 == head1 else head1
n = abs(n)
while n > 0:
h1 = h1.next
n -= 1
while h1 != h2:
h1 = h1.next
h2 = h2.next
return h1
def bothLoop(head1, loop1, head2, loop2):
if loop1 == loop2:
h1, h2, n = head1, head2, 0
while h1 != loop1:
n += 1
h1 = h1.next
while h2 != loop2:
n -= 1
h2 = h2.next
h1 = head1 if n > 0 else head2
h2 = head2 if h1 == head1 else head1
n = abs(n)
while n > 0:
h1 = h1.next
n -= 1
while h1 != h2:
h1 = h1.next
h2 = h2.next
return h1
else:
h1 = loop1.next
while h1 != loop1:
if h1 == loop2:
return h1
h1 = h1.next
else:
return None
# 测试效果
def test_getIntersectNode():
head1 = Node(1)
n1 = Node(9)
n2 = Node(2)
n3 = Node(3)
n4 = Node(4)
n5 = Node(5)
n6 = Node(6)
head2 = Node(0)
head1.next = n1
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n2
head2.next = n5
n5.next = n6
n6.next = n1
assert getIntersectNode(head1, head2) == n1
if __name__ == '__main__':
test_getIntersectNode()