一个比较常见的算法题:合并两个已知排序的单链表,合并后保持有序。网上有很多解法,都是什么所谓的并轨排序,将一个链表合并插入到另一个链表中。自己想出个最笨的方法,维护第三个resultSingleLink。从两个输入的链表中取值比较并插入到resultSingleLink中。先看下我定义的SingleLink,其中提供了很多API方便调用,顺便可以复习一下单链表写法。如下:

/**
 * Created by zengxiangge on 2018-3-13.
 */

public class SingleLink<T> {
    public Node head = null;

    /**
     * 构造方法,方便创建
     */
    public SingleLink(T[] data) {
        Node next = new Node(data[0]);
        for (int i = 0; i < data.length; i++) {
            Node temp = new Node(data[i]);
            if (i == 0) {
                head = temp;
                next = temp;
            } else {
                next.next = temp;
                next = temp;
            }
        }
    }

    public SingleLink(){}

    public void addNode(T data) {
        Node node = new Node(data);
        if (head == null) {
            head = node;
            return;
        }
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;
    }

    public void addNode(Node node){
        if(head == null){
            head = node;
            return;
        }
        Node temp = head;
        while (temp.next != null) {
            temp = temp.next;
        }
        temp.next = node;
    }

    public void deleteNode() {
        Node temp = head;
        if (temp == null) {
            return;
        }
        if (temp.next == null) {
            head = null;
            return;
        }
        while (temp.next != null && temp.next.next != null) {
            temp = temp.next;
        }
        temp.next = null;
    }

    /**
     * index start with 0:head index is 0
     *
     * @param index
     * @return
     */
    public boolean deleteNodeByIndex(int index) {
        if (index < 0 || index > length() - 1) {
            return false;
        }
        if (index == 0) {
            head = head.next;
            return true;
        }
        int i = 1;
        Node preNode = head;
        Node curNode = head.next;
        while (curNode != null) {
            if (i == index) {
                preNode.next = curNode.next;
                return true;
            }
            i++;
            preNode = curNode;
            curNode = curNode.next;
        }
        return false;
    }

    /**
     * index start with 0:head index is 0
     *
     * @param index
     */
    public boolean insertNodeByIndex(T data, int index) {
        if (index < 0 || index > length()) {
            return false;
        }
        Node node = new Node(data);
        if (index == 0) {
            node.next = head;
            head = node;
            return true;
        }
        if (index == length()) {
            addNode(data);
            return true;
        }
        int i = 1;
        Node preNode = head;
        Node curNode = head.next;
        while (curNode != null) {
            if (i == index) {
                preNode.next = node;
                node.next = curNode;
                return true;
            }
            preNode = curNode;
            curNode = curNode.next;
            i++;
        }
        return false;
    }

    /**
     * 将targetNode插入到orinialNode节点后
     */
    public boolean insert(Node orinialNode, Node targetNode) {
        if (!containNode(orinialNode))
            return false;
        Node temp = orinialNode.next;
        orinialNode.next = targetNode;
        targetNode.next = temp;
        return true;
    }

    public boolean containNode(Node node) {
        if (node == null)
            throw new RuntimeException("arg node is null!");
        if (head == null)
            return false;
        Node temp = head;
        if (head.equals(node))
            return true;
        while (temp.next != null) {
            temp = temp.next;
            if (head.equals(node)) {
                return true;
            }
        }
        return false;
    }

    public int length() {
        Node temp = head;
        if (temp == null) return 0;
        int length = 1;
        while (temp.next != null) {
            length += 1;
            temp = temp.next;
        }
        return length;
    }


    public void printLink() {
        Node temp = head;
        while (temp.next != null) {
            System.out.print("[" + temp.data + "]->");
            temp = temp.next;
        }
        System.out.print("[" + temp.data + "]");
    }

    public void reverseIteratively() {
        Node pReversedHead = head;
        Node pNode = head;
        Node pPrev = null;
        while (pNode != null) {
            Node pNext = pNode.next;
            if (pNext == null) {
                pReversedHead = pNode;
            }
            pNode.next = pPrev;
            pPrev = pNode;
            pNode = pNext;
        }
        this.head = pReversedHead;
        return;
    }

    public void printLinkReversely() {
        if (length() == 0) return;
        if (length() == 1) {
            System.out.printf("data:" + head.data + "\n");
            return;
        }
        printLinkReversely(head);

    }

    private void printLinkReversely(Node node) {
        if (node.next != null) {
            node = node.next;
            printLinkReversely(node);
            System.out.println("node data:" + node.data);
        }
    }

    public boolean isLoop() {
        if (head == null) return false;
        Node fast = head;
        Node slow = head;

        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }

    public class Node {
        public T data;
        public Node next;

        public Node(T data) {
            this.data = data;
        }

        @Override
        public boolean equals(Object obj) {
            return this.data == ((Node) obj).data;
        }
    }

}

好,接下来梳理我的解题思路,两个已排序的链表,首先我想到的是这样的:

link_1:1->3->5->7->9

link_2:2->4->6->8->10

那这样好说,每一个链表都维护一个指针,每一次取两个指针的值,较小的值插入到resultSingleLink中,并将该指针向前移动一位。若两个链表均遍历完毕,则跳出循环。于是我写出了代码:

public SingleLink<Integer> mergeSingleLink(SingleLink<Integer> link_1, SingleLink<Integer> link_2) {
        //最终输出的单链表
        SingleLink<Integer> resultLink = new SingleLink<>();
        //link_1指针
        SingleLink.Node link_1_pointer = link_1.head;
        //link_2指针
        SingleLink.Node link_2_pointer = link_2.head;
        //循环条件任意一个链表没遍历完就接着遍历
        while (link_1_pointer.next != null || link_2_pointer.next != null) {
            //比较两个指针取到的值,true代表data1<=data2(因为是升序)
            if (compareData((Integer) link_1_pointer.data, (Integer) link_2_pointer.data)) {
                  //把较小的值添加到新链表并将较小值所在链表指针向前移动
                  resultLink.addNode((Integer) link_1_pointer.data);
                  //若已经遍历到最后一项,则指针不在向前移动,否则while判断中报空指针异常
                  link_1_pointer = link_1_pointer.next != null ? link_1_pointer.next : link_1_pointer;
            } else {
                  resultLink.addNode((Integer) link_2_pointer.data);
                   ink_2_pointer = link_2_pointer.next != null ? link_2_pointer.next : link_2_pointer;
            }
        }
        return resultLink;
    }

输入测试用例:

link_1:1->3->5->7->9

link_2:2->4->6->8->10

result:1->2->3->4->5->6->7->8->9->10

欣喜如狂,以为解决了问题,但是我测试了另外一组用例

link_1:1->2->3->4->5

link_2:6->7->8->9->10

result:死循环

结果不对,我陷入了沉思,找到问题根节点:如果link_1的最后一位仍小于link_2的当前指针下的值,那么就会陷入while死循环,resultSingleLink会不断的添加link_1的最后一项。结果就是1->2->3->4->5->5->5->5.......这时也理解了为什么要用二路并归排序。那怎么办?不舍得放弃自己的思路,于是添加逻辑分支:若link_1.pointer值较小,在向resultSingleLink中添加之前,判断link_1现在是否已经遍历完了,如果已经遍历完,说明link_1中的值已经全部添加到resultSingleLink中了。而此时link_1.pointer值仍然小于link_2.pointer的值,此时则需要将link_2.pointer添加到resultSingleLink中即可(因为两个输入SingleLink本来就是有序的)。最终代码如下:

import com.zxg.datastructure.linklist.SingleLink;

/**
 * 合并两个已知排序的单链表,合并后保持有序
 */
public class AlgorithmPractice_7 {
    public SingleLink<Integer> mergeSingleLink(SingleLink<Integer> link_1, SingleLink<Integer> link_2) {
        //最终输出的单链表
        SingleLink<Integer> resultLink = new SingleLink<>();
        //link_1指针
        SingleLink.Node link_1_pointer = link_1.head;
        //link_2指针
        SingleLink.Node link_2_pointer = link_2.head;
        //循环条件任意一个链表没遍历完就接着遍历
        while (link_1_pointer.next != null || link_2_pointer.next != null) {
            //比较两个指针取到的值,true代表data1<=data2(因为是升序)
            if (compareData((Integer) link_1_pointer.data, (Integer) link_2_pointer.data)) {
                //判断条件:link_1没有遍历结束
                if (link_1_pointer.next != null) {
                    //把较小的值添加到新链表并将较小值所在链表指针向前移动
                    resultLink.addNode((Integer) link_1_pointer.data);
                    link_1_pointer = link_1_pointer.next != null ? link_1_pointer.next : link_1_pointer;
                } else {
                    /**
                     * 若link_1遍历结束,且link_1最后一个值仍小于link_2.pointer值,则直接将link_2添加
                     * 到resultLink(因为两个输入链表都是有序的)并结束循环
                     */
                    resultLink.addNode(link_2_pointer);
                    break;
                }
            //同理不赘述
            } else {
                if (link_2_pointer.next != null) {
                    resultLink.addNode((Integer) link_2_pointer.data);
                    link_2_pointer = link_2_pointer.next != null ? link_2_pointer.next : link_2_pointer;
                } else {
                    resultLink.addNode(link_1_pointer);
                    break;
                }

            }
        }
        return resultLink;
    }

    private boolean compareData(Integer data_1, Integer data_2) {
        return data_1 <= data_2;
    }

    public static void main(String[] args) {
        AlgorithmPractice_7 algorithm = new AlgorithmPractice_7();
        Integer[] array_2 = {1, 3, 5, 7, 9};
        Integer[] array_1 = {2, 4, 66, 90, 220};

        SingleLink<Integer> link_1 = new SingleLink<Integer>(array_1);
        SingleLink<Integer> link_2 = new SingleLink<>(array_2);

        SingleLink<Integer> result = algorithm.mergeSingleLink(link_1, link_2);
        result.printLink();
    }
}