双指针法在链表上的应用:LeetCode 206. 反转链表 LeetCode 19.删除链表的倒数第N个节点 LeetCode 160.链表相交 Leet Code 142.环形链表II

LeetCode 206. 反转链表 

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def reverseList(self, head):
        """
        :type head: Optional[ListNode]
        :rtype: Optional[ListNode]
        """

        dummy_head = ListNode(val=0, next=None)         ### 虚拟头结点设置
        dummy_head.next =  head     
        

        #### 与反转数组不同,反转数组是针对数组下标进行修改,链表需要根据指针
        if dummy_head.next == None or head.next == None:   ##空链表 / 只有一个元素的链表
            return dummy_head.next
        else:
            left, right = dummy_head, dummy_head.next

        
        while right != None:            #### 注意链表的操作顺序,这个是关键
            if left == dummy_head:
                temp = right.next
                left.next = None
                right.next = None
                left = right
                right = temp
            else:
                temp = right.next
                right.next = left
                left = right
                right = temp
            
        if right == None:
            dummy_head.next = left
        
        return dummy_head.next

思路:

  • 对于虚拟头结点部分,需要断开虚拟头结点的next指针和将头结点的指针改为尾节点,即将头节点的指针改为None
  • 对于非虚拟头结点部分,需要通过两个指针来进行操作,其中需要先创建一个temp变量来存储right.next,以获得下一次移动的位置,因为后续right.next会被修改了。随后进行两个相邻节点的反转操作,并将双指针同步右移一位。

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

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: Optional[ListNode]
        :type n: int
        :rtype: Optional[ListNode]
        """
        
        ### 删除倒数第N个节点,意味着我们要在倒数第N+1个节点位置进行操作,对倒数第N个节点进行删除。
        ### 单向链表只能单向移动,要找到倒数第N+1节点的位置,可以使用滑动窗口,滑动窗口的大小利用双指针实现

        dummy_head = ListNode(val=0, next=None)
        dummy_head.next = head
        left, right = dummy_head, dummy_head
        k = 0
        while k != n+1:             ### 先满足滑动窗口大小
            right = right.next
            k += 1
        while right != None:        ### 找到倒数第n+1个节点
            left = left.next
            right = right.next
        
        delete = left.next
        left.next = delete.next
        delete = None

        return dummy_head.next

思路:

  •  删除倒数第N个节点,意味着我们要在倒数第N+1个节点位置进行操作,对倒数第N个节点进行删除。

  • 单向链表只能单向移动,要找到倒数第N+1节点的位置,可以使用滑动窗口,滑动窗口的大小利用双指针实现

LeetCode 160.链表相交

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        dummy_head_A = ListNode(0)
        dummy_head_B = ListNode(0)
        dummy_head_A.next = headA
        dummy_head_B.next = headB

        ### 链表相交一定是取决于最短那方,因此最长那方不需要完全遍历
        A_length = self.get_Length(dummy_head_A)
        B_length = self.get_Length(dummy_head_B)


        min_length = min (A_length, B_length)   
        if min_length == 0:         ### 存在空链表
            return None
        k = 0
        if A_length > B_length:     ### B是最短
             while k != A_length - min_length:
                headA = headA.next
                k += 1
        elif A_length < B_length:
            while k != B_length - min_length:
                headB = headB.next
                k += 1
        
        while headA != None and headA != headB:
            headA = headA.next
            headB = headB.next
        if headA == headB and headA != None:
            return headA
        else:
            return None

    def get_Length(self, head):
        right = head.next
        total_num = 0
        if head.next == None:
            return total_num
        else:
            while right != None:
                right = right.next
                total_num += 1 
            return total_num

思路:

  • 链表相交一定取决于最短那方,最长那方不需要都进行遍历。因此需要求出两个链表的各自长度,并将最长链表的起始遍历位置改为其长度 - 最短链表长度。之后链表同时进行遍历判断。
  • 链表相交是指针相等,因此通过判断指针是否一致就可以判断出是否相交,要注意指针相等不一定是相交,因为还有None的情况需要排除。

Leet Code 142.环形链表II

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        
        ### 这道题需要画图加以理解,涉及数学推理的理解。
        dummy_head = ListNode(0)
        dummy_head.next = head

        if head == None or head.next == None:       ## 空链表 / 单元素链表 不存在环状
            return None
        
        slow, fast = head, head

        ### 判断不是环状。fast和slow指针永远不会相遇。因此可以转换为“链表相交”问题的思路
        ### 判断是环状。首先需要找到二者在环内相遇时,fast指针的位置
        while fast == head or slow != fast:
            if fast == None or fast.next == None:
                return None
            slow = slow.next
            fast = fast.next.next  
            if slow == fast:        ### 如果不加这句,会陷入死循环,因为相交点在头结点的话,while循环会一直执行
                break

        slow = head
        fast = fast

        while slow != fast:
            slow = slow.next
            fast = fast.next
        
        return slow   ### 此时为刚好进入环的索引节点    

思路:

  • 首先需要判断链表的长度是否能形成环状。
  • 记住结论:fast比slow速度快一位的话,判断 fast 在移动过程中是否会为None,如果会,则不是环。反之,fast 跟 slow一定会在环内相遇,此时fast 在环内相遇位置开始, slow在起点位置开始,二者以相同一位的速度进行移动,最终一定是在相交点相遇。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值