【图解算法数据结构】(七)双指针

本文介绍了《剑指Offer》中关于链表节点删除、数组奇偶排序、寻找链表倒数第k个节点、合并排序链表以及找两链表交点等经典算法题目的解决方案。涉及双指针、哨兵节点、位运算等技巧,旨在提升对链表和数组操作的理解与应用。
摘要由CSDN通过智能技术生成

一、剑指 Offer 18. 删除链表的节点

1.1 题求

1.2 求解

法一:哨兵节点 + 遍历节点

# 32ms - 96.24%

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

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        # 哑节点 / 哨兵节点
        dummy = ListNode(-1)
        dummy.next = head
        # 辅助左 / 右指针
        left = dummy
        right = dummy.next
        while right:
            # 找到指定节点, 直接跳接
            if right.val == val:
                left.next = right.next
                break
            # 没找到指定节点, 指向下一组
            else:
                left = right
                right = right.next
        # 返回新链表头节点        
        return dummy.next    

官方说明 

# 36ms - 89.05%
class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if head.val == val: 
            return head.next

        pre, cur = head, head.next
        while cur and cur.val != val:
            pre, cur = cur, cur.next
        if cur: 
            pre.next = cur.next
        return head

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/505fc7/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/509cy5/


二、剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

2.1 题求

2.2 求解

法一:双指针

# 40ms - 94.54%
class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        n = len(nums)
        left = 0
        right = n - 1
        while left < right:
            # 令 left 指向一个偶数
            while nums[left] % 2 == 1:
                left += 1
                # 针对全奇数数组
                if left == n:
                    return nums
                
            # 令 right 指向一个奇数
            while nums[right] % 2 == 0:
                right -= 1
                # 针对全偶数数组
                if right == -1:
                    return nums
            
            # 针对已排序数组
            if left < right:
                # 奇偶交换
                nums[left], nums[right] = nums[right], nums[left]
                # 继续移动
                left += 1
                right -= 1
                
        return nums

法一:优化

# 36ms - 98.19%
class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        left = 0
        right = len(nums) - 1
        while left < right:
            # 令 left 指向一个偶数
            while left < right and nums[left] & 1 == 1:  # 使用 & 位运算代替 % 取余更快
                left += 1
            # 令 right 指向一个奇数
            while left < right and nums[right] & 1 == 0:  # 使用 & 位运算代替 % 取余更快
                right -= 1
            # 奇偶交换
            nums[left], nums[right] = nums[right], nums[left]
        return nums

官方说明 

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        i, j = 0, len(nums) - 1
        while i < j:
            while i < j and nums[i] & 1 == 1: 
                i += 1
            while i < j and nums[j] & 1 == 0: 
                j -= 1
            nums[i], nums[j] = nums[j], nums[i]
        return nums

2.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v8a6t/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v3rqr/


三、剑指 Offer 22. 链表中倒数第 k 个节点

3.1 题求

3.2 求解

法一:数组 (以空间换时间)

# 32ms - 87.75
class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        node_list = []
        while head:
            node_list.append(head)
            head = head.next
            
        return node_list[-k]

法二:双指针

# 28ms - 96.00%
class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:

        # 快、慢指针
        slow = fast = head
        # 链表总长(快指针步数)、链表半长(慢指针步数)
        len_list = half_index = 0
        while fast:
            # 慢指针走第一步
            slow = slow.next
            half_index += 1
            # 快指针走第一步
            fast = fast.next
            len_list += 1
            # 快指针走第二步
            if fast:
                fast = fast.next
                len_list += 1
                
        # 目标节点索引, 倒数第 k 个即正数第 len_list - k 个        
        target_index = len_list - k

        # 目标节点在慢指针右侧, 则从慢指针开始
        if target_index >= half_index:
            # 从慢指针所指中部出发需要走的步数
            steps = target_index - half_index  
            while steps:
                slow = slow.next
                steps -= 1  # 需走步数 -1
            return slow
        
        # 目标节点在慢指针左侧, 则从头节点开始
        while target_index:
            head = head.next
            target_index -= 1
        return head

官方说明

# 28ms - 95.89%
# 非常巧妙的解法!!!
class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        former, latter = head, head
        for _ in range(k):  # 先走
            former = former.next
        while former:  # 后走
            former, latter = former.next, latter.next
        return latter

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        former, latter = head, head
        for _ in range(k):
            if not former:  # 考虑越界问题
                return
            former = former.next
        while former:
            former, latter = former.next, latter.next
        return latter

3.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58tl52/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/588dhc/


四、剑指 Offer 25. 合并两个排序的链表

4.1 题求

4.2 求解

法一:双指针

# 40ms - 98.21%
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        
        if not l1:
            return l2
        elif not l2:
            return l1
        
        head = dummy = ListNode(-1)
        
        while l1 and l2:
            if l1.val <= l2.val:
                head.next = l1
                l1 = l1.next
            else:
                head.next = l2
                l2 = l2.next
            head = head.next
        
        head.next = l2 if l2 else l1

        return dummy.next

官方说明 

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        cur = dum = ListNode(0)
        while l1 and l2:
            if l1.val < l2.val:
                cur.next, l1 = l1, l1.next
            else:
                cur.next, l2 = l2, l2.next
            cur = cur.next
        cur.next = l1 if l1 else l2
        return dum.next

4.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5vq98s/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5v6htd/


五、剑指 Offer 52. 两个链表的第一个公共节点

5.1 题求

5.2 求解

法一:双指针

# 120ms - 98.15%
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        # 起点
        left, right = headA, headB
        # 遍历后交换起点再遍历
        while left != right:
            left = left.next if left else headB
            right = right.next if right else headA
        # 交叉点处相等, 非空交叉, 空则平行
        return left  # right 也可

5.3 解答

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        A, B = headA, headB
        while A != B:
            A = A.next if A else headB
            B = B.next if B else headA
        return A

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/oe5os3/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/oe95zm/


六、剑指 Offer 57. 和为 s 的两个数字

6.1 题求

6.2 求解

法一:哈希集合

# 120ms - 65.21%
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashset = set()
        for n in nums:
            diff = target - n  # 作差
            if n in hashset:   # 符合
                return [diff, n]
            hashset.add(diff)  # 记录

官方说明

# 88ms - 97.77%
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 对撞双指针
        i, j = 0, len(nums)-1
        while i < j:
            s = nums[i] + nums[j]  # 当前和
            if s > target:  # 当前和过大, 减小较大值
                j -= 1
            elif s < target:   # 当前和过小, 增大较小值
                i += 1
            else: 
                return [nums[i], nums[j]]  # 当前和满足
        return []

6.3 解答

参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/5832fi/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58qpje/


七、剑指 Offer 58 - I. 翻转单词顺序

7.1 题求

7.2 求解

法一:字符串方法

# 28ms - 96.58%
class Solution:
    def reverseWords(self, s: str) -> str:
        return ' '.join(s.strip().split()[::-1])

法二:迭代 

# 44ms - 42.57%
class Solution:
    def reverseWords(self, s: str) -> str:
        # 左边界 + 检测全空字符串
        start = 0
        while start < len(s) and s[start] == ' ':
            start += 1
        if start == len(s):
            return ''
        
        # 右边界
        index = len(s) - 1
        while s[index] == ' ':
            index -= 1

        # 总字符串/单词 列表
        string = []
        word = []
        
        # 从右往左遍历
        while index >= start:
            # 记录当前 word 的有效 char 
            if s[index] != ' ':
                word.append(s[index])
            # 遇到空格
            else:
                if word:
                    # 将当前有效的 word 逆序加入 string
                    string.append(''.join(word[::-1]))
                    word.clear()
            # 左移一位
            index -= 1
    
        string.append(''.join(word[::-1]))
        return ' '.join(string)

官方说明 

# 40ms - 59.59%
class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip()                            # 删除首尾空格
        i = j = len(s) - 1
        res = []
        while i >= 0:
            while i >= 0 and s[i] != ' ': 
                i -= 1 # 搜索首个空格
            res.append(s[i+1: j+1])              # 添加单词
            while i >= 0 and s[i] == ' ': 
                i -= 1 # 跳过单词间空格
            j = i                                # j 指向下个单词的尾字符
        return ' '.join(res)                     # 拼接并返回

class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip()         # 删除首尾空格
        strs = s.split()      # 分割字符串
        strs.reverse()        # 翻转单词列表
        return ' '.join(strs) # 拼接为字符串并返回
class Solution:
    def reverseWords(self, s: str) -> str:
        return ' '.join(s.strip().split()[::-1])

7.3 解答

 参考资料:

《剑指 Offer 第二版》

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/586ecg/

https://leetcode-cn.com/leetbook/read/illustration-of-algorithm/58kxyv/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值