一题学会链表的拆分,翻转,合并

412 阅读4分钟

题目

先看题目,给定一个链表,奇数项为递增序列,偶数项为递减序列,如图:

image.png 要求将链表按从小到大进行排序。

这个题目看上去可能就是一个简单的链表,但是要怎么做到将链表按照升序的顺序进行排序。这里我们可以用三步,可能比较繁琐,但是经过这三步的过程,我们会对链表的一些常规操作有一个很清晰的认识和理解,这三个步骤就是:

  1. 链表拆分,先将整个链表拆为两个部分,升序子链表和降序子链表,也就是将下标为奇数和偶数的元素分别放到两个链表中
  2. 链表翻转,将下标为偶数链表元素组成的新的子链表进行翻转,使其按从小到大排列
  3. 链表合并,前面两个步骤得到了两个从小到大的链表,现在只需要将这两个链表进行合并 Node的定义(为了减少代码,用了lombok):
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Node {
    public int val;
    public Node next;

    public Node(int anInt) {
        this.val = anInt;
    }
}

链表拆分

image.png

image.png

这个过程比较简单,就按下标的奇偶性进行拆分就行了,代码如下:

public static void main(String[] args) {
        /**
         * 拆分链表,注意最后一项的处理
         */
        Node head = new Node(1, new Node(9, new Node(2, new Node(8, new Node(3, new Node(7, new Node(
                4, new Node(6, new Node(5, new Node(5, new Node(6, new Node(4))))))))))));

        //为了后面代码的统一,建了两个虚拟头结点
        Node node0 = new Node(-1, head);
        Node node1 = new Node(-1, head.next);

        Node head0 = node0;
        Node head1 = node1;
        int i = 0;

        while (head != null) {
            if (i % 2 == 0) {
                node0.next = head;
                node0 = node0.next;
            } else {
                node1.next = head;
                node1 = node1.next;
            }
            head = head.next;
            i++;
        }
        //循环结束之后这里要注意 处理一下,不然总会有一个子链表多一个元素
        if (i % 2 == 0) {
            node0.next = null;
        } else {
            node1.next = null;
        }
        System.out.println(head0.next);
        System.out.println(head1.next);

head0.next和head1.next就是拆分出来的两个子链表了 输出结果如下:

image.png

链表翻转

翻转的过程主要的思想是使用双指针,一前一后两个指针来遍历链表并且改变next指针的方向,这里并没有另外创建一个新的链表来实现翻转,整个过程的流程如下动图,不明白的话,更详细的的步骤可以参考文末的参考文章。

640.gif 有动图我就直接上代码了:

private static Node reverse(Node head) {
        Node cur = head;
        Node pre = null;
        Node tmp;
        while (cur != null) {
            tmp = cur.next;
            cur.next = pre;

            pre = cur;
            cur = tmp;
        }
        return pre;
    }

合并链表

力扣有一个剑指 Offer类似的题目可以参考一下:合并有序链表难度为简单。可以参考这一篇题解:题解 其实整个过程就是先遍历一个链表,遍历的时候和另一个链表当前的头结点的大小比较谁的值更小就取谁的值,然后将头结点指向下一个节点,如果另一个链表元素还有剩下的,说明都是比最后一个元素更大的,直接拼到后面就可以了,代码如下:

private static Node merge(Node node0, Node node1){
        Node node = new Node(0);
        Node head = node;
        while (node0 !=null){
            if (node1 != null && node0.val <= node1.val){
                node.next = node0;
                node0 = node0.next;
            }else {
                if (node1 != null){
                    node.next = node1;
                    node1 = node1.next;
                }
            }
            node = node.next;
        }
        if (node1 != null){
            node.next = node1;
        }
        return head.next;
    }

完整代码

public class ReSort {
    public static void main(String[] args) {
        /**
         * 拆分链表,注意最后一项的处理
         */
        Node head = new Node(1, new Node(9, new Node(2, new Node(8, new Node(3, new Node(7, new Node(
                4, new Node(6, new Node(5, new Node(5, new Node(6, new Node(4))))))))))));

        Node node0 = new Node(0, head);
        Node node1 = new Node(0, head.next);

        Node head0 = node0;
        Node head1 = node1;
        int i = 0;

        while (head != null) {
            if (i % 2 == 0) {
                node0.next = head;
                node0 = node0.next;
            } else {
                node1.next = head;
                node1 = node1.next;
            }
            head = head.next;
            i++;
        }
        if (i % 2 == 0) {
            node0.next = null;
        } else {
            node1.next = null;
        }
        System.out.println(head0.next);
        System.out.println(head1.next);

        Node reverse = reverse(head1.next);
        Node merge = merge(head0.next, reverse);
        System.out.println(merge);


    }


    /**
     * 双指针法实现链表翻转
     * @param head
     * @return
     */
    private static Node reverse(Node head) {
        Node cur = head;
        Node pre = null;
        Node tmp;
        while (cur != null) {
            tmp = cur.next;
            cur.next = pre;

            pre = cur;
            cur = tmp;
        }
        return pre;
    }

    //合并链表
    private static Node merge(Node node0, Node node1){
        Node node = new Node(0);
        Node head = node;
        while (node0 !=null){
            if (node1 != null && node0.val <= node1.val){
                node.next = node0;
                node0 = node0.next;
            }else {
                if (node1 != null){
                    node.next = node1;
                    node1 = node1.next;
                }
            }
            node = node.next;
        }
        if (node1 != null){
            node.next = node1;
        }
        return head.next;
    }


}

这种解法可能比较繁琐,代码量也比较多,但是思路很清晰,也涉及了多种链表的操作。
【参考】
【1】mp.weixin.qq.com/s/pnvVP-0ZM…
【2】力扣

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。