一个比较常见的算法题:合并两个已知排序的单链表,合并后保持有序。网上有很多解法,都是什么所谓的并轨排序,将一个链表合并插入到另一个链表中。自己想出个最笨的方法,维护第三个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();
}
}