python 单链表节点怎么快速定义_单链表逆序

dab1e75afae19dc45d4850dae01327aa.png

代码有解释,可能一开始看不到,更着动手多敲几遍就能掌握了。加油。

题目描述:

给定一个带头结点的单链表,请将其逆序。即如果单链表原来为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

6d08ef0b70a7886aeb081625911ebde9.png

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为链表的长度。

  • 递归法的主要优点是:思路比较直观,容易理解,而且也不需要保存前驱 结点的地址
  • 缺点是:算法实现的难度较大,此外,由于递归法需要不断地调用自己,需要 额外的压技与弹枝操作,因此, 与方法 相比性能会有所 降。

二、就地逆序

4473545821ebe78c16427f2eeb6ea1e4.png

移动:第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程序员宝典》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值