Linked List Exercise 1
203. 移除链表元素
难度简单794
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入: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,我们又该怎么找他的父节点呢?
代码:
707. 设计链表
难度中等351
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val
和 next
。val
是当前节点的值,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
- 对于addathead,输入的就是原先的head
-
同理删除node就是get_node,然后让他的前后接起来
-
代码:
206. 反转链表
难度简单2247
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
思路:
- 使用双指针,一个叫prev,一个叫cur
- prev一开始指向None, cur指向第一个位置。
- 让temp保存cur的next,然后让cur指向prev
- 然后prev到cur,cur到temp,再循环
代码:
24. 两两交换链表中的节点
难度中等1226
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
思路:
- 这道题一定要设置虚拟头节点才好解决
- 易错点是更新时不是node_0 = node_2,而应该是node_0 = node_0.next.next
代码:
19. 删除链表的倒数第 N 个结点
难度中等1775
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
示例 1:
输入: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是我们要删除的
代码:
面试题 02.07. 链表相交
难度简单149
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
图示两个链表在节点 c1
开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
示例 1:
输入: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:
输入: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:
输入: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 。
思路:
- 这道题挺奇葩的,就当做着玩吧
- 在我看来,好多都给了,比如intervals和skipA , skipB ,这部相当于把答案都给了嘛。但是好像这些东西在代码中都不使用。
- 具体做法是在两个链表的头部标记
headA
和headB
。然后假设第一个长度为a,第二个长度为b,重合长度为c。 - 那么两个头部指针一直走,直到二者相等时停下。这里的相等是指指针相等,而不是单纯的数值相等
- 在走完自己的后,走另一个链表,他们就一定会遇到。headA走了a+(b-c), headB走了b+a-c,最终一定会相见
- 如果不相交,也就是会出现 c = 0,最终headA=headB=Null。所以循环判断是while(headA != headB)
代码:
注意是if A存在就行,不是A.next
142. 环形链表 II
难度中等1355
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
思路:
代码: