单链表的归并排序

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

  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方法将两者合并。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值