声明
今天是LeetCode 中等第1道题,以此作为练习记录,欢迎交流。文中也会给出所参考的文章链接,感谢前辈们的总结。
(手动比心ღ( ´・ᴗ・` ))
题目
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
正文
思路1:
删除倒数第n个节点,换种思路就是删除正着数第 L-n+1 个节点。
找到这个节点
删除
需要知道 L:
删除第 L-n+1 个节点,就是改变 L-n 个节点的指针。指向 L-n+2 个节点。
LeetCode官方解答
两次遍历实现:
第一次遍历得到链表长度
第二次遍历找到第 L-n 个节点,改变其指针。
实现:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
#添加亚节点
dummy = ListNode(0)
# 亚节点和head关系
dummy.next = head # 不是 head = dummy.next
count = 0
p = head
# 第一次遍历:得到链表长度
while p:
count += 1
p = p.next
# 第二次遍历:从哑节点开始,找到第 L-n 个节点
q = dummy
count = count - n
while count > 0:
count -= 1
q = q.next # 遍历结束后,q 指向第 L -n 个节点
# 删除第L-n+1 个节点,即 改变第L-n 个节点指针
q.next = q.next.next
# 返回链表地址
return dummy.next # 为什么需要哑节点;若链表只有头节点,则删除后dummy.next是NULL
为什么第二次遍历从 count - n 开始
代码写的过程:
先写两次遍历,其他随着补充。
思路2:
参考LeetCode 对应解答
思路:
使用一次遍历。
1、p,q两个指针同时指向dummy节点
2、第一次循环:p不动,移动q,使得 p, q 两个节点之间间隔 n 个节点。
3、同时移动 p, q(每次移动相同距离),当 q 指向 NULL时,停止。
此时,p 恰好指向 要删除节点的前一个节点, 即第 L -n个节点
4、改变此时p节点的指针,删除倒数第 n 个节点。
此方法非常巧妙,还简单。
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
####方法2
dummy = ListNode(0)
p, q = dummy ,dummy
dummy.next = head
# 第一次遍历:p 不动,q 移动到第 n+1 个节点。
while n+1 > 0: # 循环条件怎么写
n -= 1
q = q.next # 循环介绍后q 指向第n+1 个节点
# 第二次遍历:p,q同时移动,每次移动一个节点,当q指向NULL,停止;此时,p指向第 L-n 个节点。
while q:
p = p.next
q = q.next # 遍历结束后,q指向NULL,p指向第 L-n 个节点
# 删除第 L-n+1 个节点
p.next = p.next.next
return dummy.next
难点两次循环条件。
收获
总结
1、链表删除元素和删除节点区别
2、数组遍历 和 链表遍历 的框架
链表遍历:(统计链表节点数)
p = head
while p :
count += 1
p = p.next
3、代码框架:
p不动,移动q到第 n+1 个节点
第二次遍历: