python | 算法-链表相关算法练习


承接-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表?

参考链接 1

参考链接 2

自己动手实现,记录如下:

其中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()

💡 其中涉及的小知识:

1 -> python @property的介绍与使用

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

八、两个单链表相交的问题

image

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()

课程中链表有关的题目练习汇总markdown笔记在这里

持续更新,写一点更一点

posted @ 2022-08-18 21:01  万国码aaa  阅读(55)  评论(0编辑  收藏  举报