1、删除链表中的节点(237)
题目描述:
【简单题】
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。
题目链接
思路分析:
1、由于传入函数的唯一参数为 要被删除的节点 。故我们无法知道待删除节点的上一节点,则无法使用常规的删除方法(待删除节点的上一节点的next指针指向待删除节点的下一节点)进行删除。考虑另一种思路。
2、将要删除节点的 next 节点的值赋值给要删除的节点,转而按照常规方法去删除 next 节点,从而达成目的。
【python 3 代码实现】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def deleteNode(self, node):
"""
:type node: ListNode
:rtype: void Do not return anything, modify node in-place instead.
"""
node.val=node.next.val
node.next=node.next.next
- 时间复杂度: O ( 1 ) O(1) O(1)
- 空间复杂度: O ( 1 ) O(1) O(1)
2、删除链表的倒数第N个节点(19)
题目描述:
【中等题】
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
题目链接
思路分析:
题解一:链表长度-两次遍历
首先扫一遍链表计算其长度,然后找到删除节点的前一节点的位置,删除即可。
【python 3代码实现】
# 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: ListNode, n: int) -> ListNode:
# 计算链表长度i
i=1
cur=head
while cur:
cur=cur.next
i+=1
# 找到倒数第n+1个结点的位置即正数第i-n个结点
dummyhead=ListNode(0,head)
curr=dummyhead
for i in range(1,i-n):
curr=curr.next
# 将倒数第n+1个结点的next指向其next.next
curr.next=curr.next.next
return dummyhead.next
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
题解二:快慢指针—一次遍历
我们可以设想假设设定了双指针 p
和 q
的话,当 q
指向末尾的 NULL,且p
与 q
之间相隔的元素个数为 n
时,那么删除掉 p
的下一个指针就完成了要求。
具体流程如下:
- 设置虚拟节点 dummyhead 指向 head
- 设定双指针 p 和 q,初始都指向虚拟节点 dummyhead
- 移动 q,直到 p 与 q 之间相隔的元素个数为 n
- 同时移动 p 与 q,直到 q 指向的为 NULL
- 将 p 的下一个节点指向下下个节点
【python 3代码实现】
# 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: ListNode, n: int) -> ListNode:
dummyhead=ListNode(0,head)
p=dummyhead
q=dummyhead
for i in range(1,n+1):# 或range(n)
q=q.next
while q.next:
p=p.next
q=q.next
p.next=p.next.next
return dummyhead.next
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
3、旋转链表(61)
题目描述:
【中等题】
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
题目链接
思路分析:
4、重排链表(143)
题目描述:
【中等题】
题目链接
思路分析:
重排链表规律:
- 首节点指向末尾节点,末尾节点指向第二节点,第二节点又指向倒数第二个节点,依次类推即相邻的两节点在原链表中的所在位置之和为n。
题解一:线性表
1、利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。
【python 3 代码实现】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
if not head:
return
# 线性表存储链表
lst=[]
while head:
lst.append(head)
head=head.next
# 定义两指针分别指向线性表的首尾,依次移动取节点重建链表
start=0
end=len(lst)-1
while start<end:
lst[start].next=lst[end]
start+=1
if start==end:
break
lst[end].next=lst[start]
end-=1
lst[start].next=None
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)
题解二:寻找链表中点 + 链表逆序 + 合并链表——快慢指针
1、可以发现,重新排列的链表可以看成原链表从中间节点分成两部分,然后将后半部分链表逆序后,两部分一对一合并的结果。
比如:
输入链表 1——>2——>3——>4——>5
切分链表
左边部分 1——>2——>3
右边部分 4——>5
右边部分逆序
逆序右边部分 5——>4
将左右两部分依次合并
1——>5——>2——>4——>3
2、具体流程
- 先找到原链表的中间节点,以此将原链表划分为左、右两部分(使用快慢指针)
- 然后将右边部分进行逆序(迭代+双指针)
- 最后将左边部分与逆序后的有部分依次一对一合并
【python 3 代码实现】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reorderList(self, head: ListNode) -> None:
"""
Do not return anything, modify head in-place instead.
"""
# 特殊情况判断
if not head:
return
def search_middle_node(head):
# 利用快慢指针查找链表的中间节点
slow=head
fast=head
while fast.next and fast.next.next:
slow=slow.next
fast=fast.next.next
return slow
def reverse_list(head):
#迭代+双指针反转链表
pre=None
cur=head
while cur:
nextnode=cur.next
cur.next=pre
pre=cur
cur=nextnode
return pre
def merge_list(lst1,lst2):
#合并链表
while lst1 and lst2:
tmp_lst1=lst1.next
tmp_lst2=lst2.next
# 依次合并
lst1.next=lst2
lst1=tmp_lst1
lst2.next=lst1
lst2=tmp_lst2
# 1、查找中间节点
mid=search_middle_node(head)
# 2、切分链表为左右两部分
lst1=head
lst2=mid.next
mid.next=None# 左部分链表的结尾指针为空
# 3、逆序右部分
lst2=reverse_list(lst2)
# 4、合并两个链表
merge_list(lst1,lst2)
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
5、回文链表(234)
题目描述:
【简单题】
请判断一个链表是否为回文链表。
题目链接
思路分析:
1、首先搞懂“什么是回文链表?”
什么是回文链表,
1 2 1 是一个回文链表
正反元素一样
若链表的逆序跟原链表一样,则此链表为回文链表。
题解一:数组+双指针
1、一般情况下,我们判断数组是否是回文,可以使用双指针的方法。初始定义双指针分别指向数组的头尾元素,指针往中间移动进行判断。因此,我们借鉴这样的方法解答回文链表问题。
2、但是链表不能够随意访问特定的数据,上面的方法也就无效。那么,遍历链表存储元素的值并存储在数组内,然后再使用上面的方法进行判断。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
list_copy=[]
while head:
list_copy.append(head.val)
head=head.next
start=0
end=len(list_copy)-1
while start<end:
if list_copy[start]!=list_copy[end]:
return False
start+=1
end-=1
return True
-
时间复杂度: O ( n ) O(n) O(n)
-
空间复杂度: O ( n ) O(n) O(n)
题解二:寻找链表中点 + 链表逆序 + 比较——快慢指针
1、对于链表而言,我们可以考虑找到链表的中间节点,将链表切分为前后两部分,然后将后面部分翻转,然后与前面部分进行比对。
例:
输入:1——>2——>2——>1
找到中间节点进行切分
前面部分 1——>2
后面部分 2——>1
翻转后面部分,进行比对
前面部分 1——>2
后面部分 1——>2
2、具体流程
- 定义一个找中间节点的函数
- 将后面部分链表进行翻转
- 比较前后两部分,判断是否是回文
【python 3 代码实现】
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def isPalindrome(self, head: ListNode) -> bool:
def search_middle_node(head):
# 查找链表中间节点
slow=head
fast=head
while fast.next and fast.next.next:
slow=slow.next
fast=fast.next.next
return slow
def reverse_list(head):
# 翻转链表
pre=None
cur=head
while cur:
nextnode=cur.next
cur.next=pre
pre=cur
cur=nextnode
return pre
if not head:
return True
# 找中间节点,切分链表
mid=search_middle_node(head)
# 后面部分链表
latter_part=mid.next
# 翻转后面部分
reversed_latter_part=reverse_list(latter_part)
# 因为翻转链表会破环链表结构,得到结论先不返回,恢复链表后再返回
res=True
# 定义指针分别指向两部分链表的头部
p=head
q=reversed_latter_part
# 开始遍历,进行比对
while res and q:
if p.val!=q.val:
res=False
p=p.next
q=q.next
# 还原链表
mid.next=reverse_list(reversed_latter_part)
return res
-
时间复杂度: O ( n ) O(n) O(n)
-
空间复杂度: O ( 1 ) O(1) O(1)