leetcode刷题——链表

一、代码注意点:

(1)if not cur等价于if cur==None,if cur等价于if cur != None (2)虚拟头结点的构造方式:dummy = ListNode(0)或dummy=Listnode(next=head)

二、链表常用技巧:

(1)dummy_head,函数返回结果是这个结点的下一个位置。如果操作涉及了链表的第一个位置,dummy_head可以维护整个操作更加通用。(2)考察链表的操作其实就是考察指针的操作:包括反转链表、环形链表的判断和找环形点。

三、链表的题型总结

  • 找两个链表的交点、判断一个链表是否环形>>用龟兔比赛(快慢指针)的解法,fast一定会在交点or环点追上slow
  • 交换节点顺序【反转链表】

四、典型例题

(一)自身环形\两链相交

实质:找重复,重合处,用双指针、哈希表

面试题 02.07. 链表相交

  • 实质:判断两个链表的末尾是否存在相同的部分
  • 思路:让两个指针分别从A和B点往C点走,两个指针分别走到C后,又各自从另外一个指针的起点,也就是A指针第二次走从B点开始走,B指针同理,这样,A指针走的路径长度 AO + OC + BO 必定等于B指针走的路径长度 BO + OC + AO,这也就意味着这两个指针第二轮走必定会在O点相遇,相遇后也即到达了退出循环的条件。如果没有交点则返回None。
  • 代码学习:cur_a=cur_a.next if cur_a else headB # 如果a走完了,那么就切换到b走,cur_a指的是cur_a!=None
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        cur_a,cur_b=headA,headB
        while cur_a!=cur_b:
            cur_a=cur_a.next if cur_a else headB  # 如果a走完了,那么就切换到b走
            cur_b=cur_b.next if cur_b else headA  # b走完了就切换到a
        return cur_a  # 知道两个结点相遇,表示找到交点

141. 环形链表
判断是否存在环

【法一】哈希表法:用哈希表记录结点,如果结点重复出现则说明链表有环
  • 代码注意点:(1)hash表中加入结点是hash.add(cur);(2)要先判断是否在,这样比较快
  • 实质:判断是否存在环的本质在于是否存在重复元素,可以用哈希表来解决。或者快慢指针。
class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        hash=set()
        cur=head
        while cur:
            if cur in hash:return True
            else:
                hash.add(cur)
                cur=cur.next
        return False

【法二】龟兔相遇法(快慢指针):如果有环,则龟兔一定会相遇。兔子一次两步,乌龟一次一步。
        # 假设初始点是dummy,因为需要符合while的条件
        if not head or not head.next:return False
        start=ListNode(next=head)
        fast=start.next.next
        slow=start.next

        while slow!= fast:
            if not fast or not fast.next:return False
            slow = slow.next
            fast = fast.next.next
        return True

142. 环形链表 II
求开始入环的第一个节点

  • 代码注意点:(1)hash.get(),返回指定键的值,如果键不在字典中返回默认值 None 或者设置的默认值。因为是字典,所以要这么写,注意一下。
【法一】哈希表法
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        Hash = {}
        cur = head
        while cur:
            if Hash.get(cur,-1) > 0: return cur # 已经出现过
            else:Hash[cur] = 1
            cur = cur.next
        return 
【法二】指针法
  • 思路:首先,fast和slow一定会遇到。其次,可以证明,当fast和slow相遇之后,从头结点出发的点和slow指针一定会在环口相遇。因此,需要在fast走到末尾前,验证if是否fast和slow会相遇,如果if相遇,就让头和slow一直走while,知道相遇
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        fast,slow=head,head
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
            if fast==slow:
                p=slow
                q=head
                while p!=q:
                    p=p.next
                    q=q.next
                return p
        return None

(二)结点顺序的交换

206. 反转链表

  • 指针法:
    cur:当前用来遍历的指针
    pre:要赋值的指针
    temp:保存原cur指向的位置
  • 实质:循环的两两交换元素顺序
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur=head
        pre=None  # 第一个元素最后指向的就是空的,当然也可以设置为pre=Listnode(next=head)这样的形式
        while cur!=None:
            temp=cur.next # 下一个遍历的位置
            cur.next=pre
            pre=cur   # 希望指向的位置
            cur=temp    # 遍历的位置
        return pre
  • 递归法
    reverse函数return的结果是,子问题解决之后的剩余问题的解决方式。
  • 代码注意点:if not cur可以写成if cur != None
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        def reverse(pre,cur):
            if not cur:return pre # 如果cur为None,则说明链表已经遍历完了,返回链表的头
            else:
                temp=cur.next
                cur.next=pre
            return reverse(cur,temp) # 进入新的递归
        return reverse(None,head)

92. 反转链表 II
https://leetcode-cn.com/problems/reverse-linked-list-ii/solution/dong-hua-tu-jie-fan-zhuan-lian-biao-de-z-n4px/

        #if not head.next:return head  # 只有一个元素的情况
        dummy = ListNode(next=head)
        pre = dummy
        count=1
        # cur先走到left
        while pre.next and count<=left-1:  # pre走left-1步,cur走到left
        #1到left需走left-1步
        #for _ in range(left-1):
            pre = pre.next
            count+=1
        cur = pre.next
        # 开始反转,一直到cur走到right
        while  count and count<=right-1:
            temp = cur.next   # 存储结点
            cur.next = temp.next
            temp.next = pre.next
            pre.next = temp
            count+=1
        
        return dummy.next

24. 两两交换链表中的节点

  • 只要涉及涉及头结点的变换都需要设置虚拟头结点
  • 涉及结点交换,一定要注意先后顺序
  • 时间复杂度: O ( n ) O(n) O(n),空间复杂度: O ( 1 ) O(1) O(1)
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        res = ListNode(next=head)   # 涉及头结点的变换都需要设置虚拟头结点,res用来保存虚拟头结点的位置
        pre = res
        
        while pre.next!=None and pre.next.next!=None:  # 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
            # 一共涉及三个结点:pre,cur,post对应最左,中间的,最右边的节点
            cur = pre.next
            post = pre.next.next
            # 交换结点顺序,前两个严格,第三个任意
            cur.next = post.next
            post.next = cur
            pre.next = post  
            
            # 更新循环
            pre = pre.next.next
        return res.next

(三)删除指定节点

19. 删除链表的倒数第 N 个结点
双指针法:

  • 本质:涉及倒数元素的操作,一般都用双指针,一个指针先走n步,随后两个指针一起走,则快指针走到最后的时候,慢指针指向要删除的位置。
  • 思路:要删除倒数第n个元素,则fast指针先走n+1步,之后fast和slow一起走。fast用于遍历,slow用于指向删除的索引位置。最后用覆盖法实现删除的操作。
  • 代码注意点:while pre.next!=None and pre.next.next!=None可以写成while pre.next and pre.next.next
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        res = ListNode(next=head)   # 涉及头结点的变换都需要设置虚拟头结点,res用来保存虚拟头结点的位置
        pre = res
        
        while pre.next!=None and pre.next.next!=None:  # 必须有pre的下一个和下下个才能交换,否则说明已经交换结束了
            # 一共涉及三个结点:pre,cur,post对应最左,中间的,最右边的节点
            cur = pre.next
            post = pre.next.next
            # 交换结点顺序,前两个严格,第三个任意
            cur.next = post.next
            post.next = cur
            pre.next = post  
            
            # 更新循环
            pre = pre.next.next
        return res.next  # 返回头结点的位置
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy_head=ListNode(next=head)  # 只要是最终返回链表的头结点,都需要设置虚拟头结点
        fast,slow=dummy_head,dummy_head
        # fast先走n+1步
        n=n+1
        while n!=0:
            fast=fast.next
            n-=1
        while fast!= None :  # 终止条件是fast走到最后
            fast=fast.next
            slow=slow.next
        # 删除slow指向的元素,即覆盖
        slow.next=slow.next.next
        return dummy_head.next # 提前存储好的虚拟头结点的next就是新链表的头结点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值