单链表的归并排序

来源:互联网 发布:网络会计授课教师招聘 编辑:程序博客网 时间:2024/05/10 19:28

归并排序是分治算法思想的一个具体体现。利用分治算法进行排序时,可以从两个方面进行考虑:

  1. merge方法:将两个已经有序的小集合合并为一个有序的大集合;
  2. sort方法:利用递归的方法,将总集合逐次二分为只有单个元素的集合,然后用merge方法将集合逐次两两合并,最后达到将总集合排序的目的。

对于链表而言,由于不能像数组一样随机访问,故而使用一般方法会因为迭代寻找节点而消耗大量的时间。归并排序可以说是链表排序的最佳方法,一方面利用分治算法不用大量的寻找节点从而节省了时间,另一方面不用开辟额外的空间保存节点。
Java代码如下所示:

import java.util.Iterator;// 可以调用递归排序的链表public class MergeLinkList<T extends Comparable<T>> implements Iterable<T>{    // 定义节点    private class Node {        T item;        Node next;    }    private Node first;// 头节点    private int currentLength;    public MergeLinkList() {        first = null;        currentLength = 0;    }    public boolean isEmpty() {        return first == null;    }    public int size() {        return currentLength;    }    public void addFirst(T item) {        Node newNode = new Node();        newNode.item = item;        newNode.next = first;        first = newNode;        currentLength++;    }    public T removeFirst() {        if (isEmpty()) {            throw new NullPointerException();        } else {            T item = first.item;            first = first.next;            currentLength--;            return item;        }    }    @SuppressWarnings("unchecked")    public static <T> boolean less(Comparable<T> v,Comparable<T> w) {        return v.compareTo((T)w)<0;    }    // 将两个有序链表合为一个有序链表并返回    private Node merge(Node a,Node b) {        if (a==null&&b==null) {            return null;        }        Node head=new Node();// 创建一个临时头节点        Node t=head;// 链表插入节点        while (a!=null&&b!=null) {            if (less(a.item, b.item)) {                // a的头节点插入                t.next=a;                t=a;                a=a.next;            }else {                // b的头节点插入                t.next=b;                t=b;                b=b.next;            }        }        // 剩余的节点全部插入        if (a!=null) {t.next=a;}        if (b!=null) {t.next=b;}        t=head.next;// 跳过头节点        head=null;// 删除头节点        return t;    }    // 递归的将链表先二分,后归并    public Node sort(Node head) {        if (head==null||head.next==null) {            return head;        }        Node pfast=head.next;// 快指针,到达链表尾部时慢指针刚好指向链表中部        Node pslow=head;// 慢指针,配合快指针记录链表的中部        while (pfast.next!=null&&pfast.next.next!=null) {            pslow=pslow.next;            pfast=pfast.next.next;        }// 结束时pslow指向中间,pfast指向末尾        pfast=sort(pslow.next);// 后半部分排序        pslow.next=null;// 从中间断开为两个链表        pslow=sort(head);// 前半部分排序        return merge(pslow, pfast);// 将前后有序链表合并    }    public void sort() {        Node newFirst=sort(first);        first=newFirst;    }    @Override    public Iterator<T> iterator() {        return  new Iterator<T>() {            private Node current=first;            @Override            public boolean hasNext() {                return current!=null;            }            @Override            public T next() {                T item=current.item;                current=current.next;                return item;            }        };    }    public static void main(String[] args) {        MergeLinkList<String> list=new MergeLinkList<>();        String[] s="uitvxbmqkcywhaszofnpldrgje".split("");        for (int i = 0; i < s.length; i++) {            list.addFirst(s[i]);        }        for (String string : list) {            System.out.print(string+" ");        }        System.out.println();        list.sort();        for (String string : list) {            System.out.print(string+" ");        }    }}

代码中利用快慢指针进行二分的方法非常巧妙,快指针的移动是慢指针的两倍,所以快指针到达链表尾部时慢指针刚好指向链表中部。此时快指针的使命完成,我们可以让它指向排序好的后半链表。将链表从中部断开,此时慢指针也完成了使命,我们让它指向排序好的前半链表。最后调用merge方法将两者合并。

0 0
原创粉丝点击