排序算法(二)—归并排序(Merge sort)
2015-06-12 10:42
513 查看
Merge sort首先将需要排序的序列拆分成单独的可排序对象,然后将这些可排序对象两两进行归并,直到把它们再合并为一个排序好的序列为止。
Merge sort不是in place排序,但是将Merge sort应用于链表(list)和数组的时候,两者的空间复杂度是不同的。应用于链表的时候空间复杂度更低。因此Merge sort更适用于链表。
本文采用非递归的方式来实现Merge sort在队列上的操作(队列其实就是一种被限制了操作的链表,可以理解为残废了的链表)。基本思想是将待排序的队列首先拆分成队列的队列,初始时每个队列只有1个可排序的对象,然后将相邻的两个队列merge,merge后将产生一个新的队列,将该新队列加入到刚才队列的队列的队尾,然后队头接着之前的merge操作。重复这个过程依次merge下去,直到队列的队列中只有一个队列为止。这时就完成了merge sort。这么介绍每部分的代码:
(1)首先定义队列(Queue)的接口:
(3)将一个LinkedQueue拆分成队列的队列的方法:
(4)merge两个队列的方法,这里要注意处理当只剩下一个队列还有元素的情况:
(5)最后是mergeSort()方法:
Merge sort的时间复杂度比较容易得到,根据递归树来计算,这里顺便提一下递归树的计算,merge sort 的每一次都是cn,因此每一次的工作量都是相同的,有些算法在用递归树分析时,工作量可能都集中在顶层,而有些又集中在底层。如图:
![](http://img.blog.csdn.net/20150612144833790?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ2FvYm9oZWxsbzE5ODc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
本文要点:
(1)Merge sort用在链表和数组的情况下空间复杂度不同,具体分析过程文中已提到;
(2)非递归的merge sort链表的过程,本文是以队列为例的。
(3)在将q拆分成队列的队列queues时,其实这时还是一个链表,只是链表中的元素类型变成了LinkedQueue,但是实际存储的对象未变。其实这个过程完全可以直接运用链表来完成,链表中的元素起初都只是包含一个对象,而随着归并的进行,每个元素包含的对象在增加,从而变成了每个元素是一个链表。之所以采用队列,就是在表述上会更清晰一些。
(4)可以通过递归树看到一个算法的时间复杂度的分布情况。
Merge sort不是in place排序,但是将Merge sort应用于链表(list)和数组的时候,两者的空间复杂度是不同的。应用于链表的时候空间复杂度更低。因此Merge sort更适用于链表。
本文采用非递归的方式来实现Merge sort在队列上的操作(队列其实就是一种被限制了操作的链表,可以理解为残废了的链表)。基本思想是将待排序的队列首先拆分成队列的队列,初始时每个队列只有1个可排序的对象,然后将相邻的两个队列merge,merge后将产生一个新的队列,将该新队列加入到刚才队列的队列的队尾,然后队头接着之前的merge操作。重复这个过程依次merge下去,直到队列的队列中只有一个队列为止。这时就完成了merge sort。这么介绍每部分的代码:
(1)首先定义队列(Queue)的接口:
/* Queue.java */ package list; public interface Queue { /** * size() returns the size of this Queue. * @return the size of this Queue. * Performance: runs in O(1) time. **/ public int size(); /** * isEmpty() returns true if this Queue is empty, false otherwise. * @return true if this Queue is empty, false otherwise. * Performance: runs in O(1) time. **/ public boolean isEmpty(); /** * enqueue() inserts an object at the end of the Queue. * @param item the item to be enqueued. **/ public void enqueue(Object item); /** * dequeue() removes and returns the object at the front of the Queue. * @return the item dequeued. * @throws a QueueEmptyException if the Queue is empty. **/ public Object dequeue() throws QueueEmptyException; /** * front() returns the object at the front of the Queue. * @return the item at the front of the Queue. * @throws a QueueEmptyException if the Queue is empty. **/ public Object front() throws QueueEmptyException; }(2)定义Queue接口的实现类LinkedQueue:
/* LinkedQueue.java */ package list; public class LinkedQueue implements Queue { private SListNode head; private SListNode tail; private int size; /** * LinkedQueue() constructs an empty queue. **/ public LinkedQueue() { size = 0; head = null; tail = null; } /** * size() returns the size of this Queue. * @return the size of this Queue. * Performance: runs in O(1) time. **/ public int size() { return size; } /** * isEmpty() returns true if this Queue is empty, false otherwise. * @return true if this Queue is empty, false otherwise. * Performance: runs in O(1) time. **/ public boolean isEmpty() { return size == 0; } /** * enqueue() inserts an object at the end of the Queue. * @param item the item to be enqueued. **/ public void enqueue(Object item) { if (head == null) { head = new SListNode(item); tail = head; } else { tail.next = new SListNode(item); tail = tail.next; } size++; } /** * dequeue() removes and returns the object at the front of the Queue. * @return the item dequeued. * @throws a QueueEmptyException if the Queue is empty. **/ public Object dequeue() throws QueueEmptyException { if (head == null) { throw new QueueEmptyException(); } else { Object o = head.item; head = head.next; size--; if (size == 0) { tail = null; } return o; } } /** * front() returns the object at the front of the Queue. * @return the item at the front of the Queue. * @throws a QueueEmptyException if the Queue is empty. **/ public Object front() throws QueueEmptyException { if (head == null) { throw new QueueEmptyException(); } else { return head.item; } } /** * * nth() returns the nth item in this LinkedQueue. * Items in the queue are numbered from 1. * @param n the number of the item to return. */ public Object nth(int n) { SListNode node = head; for (; n > 1; n--) { node = node.next; } return node.item; } /** * append() appends the contents of q onto the end of this LinkedQueue. * On completion, q is empty. * @param q the LinkedQueue whose contents should be appended onto this * LinkedQueue. **/ public void append(LinkedQueue q) { if (head == null) { head = q.head; } else { tail.next = q.head; } if (q.head != null) { tail = q.tail; } size = size + q.size; q.head = null; q.tail = null; q.size = 0; } /** * toString() converts this queue to a String. **/ public String toString() { String out = "[ "; try { for (int i = 0; i < size(); i++) { out = out + front() + " "; enqueue(dequeue()); } } catch (QueueEmptyException uf) { System.err.println("Error: attempt to dequeue from empty queue."); } return out + "]"; } }
(3)将一个LinkedQueue拆分成队列的队列的方法:
public static LinkedQueue makeQueueOfQueues(LinkedQueue q) { LinkedQueue queues = new LinkedQueue(); while(!q.isEmpty()){ LinkedQueue newQueue = new LinkedQueue(); try { newQueue.enqueue(q.dequeue()); queues.enqueue(newQueue); } catch (QueueEmptyException e) { e.printStackTrace(); } } return queues; }
(4)merge两个队列的方法,这里要注意处理当只剩下一个队列还有元素的情况:
public static LinkedQueue mergeSortedQueues(LinkedQueue q1, LinkedQueue q2) { LinkedQueue sortedQueue = new LinkedQueue(); while(!q1.isEmpty() && !q2.isEmpty()){ if(((Comparable) q1.nth(1)).compareTo((Comparable)q2.nth(1)) <= 0){ try { sortedQueue.enqueue(q1.dequeue()); } catch (QueueEmptyException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ try { sortedQueue.enqueue(q2.dequeue()); } catch (QueueEmptyException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } if(q1.isEmpty() && !q2.isEmpty()){ sortedQueue.append(q2); }else if(q2.isEmpty() && !q1.isEmpty()){ sortedQueue.append(q1); } return sortedQueue; }
(5)最后是mergeSort()方法:
public static void mergeSort(LinkedQueue q) { if(q.isEmpty()){ return; } LinkedQueue queues = makeQueueOfQueues(q); while(queues.size() != 1){ try { LinkedQueue q1 = (LinkedQueue) queues.dequeue(); LinkedQueue q2 = (LinkedQueue) queues.dequeue(); queues.enqueue(mergeSortedQueues(q1,q2)); } catch (QueueEmptyException e) { e.printStackTrace(); } } q.append(queues); }这里很有意思的是这种实现方法的空间复杂度,首先假设待排序的LinkedQueue q有n个对象,因此它的空间复杂度是O(n)。将q拆分成队列的队列queues后,这时q被清空,已经没有元素了,元素都跑到了queues里,这时queues的空间复杂度是O(n),整个程序的空间复杂度也是O(n)。随着queues的元素出队列merge,queues的元素在减少,但是减少这部分被q1和q2接收了,而当merge后新的queue又加到了queues的后边,q1和q2被清空,因此整个过程程序的空间复杂度都是O(n)。而如果是数组的话就做不到这一点。
Merge sort的时间复杂度比较容易得到,根据递归树来计算,这里顺便提一下递归树的计算,merge sort 的每一次都是cn,因此每一次的工作量都是相同的,有些算法在用递归树分析时,工作量可能都集中在顶层,而有些又集中在底层。如图:
本文要点:
(1)Merge sort用在链表和数组的情况下空间复杂度不同,具体分析过程文中已提到;
(2)非递归的merge sort链表的过程,本文是以队列为例的。
(3)在将q拆分成队列的队列queues时,其实这时还是一个链表,只是链表中的元素类型变成了LinkedQueue,但是实际存储的对象未变。其实这个过程完全可以直接运用链表来完成,链表中的元素起初都只是包含一个对象,而随着归并的进行,每个元素包含的对象在增加,从而变成了每个元素是一个链表。之所以采用队列,就是在表述上会更清晰一些。
(4)可以通过递归树看到一个算法的时间复杂度的分布情况。
相关文章推荐
- 页面设计--Tree目录树
- sgu224 分类: sgu 2015-06-12 10:42 19人阅读 评论(0) 收藏
- [剑指OFFER] 斐波那契数列- 跳台阶 变态跳台阶 矩形覆盖
- Android开发中常用到的一些开源框架
- 代理模式学习笔记
- Android:网络通信框架Volley简介(Google IO 2013)
- 报警主机报警原理
- 【转】Ubuntu10.04上编译Android源码(Build Android source in Ubuntu10.04 Platform)
- 使用Spring的@Scheduled实现定时任务
- [php] 解析JSON字符串
- JavaScript中Date.toSource()方法的使用教程
- Java中重定向输出流实现用文件记录程序日志
- 在安装android studio时如何修改properties类型文件
- HighChar
- 浏览器版本重新判断(IE11革新后)
- Java && C++
- 几种表链接的优劣
- 网络编程及安全
- jscharts.js中如何把左上角的LOGO去掉
- ECMAScript6中Set/WeakSet详解