曾经在面试时候被问到单链表的翻转,今天又重新总结了一下。对于链表的遍历有两种,一种是线性的,比如for
或while
,另一种是非线性的,也就是递归。
对于翻转单链表的线性遍历有两种思路,一种是创建一个新链表,头结点插入法,另一种是就地反转法。前者理解相对容易一些,先说第一种方法,再说第二种。
单链表翻转
在重新说一下题目的定义:
定义一个链表的单结点:
class ListNode:
def __init__(self, x):
self.value = x
self.next = None
创建新链表,头结点插入法
顾名思义,就是新创建一个链表,遍历要翻转的链表,把元素作为新链表的头元素插入。
定义两个指针,pCur代表要插入到新链表的结点。pNext是用来记录要翻转列表的。下面的代码对应上面4个步骤。
# 1. pNext是一个临时结点,记录下一个要插入到新链表的结点
pNext = pCur.next
# 2. 把pCur结点插入到新链表的dummy后边
pCur.next = dummy.next
# 3. 调整新链表的dummy结点的指向
dummy.next = pCur
# 更新pCur
pCur = pNext
经过四步之后:
总结:
- 需要创建一个新链表,两个指针,其中包括一个临时指针,4行代码
- 本质:链表的遍历和链表的插入。对于新创建的链表来讲,是一个不断插入头结点的操作。
就地反转法
需要两个指针,prev代表要反转的结点的前一个结点,pCur代表要反转的结点。
# 1. prev连接下一次需要反转的节点
反转节点pCur
纠正头结点dummy的指向
pCur指向下一次要反转的节点
prev.next = pCur.next
# 2. 反转结点
pCur.next = dummy.next
# 3. 调整dummy的指向
dummy.next = pCur
# 4. 更新pCur
pCur = prev.next
反转之后的链表,上面的数字,与上图的数字是对应的关系,更方便看不同步骤操作后得到的链接:
就地反转法的完整代码:
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
if not head:
return head
dummy = ListNode(-1)
# 加上dummy之后,链表的head就是dummy结点了,再用head不好理解
dummy.next = head
#创建prev指针,在要反转结点的前面一个结点
prev = head
# 创建pcur指针,要反转的结点
pcur = prev.next
while pcur:
# prev指向下一个要反转的结点
prev.next = pcur.next
# 反转结点
pcur.next = dummy.next
# 调整dummy结点
dummy.next = pcur
# 更新要反转的结点
pcur = prev.next
return dummy.next