当前位置:   article > 正文

LeetCode刷题2:链表篇

LeetCode刷题2:链表篇

提示:本篇共7道力扣题目供大家食用,时间自行把控~

算法刷题系列笔记



作者有话说

  1、本篇是算法刷题系列文章的第 2,写此系列的目的是为了让自己对题目的理解更加深刻。

  2、本系列博客主要参考了卡哥的 代码随想录博客 以及 卡哥本人B站讲解的视频 代码随想录B站视频 ,强烈推荐给大家,因为本人学习中 Python为主,因此博客主要由 Python 代码呈现给大家,需要其他语言的版本,卡哥博客链接自取。


一、链表知识

1.1 什么是链表?

  链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是 数据域 一个是 指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

  链表在内存中不是连续分布的

1.2 链表的类型

  • 单链表: 只能从前往后进行遍历链表,结构如下图所示:
    单链表
  • 双链表: 每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点;既可以向前查询也可以向后查询。结构如下图所示:
    双链表
  • 循环链表: 链表首尾相连,结构如下图所示:
    循环链表

1.3 链表操作

  • 删除节点: 删除节点C的操作如下图所示:
    删除操作
  • 添加节点: 添加节点E;这里有三种插入方法:头插(设置虚拟头节点);尾插(先遍历链表找到链表的尾部,然后插入);在某个位置插入(遍历链表找到此位置,然后插入新节点)。添加节点操作如下图所示:

添加节点

二、经典题目

2.1 Leetcode203.移除链表元素

  • 原题地址: 203.移除链表元素
  • 题目描述: 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
  • 解题思路: 解决此题主要有两步:1、遍历链表找到满足题意的节点;2、删除对应的节点(只需让当前节点的前一个结点指向此节点后面的一个节点,即可达到删除节点的目的)。注意:添加一个虚拟头节点 dummy_head,使得删除头节点和其他节点的规则一致。
  • 代码如下:
class Solution:
    def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
        # 设置虚拟头节点
        dummy_head = ListNode(next=head)
        curNode = dummy_head
        while(curNode.next!=None):
            if(curNode.next.val == val):
                # 找到后,删除cur.next节点
                curNode.next = curNode.next.next 
            else:
            	# 没找到,更新curNode
                curNode = curNode.next
        return dummy_head.next
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2 LeetCode707 设计链表

  • 原题地址: 707.设计链表
  • 题目描述: 自己设计一个链表,具有以下功能:
  • get(index) 获取链表中第 index 个节点的值;
  • addAtHead(val) 在链表的第一个元素之前添加一个值为 val 的节点;
  • addAtTail(val) 将值为 val 的节点追加到链表的最后一个元素;
  • addAtIndex(index,val) 在链表中的第 index 个节点之前添加值为 val 的节点;
  • deleteAtIndex(index) 如果索引 index 有效,则删除链表中的第 index 个节点。
  • 解题思路:
  • 代码如下: 单链表 + 双链表

单链表:

class Node:
    def __init__(self, val):
        self.val = val
        self.next = None
class MyLinkedList:

    def __init__(self):
        self._head = Node(0)
        self._count = 0

    def get(self, index: int) -> int:
        if 0 <= index <self._count:
            temp = self._head
            for i in range(index + 1):
                temp = temp.next
            return  temp.val
        else:
            return -1

    def addAtHead(self, val: int) -> None:
        self.addAtIndex(0, val)

    def addAtTail(self, val: int) -> None:
        self.addAtIndex(self._count, val)

    def addAtIndex(self, index: int, val: int) -> None:
        if index < 0:
            index =0
        elif index > self._count:
            return 
        add_node = Node(val)
        curNode = self._head
        while index:
            curNode =  curNode.next
            index -= 1
        else:
            add_node.next, curNode.next = curNode.next, add_node
            # 计数累加
            self._count += 1

    def deleteAtIndex(self, index: int) -> None:

        if 0 <= index < self._count:
            curNode = self._head
            while index:
                curNode = curNode.next
                index -= 1
            curNode.next = curNode.next.next
            self._count -= 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

双链表: 相对于单链表,Node新增了prev属性

class Node:
    
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None

class MyLinkedList:

    def __init__(self):
    	# 虚拟节点
        self._head, self._tail = Node(0), Node(0)  
        self._head.next, self._tail.prev = self._tail, self._head
        # 添加的节点数
        self._count = 0 

    def _get_node(self, index: int) -> Node:
        # 当index小于_count//2时, 使用_head查找更快, 反之_tail更快
        if index >= self._count // 2:
            # 使用prev往前找
            node = self._tail
            for _ in range(self._count - index):
                node = node.prev
        else:
            # 使用next往后找
            node = self._head   
            for _ in range(index + 1):
                node = node.next
        return node

    def get(self, index: int) -> int:

        if 0 <= index < self._count:
            node = self._get_node(index)
            return node.val
        else:
            return -1

    def addAtHead(self, val: int) -> None:

        self._update(self._head, self._head.next, val)

    def addAtTail(self, val: int) -> None:
    
        self._update(self._tail.prev, self._tail, val)

    def addAtIndex(self, index: int, val: int) -> None:

        if index < 0:
            index = 0
        elif index > self._count:
            return
        node = self._get_node(index)
        self._update(node.prev, node, val)

    def _update(self, prev: Node, next: Node, val: int) -> None:

        # 计数累加
        self._count += 1
        node = Node(val)
        prev.next, next.prev = node, node
        node.prev, node.next = prev, next

    def deleteAtIndex(self, index: int) -> None:

        if 0 <= index < self._count:
            node = self._get_node(index)
            # 计数-1
            self._count -= 1
            node.prev.next, node.next.prev = node.next, node.prev
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

2.3 LeetCode206 反转链表

  • 原题地址: 206. 反转链表
  • 题目描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
  • 解题思路: 双指针法:看图解理解过程
       第1步: 设置两个指针,prevcurprev=None (空指针),cur=head (指向头节点);
       第2步: 设置 temp=cur.next 保存 cur 后面的节点,pre、cur 向后移动;
       第3步: 循环第 2 步,直到 cur=None 时结束,完成链表翻转;
       第4步: 返回 prev,即新链表的头节点。

解题思路图解

  • 代码如下:

法一:双指针

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur = head   
        pre = None
        while(cur!=None):
            # 保存一下 cur 的下一个节点
            temp = cur.next 
            cur.next = pre #反转
            #更新pre、cur指针
            pre = cur
            cur = temp
        return pre
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

法二:递归

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:

        def reverse(prev, cur):
            if not cur:
                return prev;
            temp = cur.next
            # 翻转操作
            cur.next = prev
            # 下一轮反转操作
            return reverse(cur, temp)
        return reverse(None, head)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

2.4 LeetCode24 两两交换链表中的节点

  • 原题地址: 24. 两两交换链表中的节点
  • 题目描述: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
  • 解题思路: 题目要求不修改节点内部值完成,也就是让我们拿 整个节点 去做交换操作。具体交换见图解。
       第1步: 设置4个指针,dummy_head = ListNode(next=head)、prev、cur、post,prev=dummy_head(指向A节点),cur=prev.next(指向B节点),post=prev.next.next(指向C节点);
       第2步: 根据 cur.next = post.next post.next = cur prev.next = post 更新各个指针;
       第3步: 循环第2步,直到 prev.next == None(偶数节点) and prev.next.next = None(奇数节点)时结束.

图解

  • 代码如下:
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        dummy_head = ListNode(next=head)
        prev = dummy_head
        
        # 交换
        while prev.next and prev.next.next:
            cur = prev.next
            post = prev.next.next
            
            # prev,cur,post对应最左,中间的,最右边的节点
            cur.next = post.next
            post.next = cur
            prev.next = post

            prev = prev.next.next
        return dummy_head.next
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.5 LeetCode19 删除链表的倒数第N个节点

  • 原题地址: 19.删除链表的倒数第N个节点
  • 题目描述: 给你一个链表,删除链表的 倒数n 个结点,并且返回链表的头结点。
  • 解题思路: 快慢指针法:
       第1步: 定义 fast指针slow指针 ,初始值为虚拟头结点;
       第2步: fast 首先走 n + 1 步 ,为什么是 n+1 呢,因为只有这样同时移动的时候 slow 才能指向删除节点的上一个节点(方便做删除操作);
       第3步: fastslow 同时移动,直到 fast 指向末尾,删除 slow 指向的下一个节点。
  • 代码如下:
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:

        dummy_head = ListNode()
        dummy_head.next = head

        # 第 1 步:初始化
        slow, fast = dummy_head, dummy_head
        # 第 2 步: fast先往前走n步
        while(n!=0): 
            fast = fast.next
            n -= 1
        # 第 3 步: fast 走到结尾后,slow 的下一个节点为倒数第N个节点
        while(fast.next!=None):
            slow = slow.next
            fast = fast.next
        # 删除节点
        slow.next = slow.next.next 
        return dummy_head.next
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2.6 LeetCode160 链表相交

  • 原题地址: 160. 链表相交
  • 题目描述: 给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null
  • 解题思路: 根据 快慢法则,走的快的一定会追上走得慢的。在这道题里,有的链表短,他走完了就去走另一条链表,我们可以理解为走的快的指针。那么,只要其中一个链表走完了,就去走另一条链表的路。如果有交点,他们最终一定会在同一个位置相遇。
  • 代码如下:
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:

        if headA is None or headB is None: 
            return None
        cur_a, cur_b = headA, headB     # 用两个指针代替a和b


        while cur_a != cur_b:
        	# 如果a走完了,那么就切换到b走
            cur_a = cur_a.next if cur_a else headB  
            # 同理,b走完了就切换到a    
            cur_b = cur_b.next if cur_b else headA      

        return cur_a
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.7 LeetCode142 环形链表II

  • 原题地址: 142.环形链表II
  • 题目描述: 给定一个链表的头节点 head ,返回链表开始 入环的第一个节点。 如果链表无环,则返回 null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。 不允许修改链表。
  • 解题思路: 分两步解决:1、判断链表是否环(快慢指针法);2、如果有环,如何找到这个环的入口(两个指针相遇的时候就是 环形入口的节点)。
  • 代码如下:
class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            # 如果相遇
            if slow == fast:
                p = head
                q = slow
                while p!=q:
                    p = p.next
                    q = q.next
                return p

        return None
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

三、推荐题目


总结

  链表篇到这里就结束了,若文章中有表述不当的地方还望大家多多指出,哈希表篇见吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/980658
推荐阅读
相关标签
  

闽ICP备14008679号