代码有解释,可能一开始看不到,更着动手多敲几遍就能掌握了。加油。
题目描述:
给定一个带头结点的单链表,请将其逆序。即如果单链表原来为head->1->2->3->4->5->6->7,那么逆序后变为head->7->6->5->4->3->2->1。由于单链表与数组不同, 中每个结点的地址都存储在其前驱结点的指针域中, 因此,对单链表中任何 个结点的访问只能从链表的头指针开始进行遍历。在对链表的操作 过程中,需要特别注意在修改结点指针域的时候,记录下后继结点的地址,否 会丢失后继结点。
先定义好链表结构
# -*- coding: utf-8 -*-
"""@author = xuanyun@date = 2019-12-19"""
# 链表数据结构定义
class ListNode:
def __init__(self, x):
self.data = x
self.next = None
class ListOperation:
def __int__(self,node=None):
self.head=node
# 根据链表数据初始化链表
def init_list(self,n_list):
# 初始化一个头指针
head = ListNode("head")
cur = head
for i in n_list:
node = ListNode(i)
cur.next = node
cur = node # 相当于 cur = cur.next,后移
return head
# 遍历链表
def ergodic_list(self,head):
cur = head.next
while cur:
print(cur.data)
cur = cur.next
一、递归逆序
思路:
1、首先将1 -> 2 -> 3 ->4变成1 -> 4-> 3 -> 2(其中2 -> 3 ->4变成4-> 3 -> 2,采用递归方式实现)
2、再将head头节点1移到尾巴,作为尾结点,由于上一个尾结点2,是原先的head.next,此时将2的next指针指向头节点,即head.next.next = head,然后尾结点指向空,即head.next = None
3、特殊情况:头节点为空或者只有1个节点
"""方法功能:对不带头结点的单链表进行逆序输入参数: firstRef:链表头第一个结点"""
def reverse_list_one(firstRef):
#如果链表为空或者链表中只有一个元素
if firstRef is None or firstRef.next is None:
return firstRef
else:
#反转后面的结点
newhead=reverse_list_one(firstRef.next)
#把当前遍历的结点加到后面结点逆序后链表的尾部
firstRef.next.next=firstRef
firstRef.next=None
return newhead
"""方法功能 对带头结点的单链表进行逆序输入参数 head 链表头结点"""
def reverse_have_head(head):
if head is None:
return
#获取链表第一个结点
firstNode=head.next
#对链表进行逆序
newhead=reverse_list_one(firstNode)
#头结点指向逆序后链表的第一个结点
head.next=newhead
return head
算法性能分析:
由于递归法也只需要对链表进行 次遍历,因此,算法的时间复杂度也为 O(N) ,其中,N为链表的长度。递归法的主要优点是:思路比较直观,容易理解,而且也不需要保存前驱 结点的地址
缺点是:算法实现的难度较大,此外,由于递归法需要不断地调用自己,需要 额外的压技与弹枝操作,因此, 与方法 相比性能会有所 降。
二、就地逆序
移动:第1次指针操作,将当前节点指针cur.next值临时保存到nex,即nex = cur.next, 第2次指针操作,将当前的指针cur.next指向其前一个节点pre, 即cur.next = pre,这样就实现了对单个节点的操作。
操作节点的变量移动,pre结点后移:pre_node = cur_node ,cur指向下一个操作节点,即cur = nex,这样通过while循环即可实现对每个节点操作。
特殊情况:链表为空链表、链表为单节点,直接返回即可,无需逆序操作
"""Method Two :就地逆转核心思想:遍历链表,每次遍历都让当前结点指向前驱结点。时间复杂度:O(N)空间复杂度:O(1)"""
def reverse_list_two(head):
""":type head: ListNode:rtype: ListNode"""
if not head.next: # 空链表或者遍历到最后一个结点
return head # 返回头结点
# 第一步:首结点变成逆转后尾结点
cur_node = head.next # 链表首结点
next_node = cur_node.next # 原链表第二个结点
cur_node.next = None # 断开原链表首结点与第二个结点链接
# 第二步:依次逆转后续结点
pre_node = cur_node # 记录前驱结点
cur_node = next_node # 后移一位
while cur_node.next:
next_node = cur_node.next # 记录后继结点
# 让当前结点指向前驱结点
cur_node.next = pre_node # 逆转
pre_node = cur_node # 前驱结点后移
cur_node = next_node # 链表后移,遍历
"""注:为什么这个地方当前结点后移不用cur_node = cur_node.next因为在前面的代码中,cur_node.next = pre_node,即cur_node 的后继结点已经改变所以用next_node来标记逆转前的cur_node的后继结点"""
# 第三步:头结点指向原链表尾结点,完成逆转
cur_node.next = pre_node # 此时的cur_node应该为:原链表尾结点,最后一次逆转
head.next = cur_node # 头结点指向尾结点,完成逆转
return head # 返回头结点
三、插入逆序
直假定原链表为head->1->2->3->4->5->6->7,在遍历到2的时候,将其插入到头结点后,链表变为head->2->1->3->4->5->6->7,同理将后序遍历到的所有结点都插入到头结点head后,就可以实现链表的逆序。实现代码如下:
"""Method Three :插入逆转核心思想:遍历链表,把遍历到的当前结点插入到头结点之后这种方法应该是最好理解的。时间复杂度:O(N)空间复杂度:O(1)"""
def reverse_list_three(head):
""":type head: ListNode:rtype: ListNode"""
if not head.next: # 空链表或者遍历到最后一个结点
return head # 返回头结点
# 第一步:设置尾结点
cur_node = head.next.next # 取出第二个结点
head.next.next = None # 令第一个结点指向None,即设置成尾结点
# 第二步:向后遍历,并把遍历到的结点插入到头结点后
while cur_node:
next_node=cur_node.next # 记录后继结点
cur_node.next=head.next #当前要逆序的节点后面接上已经逆序好的链表head
head.next=cur_node #当前节点变成头节点head
cur_node=next_node # 链表后移
return head # 返回头结点
测试
# 当然,也许还有别的方法,比如建一个辅助的链表
# 欢迎你说出你的想法
# 程序入口,测试函数功能
if __name__ == "__main__":
s = ListOperation() # 初始化一个链表基本操作对象实例
list_data = [1, 2, 3, 4] # 链表初始化数据
head = s.init_list(list_data) # 初始化链表,带头结点
s.ergodic_list(head) # 未逆转前,遍历打印链表
#head = reverse_have_head(head) # 测试方法一,逆转链表
#head = reverse_list_two(head) # 测试方法二,逆转链表
head = reverse_list_three(head) # 测试方法三,逆转链表
print("---------------------")
s.ergodic_list(head) # 逆转后,遍历打印链表
参考:
《python程序员宝典》