题目1.
24.两两交换链表中的节点
个人认为难点在于理解交换的本质是交换链表中的连接顺序,而不是交换链表的值,因为根据链表的特性这不是合理的做法。
题目链接:. - 力扣(LeetCode)
Python代码:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def swapPairs(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
#先考虑特殊情况
#1检查链表是否为空:在Python中,链表是否为空应该检查head是否为None
if head is None or head.next is None:
return head
'''
2、不直接计算长度,因为这会需要遍历整个链表,
而题目要求我们在原地修改链表
'''
dummy_head = ListNode(next=head)
#初始化一个指针current,用于遍历链表。
#开始时,它指向虚拟头部节点。
current = dummy_head
'''
3、交换节点应该先将current.next的next指向temp
(即current.next.next.next)
然后再将current.next的next(原指向current.next.next)指向
current.next的下一个节点(即current.next自身),
最后更新current为current.next.next
'''
while current.next and current.next.next:
#保存要交换的两个节点
node1 = current.next #第一个节点
node2 = current.next.next #第二个节点
#交换节点连接
#node1的next指向node2的next
node1.next = node2.next
#node2的next指向node1
node2.next = node1
#current的next指向node2,完成交换
current.next = node2
#移动current到下一对要交换的节点之前
current = node1
#返回新的头部节点(跳过虚拟头部节点)
return dummy_head.next
C语言代码和思路:
-
递归逻辑:
-
首先,定义一个新的头节点
newHead
,它指向原链表的第二个节点(即head->next
)。这是因为我们将要交换head
和head->next
的位置,所以新的头节点应该是head->next
。 -
然后,递归地调用
swapPairs
函数处理剩余链表(即从newHead->next
开始的链表),并将返回的结果设置为head->next
。这里的关键是,递归调用会处理除当前对外的所有节点,并返回新的子链表的头节点。 -
接下来,将
newHead
(即原来的第二个节点)的next
指针指向head
,完成两个节点的交换。 -
最后,返回
newHead
作为整个链表新的头节点。
-
-
递归过程:
-
假设链表为
1 -> 2 -> 3 -> 4
,初始调用swapPairs(1)
。 -
在第一次递归中,
newHead = 2
,然后递归调用swapPairs(3)
处理剩余链表3 -> 4
。 -
递归调用
swapPairs(3)
会继续这个过程,直到到达基本情况(链表为空或只有一个节点)。 -
当递归调用返回时,我们开始逐层构建交换后的链表。首先,
2 -> 3'
(其中3'
是3 -> 4
交换后的链表),然后1 -> 2 -> 3'
,最终得到2 -> 1 -> 3' -> 4'
(其中3'
和4'
是原始3 -> 4
链表交换后的结果)。
-
struct ListNode* swapPairs(struct ListNode* head) {
if(head == NULL) || head->next == NULL){
return head;
}
struct ListNode* newHead = head->next;
head->next = swapPairs(newHead->next);
newHead->next = head;
return newHead;
}
题目2.
19.删除链表中的第N个节点. - 力扣(LeetCode)
考虑使用快慢指针
快指针先行n步,这样快慢指针之间形成了一段长度为n的窗口,之后快慢指针同步向前相当于保持窗口长度不变。这样当快指针到达了末尾指向NULL,另一端的慢指针距离末尾的长度是n,自然就是指向倒数第n个位置了。
为啥快指针先行了n+1步? 由于单链表中的next指针指向的是下一个节点,想要删除倒数第n个节点,自然要将操作指针慢指针指向倒数第n+1个节点,这样才能进行删除操作。 虚拟头节点dummyHead的作用是?
如果单链表中要删除的节点是头节点,这个头节点正好是dummyHead的下一个节点,如此即可统一起来删除操作而不必单独考虑。 额外注意? 如果使用C++,最后记得释放删除的节点以及dummyHead(来自B站评论) https://www.bilibili.com/video/BV1vW4y1U7Gf/?share_source=copy_web&vd_source=1261b97a33f42388f92b473554874c36
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
#考虑使用虚拟头部节点
#使第n-1个节点指向第n个节点的下一个节点
'''
首先让快指针比慢指针先前进n步,然后两个指针同时移动,直到快指针到达链表末尾。
这时,慢指针就指向了需要删除的节点的前一个节点。但是,这里有一个特殊情况需要注意:如果n等于链表的长度,那么实际上我们要删除的是链表的头节点。因此,我们还需要特别处理这种情况。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution(object):
def removeNthFromEnd(self, head, n):
"""
:type head: ListNode
:type n: int
:rtype: ListNode
"""
#考虑使用虚拟头部节点
#使第n-1个节点指向第n个节点的下一个节点
'''
首先让快指针比慢指针先前进n步,然后两个指针同时移动,直到快指针到达链表末尾。
这时,慢指针就指向了需要删除的节点的前一个节点。但是,这里有一个特殊情况需要注意:如果n等于链表的长度,那么实际上我们要删除的是链表的头节点。因此,我们还需要特别处理这种情况。
'''
dummy_head.next = ListNode(next = head)
prev = dummy_head # prev用于指向当前要删除节点的前一个节点
fast = head
# 快指针先前进n步
for i in range(n):
fast = fast.next
# 快慢指针同时移动,直到快指针到达末尾
while fast:
prev = prev.next
fast = fast.next
# 删除倒数第n个节点
prev.next = prev.next.next
# 返回虚拟头节点的下一个节点,即原链表的头节点
return dummy_head.next