LC 24, 19, 160, 142 Second part of Linked List.

文章介绍了链表的几种常见操作,包括206题的反转链表,通过两种方法实现,一种是改变链表节点的指向,另一种是创建新的返回链表。19题删除链表的倒数第N个节点,通过双指针法实现。160题查找两个链表的交点,探讨了递归、存储比较和双指针法。142题检测环形链表,讨论了双指针法和存储检查策略。
摘要由CSDN通过智能技术生成

LC 24, 19, 160, 142 Second part of Linked List.


Second part of Linked List.

LC 206. Reverse Linked List


206.反转链表

Logic

  • Either create a new one or reverse the link.
# LC 206, reverse the link
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
    parent = None
    cur = head
    while cur:
        temp = cur.next
        cur.next = parent
        parent = cur
        if not temp:
            break
        cur = temp
    return cur
# LC 206, create a new returned linked list
def reverseList(self, head):
    """
    :type head: ListNode
    :rtype: ListNode
    """
    cur = head
    ret = None
    if cur is not None:
        ret = ListNode(cur.val)
        while cur.next is not None: 
            cur = cur.next
            new_node = ListNode(cur.val, ret)
            ret = new_node
    return ret

19. Remove Nth Node From End of List


19.删除链表的倒数第N个节点

Logic

Add a node of Dummy head to make the operation of deletion the same for all nodes, where dummy_head.next = head, then just return the dummy_head.next.

  • Duo ptrs with fast, slow stepping speed could help, where the fast ptr will tell when to stop, and at that time, the slow ptr will points to the exact node that we want to delete.

  • However, to delete cur, since this is single-direction linked list, we can’t just remove cur. We need to:

    • cur points to the node right before the node we want to delete,
    • then, cur.next = cur.next.next.
  • So cur.next is deleted.

  • For this to happend, the fast ptr need to walk for n+1 steps, instead of n.

  • Then after steps are walked in the first while loop, one need to check again if all n+1 step (you could think as if +1 is for the dummy_head as well) are all finished before the deletion.

def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
    dummy_head = ListNode(-1)
    dummy_head.next = head
    fast = dummy_head
    # walks n + 1 steps
    while fast is not None and n >= 0:
        fast = fast.next
        n -= 1
    slow = dummy_head
    while fast is not None:
        slow = slow.next
        fast = fast.next
    if not slow or n > 0:
        return dummy_head.next
    if slow.next:
        slow.next = slow.next.next
    return dummy_head.next

160. Intersection of Two Linked Lists


面试题 02.07. 链表相交

Which is the same of LC 160. Intersection of Two Linked Lists

Logic:

Recursion
  • The first thought is to solve it by recursion, and there are limited base cases:
    • Either of headA or headB is None, then they won’t share any nodes with the other;
    • headA == headB, then they share all nodes;
    • headA.next == headB, then they share all nodes from headB;
    • headB.next == headA, then they share all nodes from headA;
    • Otherwise, we could just reduce one node from either side and it concludes all cases. Then we would also need to compare all result and return the longer one.
  • It was worth noticing that this method compare all node in headA and neadB, which gave a Time complexity $ = O(m\cdot n)$, where m = len(headA), n = len(headB).
# LC 160
def getIntersectionNode(
        self, headA: ListNode, headB: ListNode
    ) -> Optional[ListNode]:
    if headA is None or headB is None:
        return None
    if headA == headB:
        return headA
    if headA.next == headB:
        return headB
    if headB.next == headA:
        return headA
    ret1 = self.getIntersectionNode(headA.next,headB)
    ret2 = self.getIntersectionNode(headA, headBnext)
    if ret1 and ret2:
        if ret1.next == ret2:
            return ret1
        return ret2
    if ret1:
        return ret1
    return ret2
  • Well, obviously, this is not good enough to be AC on LeetCode. Then I tried with brute force and store everything.
store and back comparison
  • Basically, I can store every nodes from headA and headB, then campare them backwards to reduce the comparison runtime to O ( n ) O(n) O(n).
# LC 160 store and back search
def getIntersectionNode2(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
    lst1 = []
    lst2 = []
    cur = headA
    while cur:
        lst1.append(cur)
        cur = cur.next
    cur = headB
    while cur:
        lst2.append(cur)
        cur = cur.next
    if len(lst1) == 0 or len(lst2) == 0:
        return None
    index = -1
    while index >= - len(lst1) and index >= - len(lst2) and lst1[index] == lst2[index]:
        index -= 1
    if index < -1 and index + 1 >= - len(lst1):
        return lst1[index + 1]
    return None
Duo Ptrs
  • If I only need to compare two linked lists for the same length, could I just consume both lists to the exact length that I would want to compare?

    • Yes! We only need to compare the whole length of the shorter linked list.
  • Then the logic basically becomes:

    • Find the Shorter list;
    • Eat the longer one to be left with exact length as the shorter list;
    • Then compare them at the same rate.
# LC 160 duo ptrs
def getIntersectionNode3(self, headA: ListNode,headB: ListNode) -> Optional[ListNode]:
    size1 = 0
    size2 = 0
    cur = headA
    while cur:
        size1 += 1
        cur = cur.next
    cur = headB
    while cur:
        size2 += 1
        cur = cur.next
    # print(size1, size2)
    if size1 > size2:
        cur = headA
        cur2 = headB
    else:
        cur = headB
        cur2 = headA
    # print(cur)
    # print(cur2)
    diff = abs(size1 - size2)
    while cur and diff > 0:
        cur = cur.next
        diff -= 1
    while cur and cur2 and cur != cur2:
        cur = cur.next
        cur2 = cur2.next
    return cur

19. Remove Nth Node From End of List


142.环形链表II

Logic

  • The brute force “store and check” works with:
    • runtime: O ( n   l o g ( n ) ) O(n \ log(n)) O(n log(n))
    • space: O ( n ) O(n) O(n)
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
    lst = set()
    cur = head
    cur2 = head
    while cur:
        if cur == cur2:
            return cur
        lst.add(cur)
        cur = cur.next
    return None

However, the duo ptrs method works elegantly based on a heavy math basis.

Quote from a physics perspective:
追及问题,相对速度与绝对速度 phys111
你把时间考虑进去
第一个图中 fast走了2n slow走了n 刚好入口相遇
出发点一样的 确实
都是环入口
是时间不一样
所以位移不一样
第二个图里面 高的那个弧线画的有点过了
应该是在环入口3前面一点点
刚好和slow相遇
这个你可以考虑
fast的绝对速度是俩个单位/单位时间
slow的绝对速度是1个/单位时间
那么fast相对于slow的相对速度就是1/个/单位时间
因为fast最多走2n
slow最多走n所以就相遇了

slow进圈后粒度是1
fast不可能跳过

考虑了相对速度 fast的速度也是1

就不能跳过

  • Namely, choose the slow ptr as the inertial frame of reference. Then fast ptr is chansing up with a speed of 1 $node / unit \ time $.
  • Since the granularity of slow ptr is 1. Then fast ptr will have to chase up to slow in the first run.

Summary:


  • Overall, the linked list problems are not so difficult, but the math problem for the last problem is still chricky. May need to take a look at it another time.

  • Total time: 4-5 hrs ish.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值