参考资料:左程云算法课
迭代版本的归并排序
public ListNode sortList(ListNode head)
{
int n=0;
// count nodes
ListNode cur=head;
while(cur!=null)
{
n++;
cur = cur.next;
}
ListNode first=head;
ListNode pre = null;
ListNode h= first;
for(int len=1;len<n;len<<=1) // make sure that two parts we divide every time 'while' runs
{ // for the list headed by 'h', while运行一次,则 有两组 长度为len 的 连续子块 被归并排序,
// 下次运行 while, 就是 下两组 长度为len 的 连续子块 被归并排序,
// pre 用于连接 归并排序出的 子数组
// first 用于定位 下两组等待归并排序的起始位置
// h 表示 在这个len 下 对整个数组 做一遍归并排序 后的结果的起始位置
while(first!=null)
{
ListNode[] twoParts = twoLen(first,len); // 两组子块的起始位置、终止位置,下一组等待归并排序子块的起始位置
ListNode[] oneMerge = merge(twoParts[0],twoParts[1],twoParts[2],twoParts[3]);// 归并排序 两组子块
if(pre==null)
{
h = oneMerge[0];
pre = oneMerge[1];
}else {
pre.next=oneMerge[0];
pre = oneMerge[1];
}
first = twoParts[4]; //first移到下一个待排序子块的起始位置
}// end while
first = h; // ready for the next iteration with new 'len'
pre=null;
}// end for
return h;
}
public ListNode[] twoLen(ListNode first, int len)
{// 从 以first为开头的整体 中,取出两段长度为len的子串。返回两端的分割点 和 这次分到哪了
// head, tail, head, tail, n
ListNode ls = first;
ListNode le = first;
ListNode rs = null;
ListNode re = null;
ListNode next = null;
int pass=0;
while(first!=null)
{
pass++;
if(pass<=len)
{
le = first;
}else if(pass==len+1)
{
rs=first;
}
if(pass>=len+1)
// you cannot replace it with 'pass>len+1',
// since the range of 'len' in main fun guarantees that two parts we will get contains at least one num
// so using 'pass>=len+1', you can make sure re won't be null as its initial value;
// 这样更新re还有一个原因是,第二个子块并不总是能达到len个字符
{
re=first;
}
if(pass==(len<<1))
{
break;
}
first = first.next;
}//end while
le.next=null; // break the connection of two sub bolcks
if(re!=null)
{
next = re.next;
re.next=null;
}
return new ListNode[] {ls,le,rs,re,next};
}
public ListNode[] merge(ListNode l1, ListNode r1, ListNode l2, ListNode r2)
{
if(l2==null)
{
return new ListNode[] {l1,r1};
}
ListNode head=null;
ListNode pre=null;
ListNode cur=null;
ListNode tail = null;
while(l1!=r1.next && l2!=r2.next)
{
if(l1.val<l2.val)
{
cur = l1;
l1 = l1.next;
}else {
cur=l2;
l2 = l2.next;
}
if(pre==null)
{
head = cur;
}else {
pre.next = cur;
}
pre = cur;
}
if(l1!=r1.next)
{
while(l1!=r1.next)
{
pre.next = l1;
tail=l1;
pre=l1;
l1 = l1.next;
}
}else{
// l2 might be null, this case embodies the function of "if(l1!=r1.next)..else.."
// under this case, only the above 'if' block will be run, so all is right
while(l2!=r2.next)
{
pre.next = l2;
tail = l2;;
pre = l2;
l2 = l2.next;
}
}
return new ListNode[] {head,tail};
}
递归版本的归并排序
思路:
首先 用快慢指针找到 链表的上中点;
然后,将整个链表分为两部分,分别做排序,这里调用递归函数即可;
接着,做归并,注意申请dummy head,以备及时记录排序结果,并返回。
class Solution {
public ListNode sortList(ListNode head)
{
if(head==null || head.next==null)
{
return head;
}
// find upper-mid, by fast and slow pointers
// 注意 这里 fast,slow指针的设置,保证 拿到的是 上中点
ListNode fast = head.next;
ListNode slow = head;
while(fast!=null && fast.next!=null)
{
slow = slow.next;
fast = fast.next.next;
}
// now, slow is upper-mid point
ListNode tmp = slow.next;
slow.next=null; // break the connection between the left part and right part
ListNode left = sortList(head);
ListNode right = sortList(tmp);
// merge two parts
ListNode h = new ListNode(0);// dummy head of final result
ListNode res = h;
while(left!=null && right!=null)
{
if(left.val<right.val)
{
h.next= left;
left = left.next;
}else {
h.next=right;
right = right.next;
}
h=h.next;
}
h.next = left!=null?left:right;
return res.next;
}
链表的选择排序
在leetCode的测试中超时了 ┭┮﹏┭┮
思路是:把链表看作两部分,前部分是已排序的(结尾是tail),后部分是未排序的(开头是cur) ; 每次都从未排序部分选出最小值small,接在已排序部分的结尾。注意:准备 下一轮 未排序部分的开头时,需要看 最小值是否是当前开头cur,如果是,那么cur=cur.next, 因为small要被拿走。注意:small被拿走后,未排序部分中要用smallPre的next接上剩余部分。
public class ListNode{
int val;
ListNode next;
public ListNode() {}
public ListNode(int v)
{
val=v;
}
public ListNode(int v,ListNode node)
{
val=v;
next=node;
}
} // end class ListNode
public static ListNode selection(ListNode head)
{
// divide one list into two parts: sorted part and unsorted part
ListNode tail=null; // the tail of unsorted part
ListNode cur = head; // head of sorted part
ListNode small = null;
ListNode smallPre = null;
while(cur!=null)
{
small = cur;
smallPre = getSmallpre(cur);
if(smallPre!=null)
{
small = smallPre.next;
smallPre.next = small.next;
}
cur= cur==small?cur.next:cur; // head of unsorted part in the next iteration
// put away 'small' and 'tail'
if(tail==null)
{
head=small;
}else {
tail.next=small;
}
tail = small;
}
return head;
}
public static ListNode getSmallpre(ListNode head)
{
ListNode small=head;
ListNode smallPre=null;
ListNode cur=head;
ListNode pre = null;
while(cur!=null)
{
if(cur.val<small.val)
{
small=cur;
smallPre = pre;
}
pre = cur;
cur = cur.next;
}
return smallPre;
}