Linked List Exercise 1

Posted by Ethan on May 8, 2022

Linked List Exercise 1

203. 移除链表元素

难度简单794

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

img

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

思路:

  • 删除节点有两种情况
    • 首先是非头节点
    • 其次是头节点,解决方案是创建一个虚拟头节点
  • 具体上来说,首先建立虚拟头节点,并且下一个指向头节点,然后遍历链表
  • while循环在next = None 时停止
  • 如果找到了value匹配要删的那个,就是他的next变成next.next
  • 代码中易错点有两个
    • 首先是current_node初始化要是fake_head而不是head。这和下一点有关
    • 对于while循环中,注意我们看的是current_node的next的value,这样我们才能让current_node的next连到该连的人。如果是看current_node的value,那么如果要删current_node,我们又该怎么找他的父节点呢?

代码:

image-20220204183415370

707. 设计链表

难度中等351

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

思路:

  • 这道题因为没有给定Node的样子,所以我们既可以使用单向链表,也可以使用双向链表

  • 需要实现的功能是get(i): 得到index为i处的值

    • 需要一个计数器来判断index是否合法
    • 如果合法,从头到尾,使用for循环找到它,再返回它的val
  • 对于实现addAtHead和addAtTail,其实可以使用相同的方式,根据index来add

  • 对于实现deleteAtIndex,

  • 首先是使用单向链表

    • 在class Node处啥也不用写,然后在init处,不止要self,还要一个值val;再设置Node的next为Node
    • 只需要一个虚拟头节点
    • 再是MyLinkedList部分
      • 毫无疑问,首先是在init定义一个head也就是虚拟头节点,里头随便放一个值
      • 对于get功能,我们需要定义在这部分的init定义一个计数器,来判断当前的index是否合法
  • 如果是使用双向链表

    • 在class Node处的init还需要prev的定义

    • 在class MyLinkedList中的init主要就是虚拟头尾节点的定义以及节点计数器count

    • 除了虚拟头节点还需要虚拟尾节点

      前面的单链表我觉得很扯淡,之后看心情再补上吧

    • 如果使用双向链表,就是可以往前找和往后找,我们就不需要从头循环,我们可以根据index与count//2的大小关系来决定从头开始还是从屁股开始。我们定义这个从前或者从后进行for循环的功能叫get_node

    • 如此一来,get也就是根据index返回值这个函数,就可以用get_node找到node,然后返回node.val

    • 然后是addAtHead,addAtTail,addAtIndex都可以调用一个update函数。update接受三个变量:前一个节点,后一个节点和节点值

      • 对于addathead,输入的就是原先的head self.head, 原先head的next;对于addattail也是同理的
      • 对于addatindex就是先get_node找到那个node再update
    • 同理删除node就是get_node,然后让他的前后接起来

代码:

image-20220210173902110

image-20220210173924117image-20220210181544994

206. 反转链表

难度简单2247

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

img

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

img

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

思路:

  • 使用双指针,一个叫prev,一个叫cur
    • prev一开始指向None, cur指向第一个位置。
    • 让temp保存cur的next,然后让cur指向prev
    • 然后prev到cur,cur到temp,再循环

代码:

image-20220210183901366

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

难度中等1226

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

img

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

思路:

  • 这道题一定要设置虚拟头节点才好解决 image-20220210185815506
  • 易错点是更新时不是node_0 = node_2,而应该是node_0 = node_0.next.next
  • image-20220212115321793

代码:

image-20220212115101363

image-20220212115130457

19. 删除链表的倒数第 N 个结点

难度中等1775

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

思路:

  • 同样需要虚拟头节点, 便于返回链表能找到头
  • 设置快慢指针,假如删倒数第二个,我们就设计slow.next是倒数第二个,然后让slow.next=slow.next.next
  • 倒数第n个,fast就先走n+1步。只有这样同时移动的时候slow才能指向删除节点的上一个节点
  • fast指到末尾的null时,slow的next就是要删的。要让slow和fast差了n+1位,这样fast到达Null,slow.next是我们要删除的
  • img
  • img
  • imgimg

代码:

image-20220214143141293

面试题 02.07. 链表相交

难度简单149

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

图示两个链表在节点 c1 开始相交

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例 1:

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

img

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [0,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

img

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

思路:

  • 这道题挺奇葩的,就当做着玩吧
  • Picture1.png
  • 在我看来,好多都给了,比如intervals和skipA , skipB ,这部相当于把答案都给了嘛。但是好像这些东西在代码中都不使用。
  • 具体做法是在两个链表的头部标记headAheadB。然后假设第一个长度为a,第二个长度为b,重合长度为c。
  • 那么两个头部指针一直走,直到二者相等时停下。这里的相等是指指针相等,而不是单纯的数值相等
  • 在走完自己的后,走另一个链表,他们就一定会遇到。headA走了a+(b-c), headB走了b+a-c,最终一定会相见
  • 如果不相交,也就是会出现 c = 0,最终headA=headB=Null。所以循环判断是while(headA != headB)

代码:

image-20220212112431405

注意是if A存在就行,不是A.next

142. 环形链表 II

难度中等1355

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

img

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

img

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

img

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

思路:

代码: