剑指offer题解(Java实现)—— 面试题6:从头到尾打印链表

前言

本系列的文章为笔者学习《剑指offer第二版》后的笔记整理以及Java实现,解法不保证与书上一致。

另外,我最近在系统整理一些 Java 后台方面的面试题和参考答案,有找工作需求的童鞋,欢迎关注我的 Github 仓库,如果觉得不错可以点个 star 关注 :

题目描述

输入一个链表的头节点,从尾到头反过来打印出每个节点的值。链表节点定义如下:

private class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}

解题思路

思路一:使用栈

由于需要从头到尾遍历链表,但打印链表顺序则相反,要从尾到头打印。也就是说第一个遍历的节点要最后一个输出,而最后一个遍历的节点第一个输出。这是很典型的后进先出,自然而然可以选择来实现。接下来我们可以这样做:

  • 从头到尾遍历链表,每经过一个节点时,将其放入到一个栈中;
  • 遍历完整个链表之后,栈中现在的顺序是头节点位于栈底,尾节点位于栈顶;
  • 从栈顶开始逐个输出节点的值,此时输出的节点的顺序即相当于从尾到头反过来打印链表中每个节点的值。
package com.offers.chapter2.question06;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * 输入一个链表的头节点,从尾到头反过来打印出每个节点的值。
 *
 * 使用栈的方法
 *
 * @author Rotor
 * @since 2019/9/30 10:22
 */
public class PrintListFromTail2Head {
    // 定义链表节点类
    private class ListNode {
        int val;
        ListNode next = null;

        ListNode(int val) {
            this.val = val;
        }
    }

    /**
     * 使用栈,从头到尾扫描并压入节点,最终尾节点位于栈顶
     *
     * @param listNode 链表的头结点
     * @return 从头到尾排列的链表节点
     */
    public ArrayList<Integer> printListReversingly_Iteratively(ListNode listNode) {
        LinkedList<Integer> stack = new LinkedList<>();
        // 定义一个临时节点,指向链表头
        ListNode node;
        for (node = listNode; node != null; node = node.next) {
            stack.push(node.val);
        }
        // 返回从尾到头排列的链表节点
        return new ArrayList<>(stack);
    }
}

思路二:使用递归

既然想到了用栈来实现,而递归本质上就是一个栈结构,自然而然地就又想到了用递归的方式来实现。现在要反过来输出链表,我们可以这么做:

  • 每访问一个节点的时候,先递归输出它后面的节点;
  • 然后再输出该节点自身,这样链表的输出结果就反过来了。
package com.offers.chapter2.question06;

import java.util.ArrayList;

/**
 * 输入一个链表的头节点,从尾到头反过来打印出每个节点的值。
 *
 * 使用栈的方法
 *
 * @author Rotor
 * @since 2019/9/30 10:22
 */
public class PrintListFromTail2Head {
    // 类成员变量,用于封装递归时从后往前输出的链表节点
    private ArrayList<Integer> array = new ArrayList<>();

    // 定义链表节点类
    private class ListNode {
        int val;
        ListNode next = null;

        ListNode(int val) {
            this.val = val;
        }
    }

    /**
     * 使用递归,先递归到最后一个结点后开始依次返回。
     * 但如果链表如果很长则不适合用递归,因为递归深度将很大。
     *
     * @param listNode 表的头结点
     * @return 从头到尾排列的链表节点
     */
    public ArrayList<Integer> printListReversingly_Recursively(ListNode listNode) {
        if (listNode != null) {
            if (listNode.next != null) {
				printListReversingly_Recursively(listNode.next);
			}
            array.add(listNode.val);
        }
        return array;
    }

}

上面递归看着很简洁,但如果链表过长则不适用,因为链表过长会导致递归深度很大,从而有可能导致方法调用栈溢出。

总结

  • 从尾到头打印输出每个链表节点的值时,虽然从头到尾输出很简单,可以把链表中链接节点的指针反转过来,改变链表的方向,然后就可以从头到尾输出了。但是这么做会改变链表的结构。
  • 从为到头打印链表节点的值可以使用栈或者递归的方法来解决,但是链表很长时可能会导致递归深度很大,从而导致方法调用栈溢出,因此使用哪种方式需要好好权衡。

后记

如果你同我一样想要努力学好数据结构与算法、想要刷 LeetCode 和剑指 offer,欢迎关注我 GitHub 上的 LeetCode 题解:awesome-java-notes

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值