双指针-两个链表的第一个公共节点、环形链表 II、和为s的两个数字、翻转单词顺序

本文解析了剑指 Offer 中的链表相关问题,包括删除节点、查找倒数第k个节点、合并排序链表、寻找公共节点、环形链表检测、数组调整顺序和数字组合。通过实例演示和代码优化,展示了如何高效解决这些问题并理解核心算法原理。
摘要由CSDN通过智能技术生成

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

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。题目保证链表中节点的值互不相同。

pre、node分别指前一个节点、要判断的节点

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if head.val == val: return head.next
        pre, node = head, head.next
        while node:
            if node.val==val:
                pre.next = node.next
                node = pre.next
            else:
                pre = node
                node = node.next
        return head

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

倒数第三个,k=3
- - - - - - - None
- - - - k - - None
i. - - j. - - - None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        i = j = head
        for _ in range(k):
            if j:
                j = j.next
            else: return i # k比链表长
        while j:
            i=i.next
            j=j.next
        return i

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

输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
我的要判断条件,以l1或者l2为头。参考答案自己生成一个头节点,不需要多余的判断语句。

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1: return l2
        if not l2: return l1
        if l1.val <= l2.val:
            head = l1
            l1 = l1.next
        else:
            head = l2
            l2 = l2.next
        cur = head
        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 head

参考答案

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

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

该题想不到,涉及巧妙的数学计算,与下面找到环形入口相似。
在这里插入图片描述
在这里插入图片描述

解释: 这里把最后None也当成节点

两个链表长度分别为L1+C、L2+C, C为公共部分的长度,按照楼主的做法: 第一个人走了L1+C步后,回到第二个人起点走L2步;第2个人走了L2+C步后,回到第一个人起点走L1步。 当两个人走的步数都为L1+L2+C时就相遇了。

不相交的情况设A链表长度是a B链表长度是b 最后a+b = b+a 所以一定是一起走到None这个节点。可以理解为两条链表最后都指向了同一个 null (None)节点,代替了不相交的特殊情况。 非常的巧妙。

而不是
node1 = node1.next if node1.next else headB

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        node1, node2 = headA, headB

        while node1 != node2:
            node1 = node1.next if node1 else headB # 当node1走到A的None节点,它下一步到B
            node2 = node2.next if node2 else headA
        # A和B等长时,第一遍就知道结果
        return node1

leetcode142. 环形链表 II

https://leetcode-cn.com/problems/linked-list-cycle-ii/
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结:
f=2s (快指针每次2步,路程刚好2倍)
f = s + nb (相遇时,刚好多走了n圈)
推出1. 第一次相遇,slow = nb
2. 从head结点走到入环点需要走 : a + nb
3. 而slow已经走了nb,那么slow再走a步就是入环点了。
4. 由3得出,起始距离入口 = 第一次相遇位置 + a

如何知道slow刚好走了a步? 从head开始,和slow指针一起走,相遇时刚好就是a步

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

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

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
在这里插入图片描述

x&1 位运算 等价于 x % 2x%2 取余运算,即皆可用于判断数字奇偶性。

class Solution:
    def exchange(self, nums: List[int]) -> List[int]:
        i, j = 0, len(nums)-1
        while i<j: # 可能不在这发生i==j
            while i<j: # 不能等于,当i==j就会结束
                if nums[i]%2==1: i+=1
                else: break
            while j>i:
                if nums[j]%2==0: j-=1
                else: break
            if i!=j:
                tmp = nums[i] 
                nums[i] = nums[j]
                nums[j] = tmp

        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

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

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

在这里插入图片描述

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 []

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

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。

有两个细节处理
在这里插入图片描述

python中split()和split(’ ')的区别

方法一:
利用 “字符串分割”、“列表倒序” 的内置函数 (面试时不建议使用) ,可简便地实现本题的字符串翻转要求。
在这里插入图片描述

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

方法二:双指针,以单词为单位,找出每个单词作为列表元素
在这里插入图片描述

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 s[i] == ' ': i -= 1 # 跳过单词间空格 # i=-1 其实指最后一个
            j = i # j 指向下个单词的尾字符
        return ' '.join(res) # 拼接并返回
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值