Algs4-2.2.17链表排序
2.2.17链表排序。实现对链表的自然排序(这是将链表排序的最佳方法,因为它不需要额外的空间,且运行时间是线性对数级别的)。
线性单链表自底向上自然归并排序算法:
1)如果链表长度为0或1时排序完成。
2)链表长度大于1时需要进行归并排序。
2.1)最开始将first作为开始结点查找出sp,hi结点.
2.2)当hi结点是尾结点之后的结点,说明只存在一个升序子链,不需要归并。
2.3)当hi结点不是尾结点之后的结点,说明存在两个子链,需要归并。
2.4)第二子链的最后结节的下一个结点将作为本层下一次归并的第一个子链的开始结点。本次归并时第二子链的最后结点如果不是最大元素,那么此在本次归并后其下一结点将被修改,所以在本次归并前需要保存其下一结点的位置,以确保下一次归并的第一子链的开始结点正确。
2.5)本次归并完,开始查找下一次两个归并子链的边界结点,当第一个子链的开始结点为空时,说明本层归并已结束。
2.6)最近一次的归并的第一子链的开始结点与整个链的开始结点相同,并且第一子链的尾结点的下一结点为空,说明第一子链与整个链相同,结束排序。否则开始新一个层次的归并,同时将整个链表的首结点作为第一子链的开始结点。
归并算法说明:
1)归并时生成两个子链合并后的新链的首尾结点是first、tail。
2)归并过程中的边界结点leftMax,rightMax一定要在归并前保存,以免归并过程中对sp和hi的next域值变更,引起边界检查的bug。
3)归并结束后,归并链的尾结点tail应指向原hi结点的指向,即tail的next应指向rightMax,以保证整个链表的顺序连续不断。
4)如果参与本次归并的第一子链的开始结点是整个链表的首结点,那么说明本次归并的第一子链是整个链表的最左边部分,归并过程中整链的原首结点可能因为元素值较大,而不再作为首结点,那么归并后的新的归并链的首结点将作为整个链表的首结点。
5)在同一层次,完成本次归并完成后,形成的新的子链的尾结点是指向下一次归并的第一子链的开始结点(2.4描述),在进行下一次归并时,由于第一子链的开始结点元素值比较大时将不会是归并后子链的首结点,所以需要在归并结束后将上次归并后的子链的尾结点指向本次归并后子链的首结点。
6)由于java函数参数只能传值,所以涉及对节点指向的变更均使用了只修改参数值的.next域的形式。
public class E2d2d17
{
public static void sort(Node firstPre)
{
if (firstPre.next==null || firstPre.next.next==null) return;
Node hiNext=firstPre.next;
Node preHiPre=new Node();
Node lo=firstPre.next;
Node sp=firstPre.next;
Node hi;
while(!(lo==firstPre.next && sp.next==null))
{
lo=firstPre.next;
while(!(lo==null))
{
sp=FindAscIndex(lo);
hi=FindAscIndex(sp.next);
if(hi==null)
break;
else
{
hiNext=hi.next;
merge(firstPre,preHiPre,lo,sp,hi);
lo=hiNext;
}//end if
}//end while
}//end while
}
private static void merge(Node firstPre,Node preHiPre,Node lo,Node sp,Node hi)
{
Node mergeFirst=null;
Node mergeTail=null;
Node leftMax=sp.next;
Node rightMax=hi.next;
Node i=lo;
Node j=sp.next;
if(less(i.item,j.item))
{
mergeFirst=i;
mergeTail=i;
i=i.next;
}
else
{
mergeFirst=j;
mergeTail=j;
j=j.next;
}
while(!(i==leftMax && j==rightMax) )
{
if(i==leftMax)
{
mergeTail.next=j;
mergeTail=mergeTail.next;
j=j.next;
}
else if(j==rightMax)
{
mergeTail.next=i;
mergeTail=mergeTail.next;
i=i.next;
}
else if(less(i.item,j.item))
{
mergeTail.next=i;
mergeTail=mergeTail.next;
i=i.next;
}
else
{
mergeTail.next=j;
mergeTail=mergeTail.next;
j=j.next;
}
}//end while
mergeTail.next=rightMax;
if(firstPre.next==lo)
{
firstPre.next=mergeFirst;
preHiPre.next=mergeTail;
}
else
{
preHiPre.next.next=mergeFirst;
preHiPre.next=mergeTail;
}
}
public static Node FindAscIndex(Node lo)
{
if (lo==null) return null;
Node pre=lo;
Node cur=lo.next;
while(!(cur == null))
{
if(less(cur.item,pre.item))
break;
else
{
pre=cur;
cur=cur.next;
}
}//end while
return pre;
}
private static boolean less(Comparable v ,Comparable w)
{
return v.compareTo(w)<0;
}
public static boolean isSorted(Node firstPre)
{
if (firstPre.next==null || firstPre.next.next==null) return true;
Node pre=firstPre.next;
Node cur=firstPre.next.next;
while(!(cur==null))
{
if(less(cur.item,pre.item)) return false;
pre=cur;
cur=cur.next;
}
return true;
}
public static int count(Node firstPre)
{
int cnt=0;
Node node=firstPre.next;
while(!(node==null))
{
cnt++;
node=node.next;
}
return cnt;
}
public static void main(String[] args)
{
int N=Integer.parseInt(args[0]);
Node first=null;
Node tail=null;
for (int i=0;i<N;i++)
{
Node node=new Node();
node.item=StdRandom.uniform();
if(first==null && tail==null)
{
first=node;
tail=node;
}
else
{
tail.next=node;
tail=tail.next;
}//end if
}//end for
Node firstPre=new Node();
firstPre.next=first;
sort(firstPre);
//
StdOut.printf("\nlink is sorted=%s ,count=%d\n",isSorted(firstPre),count(firstPre));
}
}
public class Node
{
Comparable item;
Node next;
}