今天做了链表的冒泡排序和归并排序,复习了二叉树的遍历,明天继续。
冒泡效率低,前一篇已经讲过,这篇来记录归并排序,归并排序最坏和平均时间复杂度都是O(nlogn),而且属于稳定排序,所以掌握了可以使用在几乎所有情况。
这里先说对链表的二路归并排序思路:
1.先把链表从中间分成两个子链表
public static ListNode divide(ListNode L){
ListNode start, middle, temp;
start = L;
middle = null;
temp = L;
while(temp != null && temp.next != null)
{
temp = temp.next.next;
middle = start;
start = start.next;
}
middle.next = null;
return start;
}
其中temp节点和middle、start节点,作为快慢指针遍历一遍链表,middle最后记录下前一个子链表的最后一个节点位置,start记录下后一个子链表的头结点位置,操作完之后原本的L链表,在middle位置被截断,middle位置的节点的后继成为空,这里可以看出java变量的特性,如果不是用构造方法新建的变量,类似start = L这样的语法,start和L只是两个类似指针的变量,指向同一块内存区域,所以当middle.next被清空了之后原来的内存区域也被操作,所以在函数内没有操作L,L也变化了
2.递归方法对两个子链表做归并排序
public static ListNode merge(ListNode L1, ListNode L2){
ListNode temp1 = L1;
ListNode temp2 = L2;
ListNode s = null;
ListNode lc = null;
if(temp1.val <= temp2.val)
{
s = temp1;
lc = s;
temp1 = temp1.next;
}else{
s = temp2;
lc = s;
temp2 = temp2.next;
}
while(temp1 != null && temp2 != null)
{
if(temp1.val <= temp2.val)
{
s.next = temp1;
temp1 = temp1.next;
s = s.next;
}else{
s.next = temp2;
temp2 = temp2.next;
s = s.next;
}
}
if(temp1 == null)s.next = temp2;
else if(temp2 == null) s.next = temp1;
return lc;
}
public static ListNode mergeSortList(ListNode head){
if(head == null || head.next == null) return head;
ListNode a,b;
b = divide(head);
a = head;
head = merge(mergeSortList(a),mergeSortList(b));
return head;
}
merge函数里面的lc和前面讲到的是一样的道理,在一开始用一个额外的变量存储链表的头地址,在链表被操作的面目全非之后lc还能指向正确的头地址。其中的归并方法,首先找出传入的两个链表的头结点的值哪个更小,就记录到临时变量s.next中,同时记录到 lc 中,同时小值开头的链表头后移(只是这么理解,实际只是一个指针),然后s变成自己的后继,也就是刚才那个较小的头结点本身,而头本身变成了自己的后继,之后判断如果两个链表都还没有走到头的话,继续判断两段的头结点的大小,此时的两段链表为其中一条去掉了小的头,另一条不变,所以等于重复了刚才的过程,最后的结果是较小的节点被赋值给s.next,前一次s指向的地址是两个(注意:这里两个链表内部是有序的)链表的最小节点,所以现在这个最小节点的next指向第二小的节点,第二小的节点本身往后移动,s成为这个第二小的点本身,以此类推,最后两个链表会成为一个有序链表,但是会留下最后一个最大的节点空出来,被存在变量temp1/2中,所以在最后把这个节点接上,如代码所示。
在主函数里,递归实现归并。
下面是测试代码,在OJ上也AC了
public static void main(String args[]){
ListNode l = new ListNode(5);
ListNode ll = new ListNode(3);
ListNode lll = new ListNode(6);
ListNode llll = new ListNode(2);
ListNode lllll = new ListNode(8);
ListNode llllll = new ListNode(9);
l.next = ll;
ll.next = lll;
lll.next = llll;
llll.next = lllll;
lllll.next = llllll;
ListNode n = mergeSortList(l);
//System.out.print(re.val);
while(n != null)
{
System.out.print(n.val);
n = n.next;
}
}
output:235689