链表中相关算法探讨
前言
一、移除链表元素
- 力扣算法题目第 203 题:给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
- 前提:
- 列表中的节点数目在范围 [ 0,
1
0
4
10^4
104] 内
- 1 <= Node.val <= 50
- 0 <= val <= 50
1.1 思路分析
- 将要删除节点的前一个节点的next指向删除节点的下一个节点的地址,就可以删除这个节点。
1.2 解法探讨
1.2.1 直接删除
- 我们首先检查头节点head是否是要删除的节点,如果是,则不断前进直到找到不是要删除的节点或者到达链表末尾。
- 如果整个链表都是要删除的节点,则最终head会变成None。
- 接下来进入常规的遍历过程,从head开始遍历,对于每一个节点current,如果它的下一个节点是需要删除的节点,则跳过这个节点,否则就继续前进。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int):
if head is None:
return None
while head is not None and head.val == val:
head = head.next
cur = head
while cur is not None and cur.next is not None:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return head
1.2.2 创建虚拟头节点来删除
- 在涉及到 对链表的增删操作的时候,通常设置虚拟头节点会非常便于理解,同时操作也变得非常方便。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
dummy_head = ListNode(0, head)
cur = dummy_head
while cur.next is not None:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
return dummy_head.next
1.2.3 递归版删除
- 因为每个节点要进行的操作都一样,都是检查是否是要删除的节点,如果是,那么就删除,不是的话就判断下一个节点。
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]:
if head is None:
return None
if head.val == val:
return self.removeElements(head.next, val)
else:
head.next = self.removeElements(head.next, val)
return head
二、反转列表
- 力扣算法题目第 203 题:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
- 前提:
- 链表中节点的数目范围是 [ 0, 5000 ]
- -5000 <= Node.val <= 5000
2.1 思路分析
- 如果再定义一个新的链表,然后遍历原来的链表,再依次将原来链表的值插进新链表,这会浪费内存空间,不过我们下边也会介绍这种做法,其实只要改变头节点跟next的指针指向即可。
2.2 做法
2.2.1 创建新链表方式
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
reversed_head = None
current = head
while current:
new_node = ListNode(current.val)
new_node.next = reversed_head
reversed_head = new_node
current = current.next
return reversed_head
2.2.2 双指针法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
cur = head
pre = None
while cur:
tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
return pre
2.2.3 递归法
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
return self.reverse(head, None)
def reverse(self, cur: ListNode, pre: ListNode) ->ListNode:
if cur == None:
return pre
tmp = cur.next
cur.next = pre
return self.reverse(tmp, cur)
三、两两交换链表中的节点
- 力扣算法题目第 24 题:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
- 前提:
- 链表中节点的数目在范围 [ 0, 100 ] 内
- 0 <= Node.val <= 100
3.1 思路分析
- 两两交换链表中的节点,我们就需要两个指针指向要交换的两个节点,还需要保存第二个节点的next 防止丢失,循环遍历链表中的节点,依次完成这个动作。
- 也可以使用虚拟头节点,这样更好理解一点
3.2 解法探讨
3.2.1 不使用虚拟头节点(递归法)
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head is None or head.next is None:
return head
pre = head
cur = head.next
head = head.next.next
cur.next = pre
pre.next = self.swapPairs(head)
return cur
3.2.2 使用虚拟头节点
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0)
dummy.next = head
current = dummy
while current.next and current.next.next:
A = current.next
B = current.next.next
current.next = B
A.next = B.next
B.next = A
current = A
return dummy.next
四、删除链表的倒数第N个节点
- 力扣算法题目第 19 题:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
- 前提:
- 链表中结点的数目为 sz
- 1 <= sz <= 30
- 0 <= Node.val <= 100
- 1 <= n <= sz
4.1 思路分析
- 因为我们不知道倒数的第 n 个节点是正着数的第几个节点, 所以我们需要遍历两次,第一次拿到链表长度,第二次根据长度跟倒数的 n 个节点,就能推出来要删除正数第几个节点
- 还可以用双指针,双指针之间有n个节点, 那么当其中一个指针指到末尾的时候,另一个指针就是倒数的第 n 个节点
- 还可以使用递归来做
4.2 解法探讨
4.2.1 两次遍历
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
length = 0
tmp = head
while tmp:
length += 1
tmp = tmp.next
if length == n:
return head.next
i = 1
pre = head
while i < length - n:
pre = pre.next
i = i +1
pre.next = pre.next.next
return head
4.2.2 双指针法
4.2.2.1 一次遍历(不使用虚拟头节点)
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
pre = head
cur = head.next
i = 1
while i < n:
cur = cur.next
i += 1
if cur is None:
return head.next
while cur.next:
pre = pre.next
cur = cur.next
pre.next = pre.next.next
return head
4.2.2.2 一次遍历(使用虚拟头节点)
Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
first = second = dummy
for _ in range(n + 1):
first = first.next
while first:
first = first.next
second = second.next
second.next = second.next.next
return dummy.next
4.2.3 递归法
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
def helper(node):
if not node:
return 0
pos = helper(node.next)
if pos == n :
node.next = node.next.next
return pos + 1
dummy = ListNode(0, head)
helper(dummy)
return dummy.next
五、链表相交
5.1 思路分析
5.2 解法探讨
5.2.1
六、环形链表II
6.1 思路分析
6.2 解法探讨
6.2.1
总结
- 以上就是力扣中有关链表的题目的解题思路跟代码,我只是列举出来 我能想到的几种办法,如有其他解法,可以后台私信我。