链表逆序的三大方法(Python实现)

链表逆序的三大方法

更多文章代码详情,可以查看博主:
GitHub地址:https://github.com/TheAlgorithm-SimpleChinese/Python
个人网站:https://www.iwtmbtly.com


假设给定一个带头结点的单链表,要将其逆序。

即如果单链表原来为head->1->2->3->4->5->6->7,那么逆序后变为 head->7->6->5->4->3->2->1。

有什么办法可以解决呢。

​由于单链表与数组不同,单链表中每个结点的地址都存储在其前驱结点的指针域中,因此,对单链表中任何一个结点的访问只能从链表的头指针开始进行遍历。在对链表的操作过程中,需要特别注意在修改结点指针域的时候,记录下后继结点的地址,否则会丢失后继结点。

链表的逆序——就地逆序法

​ 主要思路为:在遍历链表的时候,修改当前结点的指针域的指向,让其指向它的前驱结点。为此需要用一个指针变量来保存前驱结点的地址。此外,为了在调整当前结点指针域的指向后还能找到后继结点,还需要另外一个指针变量来保存后继结点的地址,在所有的结点都被保存好以后就可以直接完成指针的逆序了。除此之外,还需要特别注意对链表首尾结点的特殊处理。

代码如下:

# -*- coding: utf-8 -*-
"""
程序功能:实现链表的逆序
"""

class Node:
    """创建节点类"""

    def __init__(self, data: int, next=None):  # 节点数据统一以整数作为示例且初始值为0
        self.data = data
        self.next = next


# 方法功能:对单链表进行逆序 输入参数:head:链表头结点
def Reverse(head):
    # 判断链表是否为空
    if head is None or head.next is None:
        return
    # pre = None  # 前驱结点
    # cur = None  # 当前结点
    # next = None  # 后继结点
    # 把链表首结点变为尾结点
    cur = head.next
    next = cur.next
    cur.next = None
    pre = cur
    cur = next
    # 使当前遍历到的结点cur指向其前驱结点
    while cur.next is not None:
        next = cur.next
        cur.next = pre
        pre = cur
        cur = next
    # 链表最后一个结点指向倒数第二个结点
    cur.next = pre
    # 链表的头结点指向原来链表的尾结点
    head.next = cur


def main():
    head = Node(7, None)  # 创建头节点,存储节点个数
    cur = head
    for i in range(1, 8):  # 创建节点
        cur.next = Node(i, None)
        cur = cur.next
    print("逆序前:", end=' ')  # 输出逆序前节点值
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next
    print("\n逆序后:", end=' ')  # # 输出逆序前节点值
    Reverse(head)
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next


if __name__ == "__main__":
    main()

输出如下:

逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1

算法性能分析:

以上这种方法只需要对链表进行一次遍历,因此,时间复杂度为O(N),其中,N为链表的长度。但是需要常数个额外的变量来保存当前结点的前驱结点与后继结点,因此,空间复杂度为O(1)。

链表的逆序——递归法

假定原链表为1->2->3->4->5->6->7,递归法的主要思路为:

先逆序除第一个结点以外的子链表(将1->2->3->4->5->6->7变为 1->7->6->5->4->3->2),

接着把结点1添加到逆序的子链表的后面(1->7->6->5->4->3->2 变为7->6->5->4->3->2->1)。

同理,在逆序链表2->3->4->5->6->7时,也是先逆序子链表 3->4->5->6->7(逆序为 2->7->6->5->4->3),

接着实现链表的整体逆序(2->7->6->5->4->3转换为 7->6->5->4->3->2)。

实现代码如下:

# -*- coding: utf-8 -*-
"""
程序功能:实现链表的逆序
"""

class Node:
    """创建节点类"""

    def __init__(self, data: int, next=None):  # 节点数据统一以整数作为示例且初始值为0
        self.data = data
        self.next = next
"""
方法功能:对不带头结点的单链表进行逆序
输入参数:head:链表头结点
"""
def RecursiveReverse(head):
    # 如果链表为空或者链表中只有一个元素
    if head is None or head.next is None:
        return head
    else:
        # 反转后面的结点
        new_head = RecursiveReverse(head.next)
        # 把当前遍历的结点加到后面结点逆序后链表的尾部
        head.next.next = head
        head.next = None
    return new_head

"""
方法功能:对带头结点的单链表进行逆序
输入参数:head:链表头结点
"""


def Reverse(head):
    if head is None:
        return
        # 获取链表第一个结点
    firstNode = head.next
    # 对链表进行逆序
    new_head = RecursiveReverse(firstNode)
    # 头结点指向逆序后链表的第一个结点
    head.next = new_head
    return new_head


def main():
    head = Node(7, None)  # 创建头节点,存储节点个数
    cur = head
    for i in range(1, 8):  # 创建节点
        cur.next = Node(i, None)
        cur = cur.next
    print("逆序前:", end=' ')  # 输出逆序前节点值
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next
    print("\n逆序后:", end=' ')  # # 输出逆序前节点值
    Reverse(head)
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next


if __name__ == "__main__":
    main()

输出如下:

逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1

算法性能分析:

由于递归法也只需要对链表进行一次遍历,因此,算法的时间复杂度也为O(N),其中,N为链表的长度。递归法的主要优点是:思路比较直观,容易理解,而且也不需要保存前驱结点的地址。缺点是:算法实现的难度较大,此外,由于递归法需要不断地调用自己,需要额外的压栈与弹栈操作,因此,与方法一相比性能会有所下降。

链表的逆序——插入法

插入法的主要思路为:

从链表的第二个结点开始,把遍历到的结点插入到头结点的后面,直到遍历结束。

假定原链表为 head->1->2->3->4->5->6->7,在遍历到2的时候,将其插入到头结点后,链表变为head->2->1->3->4->5->6->7,

同理将后续遍历到的所有结点都插入到头结点 head 后,就可以实现链表的逆序。

实现代码如下:

# -*- coding: utf-8 -*-
"""
程序功能:实现链表的逆序
"""

class Node:
    """创建节点类"""

    def __init__(self, data: int, next=None):  # 节点数据统一以整数作为示例且初始值为0
        self.data = data
        self.next = next


# 插入法
def Reverse(head):
    # 判断链表是否为空
    if head is None or head.next is None:
        return
    cur = None  # 当前结点
    next = None  # 后继结点
    cur = head.next.next
    # 设置链表第一个结点为尾结点
    head.next.next = None
    # 把遍历到结点插入到头结点的后面
    while cur is not None:
        next = cur.next
        cur.next = head.next
        head.next = cur
        cur = next


# 递归
def ReversePrint(firstNode):
    if firstNode is None:
        return
    ReversePrint(firstNode.next)
    print(firstNode.data)


def main():
    head = Node(7, None)  # 创建头节点,存储节点个数
    cur = head
    for i in range(1, 8):  # 创建节点
        cur.next = Node(i, None)
        cur = cur.next
    print("逆序前:", end=' ')  # 输出逆序前节点值
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next
    print("\n逆序后:", end=' ')  # # 输出逆序前节点值
    Reverse(head)
    test = head.next
    while test is not None:
        print(test.data, end=' ')
        test = test.next


if __name__ == "__main__":
    main()

输出为:

逆序前:1 2 3 4 5 6 7
逆序后:7 6 5 4 3 2 1

算法性能分析:

以上这种方法也只需要对单链表进行一次遍历,因此,时间复杂度为 O(N),其中,N为链表的长度。与方法一相比,这种方法不需要保存前驱结点的地址,与方法二相比,这种方法不需要递归地调用,效率更高。

引申:

  1. 对不带头结点的单链表进行逆序

  2. 从尾到头输出链表

分析与解答:

对不带头结点的单链表的逆序读者可以自己练习(方法二已经实现了递归的方法),这里主要介绍单链表逆向输出的方法。

  • 方法一:就地逆序+顺序输出
    首先对链表进行逆序,然后顺序输出逆序后的链表。这种方法的缺点是改变了链表原来的结构。

  • 方法二:逆序+顺序输出
    申请新的存储空间,对链表进行逆序,然后顺序输出逆序后的链表。逆序的主要思路为:每当遍历到一个结点的时候,申请一块新的存储空间来存储这个结点的数据域,同时把新结点插入到新的链表的头结点后。这种方法的缺点z要申请额外的存储空间。

  • 方法三:递归输出
    递归输出的主要思路为:先输出除当前结点外的后继子链表,然后输出当前结点,

    假如链表为:1->2->3->4->5->6->7,那么先输出 2->3->4->5->6->7,再输出 1。

    同理,对于链表2->3->4->5->6->7,也是先输出 3->4->5->6->7,接着输出2,

    直到遍历到链表的最后一个结点7的时候会输出结点7,然后递归地输出6,5,…,1。

实现代码如下:

def ReversePrint(firstNode):
	if firstNode is None:
		return
	ReversePrint(firstNode.next) 
	print firstNode.data

算法性能分析:

以上这种方法只需要对链表进行一次遍历,因此,时间复杂度为 O(N),其中,N 为链表的长度。


文章代码详情地址,可以查看博主GitHub地址:https://github.com/TheAlgorithm-SimpleChinese/Python

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HiSpring流云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值