剑指Offer 从尾到头打印列表

import java.util.ArrayList;
import java.util.Stack;

/**
 * 输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
 */

public class JZ003PrintListFromTailToHead {

    /**
     * 本题很简单,因为是从尾到头的开始答应,那应该首先想到的是栈 Stack
     * @param listNode
     * @return
     */
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {

        ArrayList<Integer> arrayList = new ArrayList<>();
        Stack<Integer> stack = new Stack<>();
        ListNode curNode = listNode;

        while (curNode != null) {
            int val = curNode.val;
            stack.push(val);
            curNode = curNode.next;
        }

        while (!stack.isEmpty()) {
            arrayList.add(stack.pop());
        }

        return arrayList;
    }

    public static void main(String[] args) {

        // ListNode listNode = new ListNode(1);
    }

    /*
    从尾到头打印链表
    总结:
        1、从尾到头,首先想到的是栈 Stack,(Stack、ArrayList、LinkedList、Vector等的区别)
        2、本地考察链表,需要注意链表和数组的区别:
            - 链表是一种动态数据结构,是因为在创建链表时,无须知道链表的长度。当插入一个节点时,只需要为新节点分配内存,然后调整引用的指向
            来确保新节点被链接到链表中。内存分配不是在创建链表是一次性完成,而是每次添加一个节点分配一次内存。由于没有闲置的内存,链表中的
            空间比数组高。
        3、链表基本的使用:
            - addNode()         添加节点
            - deleteNode()      删除节点
            - lengthNode()      返回节点的长度
            - reverseList()     链表反转
            - searchMid()       查找单聊表的中间节点
            - searchEndElem()   查找倒数第k个元素
            - orderList()       排序
            - printNode()       打印节点
            - isPool()          判断链表是否有环,单链表有环时(两个引用,一个快,一个慢)
            - findLoopPort()    找出链表环的入口 (这个有点难,需要数学公式的推到,具体参考:https://blog.csdn.net/weixin_40879743/article/details/90646399)
     */

    /**
     * 添加节点
     * @param listNode
     * @param data
     */
    public static void addNode(ListNode listNode, int data) {
        ListNode newNode = new ListNode(data);
        if (listNode == null) {
            listNode = newNode;
        }
        ListNode curNode = listNode;
        while (curNode.next != null) {
            curNode = curNode.next;
        }
        curNode.next = newNode;
    }

    /**
     * 删除节点
     * @param listNode
     * @param index
     * @return
     */
    public static boolean deleteNode(ListNode listNode, int index) {
        if (index < 1 || index > lengthNode(listNode)) {
            return false;
        }
        if (index == 1) {
            listNode = listNode.next;
            return true;
        }
        int i = 2;
        ListNode preNode = listNode;
        ListNode curNode = preNode.next;
        while (curNode != null) {
            if (i == index) {
                preNode.next = curNode.next;
                return true;
            }
            preNode = curNode;
            curNode = curNode.next;
            i++;
        }
        return false;
    }

    /**
     * 返回节点的长度
     * @param listNode
     * @return
     */
    public static int lengthNode(ListNode listNode) {
        int len = 0;
        ListNode curNode = listNode;
        while (curNode != null) {
            len++;
            curNode = curNode.next;
        }
        return len;
    }

    /**
     * 链表反转
     * @param listNode
     */
    public static void reverseList(ListNode listNode) {
        ListNode preNode = null; // 头节点
        ListNode curNode = listNode; // 前一个节点
        while (curNode != null) {
            ListNode nextNode = curNode.next; // 保存下一个节点
            curNode.next = preNode; // 链表的反转
            preNode = curNode;  // 前节点后移
            curNode = nextNode; // 当前节点后移
        }
        listNode = preNode;
    }

    /**
     * 查找单聊表的中间节点
     * @param listNode
     * @return
     */
    public static ListNode searchMid(ListNode listNode) {
        ListNode slowNode = listNode;
        ListNode quickNode = listNode;
        // quickNode.next == null 表示链表的个数为奇数,快引用已经走到了最后一个节点
        // quickNode.next.next == null 表示链表的个数为偶数,快引用已经走到了倒数第二个节点
        // 链表为奇数,返回的是中间节点
        // 链表为偶数,返回的是中间两个节点的前一个节点
        while (quickNode.next != null && quickNode.next.next != null) {
            slowNode = slowNode.next;
            quickNode = quickNode.next.next;
        }
        return slowNode;
    }

    /**
     * 查找倒数第k个元素
     * @param listNode
     * @param k
     * @return
     */
    public static ListNode searchEndElem(ListNode listNode, int k) {
        if (k < 1 || k > lengthNode(listNode)) {
            return null;
        }
        ListNode firstNode = listNode;
        ListNode secondNode = listNode;
        for (int i = 0; i < k - 1; i++) {
            firstNode = firstNode.next;
        }
        while (firstNode.next != null) {
            firstNode = firstNode.next;
            secondNode = secondNode.next;
        }
        return secondNode;
    }

    /**
     * 排序 选择排序,时间复杂度 O(n^2)
     * @param listNode
     * @return
     */
    public static ListNode orderList(ListNode listNode) {
        ListNode curNode = listNode;
        while (curNode != null) {
            ListNode nextNode = curNode.next;
            while (nextNode != null) {
                if (curNode.val > nextNode.val) {
                    int tmp = curNode.val;
                    curNode.val = nextNode.val;
                    nextNode.val = tmp;
                }
                nextNode = nextNode.next;
            }
            curNode = curNode.next;
        }
        return listNode;
    }

    /**
     * 打印节点
     * @param listNode
     */
    public static void printNode(ListNode listNode) {
        ListNode curNode = listNode;
        while (curNode != null) {
            System.out.print(curNode.val + " ");
            curNode = curNode.next;
        }
        System.out.println();
    }

    /**
     * 判断链表是否有环,单链表有环时(两个引用,一个快,一个慢)
     * @param listNode
     * @return
     */
    public static boolean isPool(ListNode listNode) {
        if (listNode == null || listNode.next == null) {
            return false;
        }
        ListNode slowNode = listNode;
        ListNode fastNode = listNode;
        while (fastNode.next != null && fastNode.next.next != null) {
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
            if (slowNode == fastNode) {
                return true;
            }
        }
        return false;
    }

    /**
     * 找出链表环的入口
     * @param listNode
     * @return
     */
    public static ListNode findLoopPort(ListNode listNode) {
        if (listNode == null && listNode.next == null) {
            return null;
        }
        ListNode slowNode = listNode;
        ListNode fastNode = listNode;
        while (fastNode.next != null && fastNode.next.next != null) {
            slowNode = slowNode.next;
            fastNode = fastNode.next.next;
            if (slowNode == fastNode) {
                break;
            }
        }

        if (fastNode.next == null || fastNode.next.next == null) {
            return null;
        }

        fastNode = listNode;
        while (fastNode != slowNode) {
            fastNode = fastNode.next;
            slowNode = slowNode.next;
        }
        return slowNode;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鬼王呵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值