剑指offer之03:从尾到头打印链表arrayList

本文介绍了如何从尾到头打印链表arrayList,提供两种不同的思路:通过遍历链表存储到list再倒序输出,以及直接反转链表。详细分析了反转链表的步骤,并附有代码实现。

欢迎关注公众号:

 

题目:从尾到头打印链表arrayList

题目描述:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

思路分析:

  (1)链表简介:链表是这样一种数据结构,它是线性表,表示逻辑有序,即即数据之间的顺序关系是由某种逻辑关系(如指针)来决定的,其结构如下图所示,每一个节点包括有当前节点的数值和下一个节点的地址。(后面我们在数据结构和算法中详细介绍链表)

图片

(2)逻辑分析:在上面的描述中我们知道链表的基本结构,那么,如何实现链表的反转呢?目前有两种思路

    <1>链表是一种链式存储的数据结构,我们可以遍历链表,将链表中的数值存储到list中,然后倒序打印list。如下图所示,倒序输出list。

图片

     <2>链表上的节点存储当前位置的值和下一个节点的位置,如下图所示。

图片

对于每一个节点,我们将next的位置指向前一个节点,这样相当于将链表反转,如下图所示:

反转前:

图片

反转后:

图片

反转的过程为:

图片

反转的过程为:

对节点v做反转时,如反转前的图所示,u的next域指向v,v的next域指向w。现在要做的是将v的next域指向u。从图中我们可以看出,当把v的next指针指向u的同时,w节点及其后面的节点无法访问,为了避免“断链”,我们必须在指针更改指向之前,保存修改结点的下一结点。同时我们也必须存储上一个结点,因为next域即将修改指向该结点。因此定义三个指针,分别指向当前遍历的结点(head),前一个结点(pre)和后一个结点(next)

具体代码实现过程如下。

【第一种思路】

# -*- coding:utf-8 -*-
class ListNode:
    #在初始化方法中定义链表的节点,
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    # 返回从尾部到头部的列表值序列,例如[1,2,3]
    def printListFromTailToHead(self, listNode):
        # write code here
        # 定义一个空数组,用于存储链表中的数值
        l=[]
        # 遍历链表,
        while listNode:
            # 将链表中的当前节点的数值存储到list中,
            l.append(listNode.val)
            # 下个节点的值赋值给当前节点
            listNode=listNode.next
        return l[::-1]

if __name__=='__main__':
    #首先我们定义一个链表节点:1,2,3,4,5
    A1 = ListNode(1)
    A2 = ListNode(2)
    A3 = ListNode(3)
    A4 = ListNode(4)
    A5 = ListNode(5)
    #然后创建链表, 1-->2-->3-->4-->5
    A1.next=A2
    A2.next=A3
    A3.next=A4
    A4.next=A5
    print("打印反转前链表")
    print ( A1.val , A2.val,A3.val,A4.val,A5.val)
    solution=Solution()
    #调用方法,返回数组
    ans=solution.printListFromTailToHead(A1)
    print("打印反转后链表")
    print(ans)

在上述代码中,从前向后遍历链表,并将链表中的数依次赋值给list,然后倒序输出list。

图片

上述代码运行结果如下。

图片

【第二种思路】

class Node ( object ) :
    def __init__ ( self , elem , next_ = None ) :
        self.elem = elem
        self.next = next_

def reverseList ( head ) :
    if head == None or head.next == None :  # 若链表为空或者仅一个数就直接返回
        return head
    pre = None
    next = None
    while (head != None) :
        # 用临时变量 pre 和 next 实现  头节点的值和指向下一个节点的位置的变换
        # 即pre 和 next作为临时节点,head及head.next作为要变换的节点,
        next = head.next  # 1
        head.next = pre  # 2
        pre = head  # 3
        head = next  # 4
    return pre

if __name__ == '__main__' :
    l1 = Node ( 1 )  # 建立链表3->2->1->9->None
    l1.next = Node ( 2 )
    l1.next.next = Node ( 3 )
    l1.next.next.next = Node ( 4 )
    l1.next.next.next.next = Node ( 5 )
    print("反转前:")
    print ( l1.elem , l1.next.elem , l1.next.next.elem , l1.next.next.next.elem,l1.next.next.next.next.elem )

    l = reverseList ( l1 )

    print("反转后:")
    print ( l.elem , l.next.elem , l.next.next.elem , l.next.next.next.elem, l.next.next.next.next.elem)

运行结果为:

图片

上述代码中,首先定义一个链表 ,如下图所示:

图片

反转之后链表如下图所示。

图片

那么,当我们调用函数时,已知链表不为空或只有一个节点,pre=None,next=None,当head不为空时,

在第一次循环时,对于链表的第一个节点 1 及1.next

next = head.next

第一步:next = head.next
将 head.next 赋值给 next 变量,即next 指向了节点2,先将节点2 保存起来。

图片

head.next = pre

第二步:head.next = pre (初始pre==None)
将 pre 变量赋值给 head.next,即 此时节点1 指向了 None

图片

pre = head

第三步:pre = head
将 head 赋值给了 pre,即 pre 指向节点1,将节点1 设为“上一个节点”

图片

head = next

第四步:head = next

将 next 赋值给 head,即 head 指向了节点2,此时节点2 设为“头节点”

图片

第一遍循环执行完毕,

反转前:

图片

反转后:

图片

进入第二遍循环,

next = head.next  # 1head.next = pre  # 2pre = head  # 3head = next  # 4

第一步:next = head.next

将 head.next 赋值给 next 变量,即 next 指向了节点3,先将节点3 保存起来。

第二步:head.next = pre (此时的pre已经不为None)

将 pre 赋值给 head.next,pre 在上一次循环的时候指向了节点1,那么这一步的意义就是节点2 指向了 节点1,完成1和2节点的反转。

第三步:pre = head

将 head 赋值给了 pre,即 pre 指向节点2,将节点2 设为“上一个节点”

第四步:head = next

将 next 赋值给 head,即 head 指向了节点3。此时节点3 设为“头节点”

图片

第二次循环完毕,以此类推,执行后面的循环,最后结果如下图所示:

图片

在上述遍历的过程中,

(1)pre指向反转后链表的第一个节点,next指向反转前链表的第一个节点,head指向当前节点,反转的过程如下图所示。

图片

(2)当前节点的下一个节点一定要保存,如不保存,下一次循环时无法找到反转前链表的节点,如当前节点为2,则先将节点3保存起来

(3)实现反转的关键点在:head.next = pre  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值