一. Bubble Sort
主要思路:从最开始两个数开始选定,依次两两比较,若是大就交换。直到完成L-1次的排序。
方法:
外层从最末位循环到1(只剩最后一个时不用排序)。
内层从0循环到out - 1(因为比较时in + 1,会到达out),如果前位data[in]比后位data[in + 1]大则交换。每次内层循环过后,循环经过部分最后一位已经排好。
时间复杂度O(n ^ 2) ,其中比较约n^2/2,swap约n^2/4
二. Selection Sort
主要思路:找当前位及之后的最小值,与当前位交换。
方法:
外层从0到最末位-1(最后一个)。
内层循环前首先记录最小值索引min(默认为此时外层索引out),然后从out + 1到最末尾做遍历更新min。如遍历结束后min不是out,则与out位交换。
每次循环后out位,即内层循环的前一位,是已经被排序的。
时间复杂度O(n ^ 2) ,其中比较约n^2/2,swap约n/2
三. Insertion Sort
主要思路:把当前元素插入前面排好顺序序列的合适位置(从后开始,因为需要位移比当前数大的数,这样不会花费额外的时间遍历)。
方法:
外层从1到最末位。
内层循环前首先将外层out对应的值存入tmp,然后从out 开始遍历,如果值大于tmp则向后移,直到in为0或值小于tmp,将tmp插入(out == in则不需移动)。
每次循环后,out位,即内层循环的后一位,已被排序。
时间复杂度O(n ^ 2) ,其中比较约n^2/4,没有swap只有shift
在近似已排序数组时,时间复杂度接近O(n)
四. Merge Sort
主要思路:部分有序到整体有序。即从两两有序,到四四有序,到八八有序……将无序数组排序变成多次的“两个有序数组的按顺序合并”
可递归实现,也可不通过递归实现。可以不占用新空间,也可占用新空间。
方法:
(1)递归实现(重点)
先对左右两个数组分别排序一次(每一个排序是对左右数组的一半再分别排序,通过merge合并,直到分成长为1的数组),再通过一次merge合并到一起。
merge时需注意,半个子数组遍历完成后不要忘记将另半个子数组最后的部分拷贝到目标(或暂存)数组里。
(2)非递归实现
先分成长度为二的段,merge排序。再分成长度为4的段,merge一次。直到长度为数组长。
时间复杂度为O(nlogn),其中每次merge复杂度为O(n),共需进行O(logn)次的归并
原地merge可以参考链接:
http://blog.csdn.net/acaiwlj/article/details/10948035
五. Quick Sort
主要思路:整体有序到部分有序。选定一个数,把比它大的放在它左边,比它小的放在它右边,然后再排左右两个子序列。其中放置过程是左边找到一个大数,右边找到一个小数,交换,直到左右指针重合或覆盖,插入。
方法:预先选定一个枢纽参考值pivot value,这个值通常是最后一位(也可以是其它任何数,比如中间位,中间几位的平均,随机数等)。
外层递归方法将pivot值通过内层方法插入的位置,再将插入位置的左右两边分别传入此递归方法中进行排序,直到排序数列长度为1.
内层划分方法,分别从左边开始找大于pivot的数、从右边找小于pivot的数,如果此时左右查询位置尚且不重合,则左右互换,保证插入点左边均小于pivot,右边均大于pivot。如果左右指针交叉,则说明已经找到了插入点,插入pivot值即可。
(也可以将两层合并到一个递归方法中写,思想相同)
快速排序法平均时间复杂度为O(nlogn),可是最坏的情况下的排序时间为O(n^2)此时近似于找到的每一个pivot都是待排序数组中的极值。
不过quick仍然时平均时间最快的比较排序算法。(非比较排序算法待补充)
六. Heap Sort
优先队列priority queue的实现原理(min heap)。优先队列不可以实现按指定顺序遍历,因为其基于heap。
1.max heap
特点:近似完全树、每一个节点的孩子均小于这个节点(根节点最大)
2. insert
插入新节点时,先保证近似完全树(即插到最底层最后一个位置),再将它与父节点调换(如果小于父节点则完成,如果调换后仍大于新父节点则再调换),直到成为max heap。
3.delete max
用最后一个节点替换根节点保证完全树,再与其子节点比较、调换,直到成为max heap
4. 实现heap sort
通过将数据插入insert得到我们的max heap,再观察同时删除最大节点,我们可以实现排序。
因为是近似完全树,所以我们可以用表的形式储存树,从而节省指针所需要的空间。即我们的node class没有指针field,只有value。需要注意的是,要判断当前数组最大长度和当前树中节点的个数,如果数组空间已满则不能再插入。
我们可以发现给定index位置的节点,则:
左孩子 lc = (index * 2) + 1
右孩子 rc = (index + 1)* 2= lc + 1
父亲 parent = (index - 1)/2
最后一个有孩子(左孩子)的节点 lastP = currentSize / 2
insert可实现如下:
deleteMax可实现如下:
时间复杂度O(nlogn), 其中insert和delete的复杂度为O(logn), 需要插入/取出n次。
5.priorityqueue
常用方法:add/offer(obj),contains(obj), poll(obj), peek(), size(), toArray(), remove(obj),
PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
PriorityQueue
with the specified initial capacity that orders its elements according to the specified comparator.
七. 总结
最快 平均 最慢 稳定性
冒泡排序 O(n) O(n^2) O(n^2) 稳定
选择排序 O(n^2) O(n^2) O(n^2) 不稳定
插入排序 O(n) O(n^2) O(n^2) 稳定
merge排序 O(nlogn) O(nlogn) O(nlogn) 稳定
快速排序 O(nlogn) O(nlogn) O(n^2) 不稳定
堆排序 O(nlogn) O(nlogn) O(nlogn) 不稳定
(其中冒泡排序的最快实现需对上面的程序进行优化。用一个flag记录循环中有无swap,在无swap的情况下直接跳出循环)
表格可参见 http://blog.chinaunix.net/uid-21457204-id-3060260.html