Post

Krahets88道 双指针

判断子序列

Problem: 392. 判断子序列

思路

单词循环逐个对比各个字符串

解题方法

比较基础的双指针应用

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)

空间复杂度:

添加空间复杂度, 示例: O(1)

Code

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        """判断子序列"""
        i = j = 0
        if not s:
            return True
        while i < len(s) and j < len(t):
            if s[i] == t[j]:
                i += 1
                if i == len(s):
                    return True
            j += 1
        return False

链表的中间节点

Problem: 876. 链表的中间结点

思路

没思路,看了提示用快慢指针,也没做出来

解题方法

对于链表了解的不多,一直以为双指针指的是两个索引,但不清楚的是,对于链表而言,应将指针理解为引用

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)

空间复杂度:

添加空间复杂度, 示例: O(n)

Code

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        """
            链表的中间节点
            对于链表了解的不多,一直以为双指针指的是两个索引,但不清楚的是,对于链表而言,应将指针理解为引用
        """
        fast = slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        return slow

相交链表

Problem: 160. 相交链表

思路

没做出来,以下为Krahests的思路:

设HeadA到公共节点之前的长度为a,设HeadB到公共节点之前的长度为b,公共长度为c。

已知:a + c + b = b + c + a

即 HeadA到公共节点之前的长度 加上 公共长度 加上 HeadB到公共节点之前的长度 等于 HeadB到公共节点之前的长度 加上 HeadA到公共节点之前的长度

解题方法

通过同时从headA、headB开始循环,直至找到公共节点为止,如遍历完链表后仍未找到则返回空。

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)

空间复杂度:

添加空间复杂度, 示例: O(1)

Code

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        A, B = headA, headB
        while a != b:
            A = A.next if A else headB
            B = B.next if B else headA
        # 很神奇,一时竟未理解这种写法为何不会导致死循环,看了评论区找到了答案:即使A和B无相交节点也会再遍历到最后时结束,因为两链表末尾都有Null节点

两数之和 II - 输入有序数组

Problem: 167. 两数之和 II - 输入有序数组

思路、解题方法

已知数组按正序排列,使用双指针指向数组首尾。如果和大于目标值则将头指针向后移动来使和更大;如果和小于目标值则将尾指针向前移动来使和更小(俗称对撞双指针)。

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)

空间复杂度:

添加空间复杂度, 示例: O(1)

Code

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        i, j = 0, len(numbers) -1
        if len(numbers) <= 1:
            return None
        while i < j:
            if numbers[j] + numbers[i] > target:
                j -= 1
            elif numbers[j] + numbers[i] < target:
                i += 1
            else:
                return [i + 1, j + 1]

环形链表 II

Problem: 142. 环形链表 II

思路

啊!用了笨办法哈希表做出来了,看了K神的双指针讲解,又学到了。

解题方法

Krahets

设快慢指针(似乎对于链表则都是快慢指针,跟链表的特性有关),令fast每轮走2步,slow每轮走1步,会出现两种结果:

1.fast指针走过链表末端,说明链表无环,此时直接返回null,如果链表存在环,则双指针一定会相遇,因为每走1轮,fast与slow的间距 +1,fast一定会追上slow。

2.当fast = slow时,两针再环中第一次相遇,设链表共 a + b个节点,其中链表头部到链表入口有a个节点,链表环有b个节点,设两指针各走了f、s步,则有:

1
2
3
1.fast走的步数是slow步数的两倍,即f = 2s

2.fast比slow夺走了n个环的长度,即f = s + nb(双指针都走过a步,然后再环内绕圈直到重合,重合时fast比slow多走环的长度整数倍)

以上两式可得 f = 2nb , s = nb , 即fast和slow指针分别走了2n,n个环的周长。

如果让指针从链表头走k步,那么所有走到链表入口节点时的部署就是k = a + nb。而目前slow已经走了nb步,那么再走a步,就可以到达环的入口,而这时可以用一个新指针,从头列表与slow一起走,当他们相遇的时候,则说明已经找到环入口。

双指针第二次相遇

  1. 令fast重新指向链表头部节点,此时f = 0 , s = nb。
  2. 令slow和fast每轮只走一步。
  3. 当fast指针走到f = a时,slow指针走到s = a + nb步。此时双指针重合,并同时指向链表环入口,返回slow指向的节点即可。

答案出处: 环形链表 II(双指针,清晰图解)-Krahets

感觉这个题的双指针解法好难啊,看了三四遍才看懂。

复杂度

时间复杂度:

添加时间复杂度, 示例: O(n)

空间复杂度:

添加空间复杂度, 示例: O(1)

Code

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        dic = dict()
        while head:
            dic[head.val] = head 
            head = head.next
            if head.val in dic.keys():
                return dic[head.val]
        return None

# Krahets双指针解法

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast, slow = head, head
        while True:
            if not (fast and fast.next): return
            fast, slow = fast.next.next, slow.next
            if fast == slow: break
        fast = head
        while fast != slow:
            fast, slow = fast.next, slow.next
        return fast

反转字符串中的单词

Problem: [409. 最长回文串]

思路

解题方法

复杂度

时间复杂度:

添加时间复杂度, 示例: O()

空间复杂度:

添加空间复杂度, 示例: O()

Code


无重复字符的最长字串

Problem: [409. 最长回文串]

思路

解题方法

复杂度

时间复杂度:

添加时间复杂度, 示例: O()

空间复杂度:

添加空间复杂度, 示例: O()

Code


三数之和

Problem: [409. 最长回文串]

思路

解题方法

复杂度

时间复杂度:

添加时间复杂度, 示例: O()

空间复杂度:

添加空间复杂度, 示例: O()

Code


This post is licensed under CC BY 4.0 by the author.