单链表反转(Java)

前言

单链表反转是leetcode(力扣)第206题:206. 反转链表
要想反转单链表,首先得知道什么是单链表。前一篇 图解数据结构:数组和单链表 已经非常详细的解析了什么是单链表。

单链表结构

/**
 * 单链表
 */
public class SingleLinkedList<E> {

    /**
     * 内部节点
     */
    private class Node {
        private E e;
        private Node next;

        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }

        public Node(E e) {
            this(e, null);
        }
    }

	// 头节点
    private Node head;
    // 节点个数
    private int size;
}

三个指针实现反转

分别用三个指针prev、curr、next指向连续的三个节点,curr节点表示需要修改后继节点的节点,也就是:curr的后继节点本来指向next节点,现在要改成指向prev节点,对每一个节点(除了头节点外)执行这样的操作后,链表就被反转了。整个过程示意图如下:
在这里插入图片描述
看懂了过程图,再看实现代码,应该就一目了然了。

public Node reverse() {
    if (head == null || head.next == null) {
        // 空链表和只有一个节点的链表,反转后还是原来的结构
        return head;
    }
    Node prev = head;
    Node curr = prev.next;
    Node next = null;
    while (curr != null) {
        next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    head.next = null;
    head = prev;
    return head;
}

递归实现

反转整个链表,等于反转除头节点外,其余的链表,并将返回的反转后的链表的后继指针指向头节点。这样可以把原问题变成更小的子问题,从而写出递归算法。递归算法虽然写起来代码量更少,但是不如三个指针的方法更直观。下面是递归方式的实现:

/**
 * 反转链表(递归版)
 *
 * @return
 */
public Node reverse() {
    if (head == null) {
        return null;
    }
    reverse(head);
    return head;
}

/**
 * 反转以node为头结点的单链表,并且返回反转后的尾结点(反转之后就成了尾结点)
 *
 * @param node
 * @return
 */
private Node reverse(Node node) {
    if (node.next == null) {
        head = node;
        return node;
    }
    Node lastTail = reverse(node.next);
    node.next = null;
    lastTail.next = node;
    return node;
}

验证

为了验证两个反转方法的正确性,还得先写一个向单链表中添加元素的方法,由于不是重点,所以直接给出。以下是完整的源代码,包括:单链表结构定义、向单链表中添加元素、反转单链表、打印单链表等。

/**
 * 单链表
 */
public class SingleLinkedList<E> {

    /**
     * 内部节点
     */
    private class Node {
        private E e;
        private Node next;

        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }

        public Node(E e) {
            this(e, null);
        }
    }

    private Node head;
    private int size;

    /**
     * 链表尾部新增节点
     *
     * @param e
     */
    public void add(E e) {
        head = add(head, e);
    }

    /**
     * 向以node为根节点的链表插入节点,并返回插入后的链表
     *
     * @param node
     * @param e
     * @return
     */
    private Node add(Node node, E e) {
        if (node == null) {
            // 递归结束条件
            size++;
            return new Node(e);
        }
        node.next = add(node.next, e);
        return node;
    }

    /**
     * 反转链表(非递归版)
     * @return
     */
//    public Node reverse() {
//        if (head == null || head.next == null) {
//            // 空链表和只有一个节点的链表,反转后还是原来的结构
//            return head;
//        }
//        Node prev = head;
//        Node curr = prev.next;
//        Node next = null;
//        while (curr != null) {
//            next = curr.next;
//            curr.next = prev;
//            prev = curr;
//            curr = next;
//        }
//        head.next = null;
//        head = prev;
//        return head;
//    }

    /**
     * 反转链表(递归版)
     *
     * @return
     */
    public Node reverse() {
        if (head == null) {
            return null;
        }
        reverse(head);
        return head;
    }

    /**
     * 反转以node为头结点的单链表,并且返回反转后的尾结点(反转之后就成了尾结点)
     *
     * @param node
     * @return
     */
    private Node reverse(Node node) {
        if (node.next == null) {
            head = node;
            return node;
        }
        Node lastTail = reverse(node.next);
        node.next = null;
        lastTail.next = node;
        return node;
    }

    /**
     * 输出链表信息
     *
     * @return
     */
    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        Node curr = head;
        while (curr != null) {
            result.append(curr.e).append("->");
            curr = curr.next;
        }
        result.append("NULL");
        return result.toString();
    }
}

测试方法

public static void main(String[] args) {
    SingleLinkedList<Integer> list = new SingleLinkedList();
    for (int i = 0; i < 7; i++) {
        list.add(i);
    }
    System.out.println(list);
    list.reverse();
    System.out.println(list);
}

执行结果:

反转前:0->1->2->3->4->5->6->NULL
反转后:6->5->4->3->2->1->0->NULL

总结

单是一个线性数据结构的反转其实不难,主要是单链表只能根据当前节点找到后一个节点,所以操作起来感觉处处受限。所以需要发散思维,借助一些辅助的指针等完成反转操作。
当然除了以上两种方法,还有一些简单粗暴的方法:直接开辟一个数组,数组长度等于单链表节点个数,把单链表节点存入数组,再将数组反序遍历,重新构造成单链表。或者利用(Stack)LIFO的性质等等。算法比较简单,就不赘述了。

发布了52 篇原创文章 · 获赞 107 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Baisitao_/article/details/102964765