前言
都什么时代了,还写排序算法的总结?
原因有二。一是别人的精彩永远是别人的,你只有鼓掌的份儿;有些事情实际动手去做了才会有所体会。
二是排序算法是一类基础类的算法,不光是IT从业者真正入门的门槛,也是一些高级算法的关键部分或算法评估的benchmark。
计划说明的算法内容有哪些?
算法的思想、Java代码实现和平均算法复杂度、算法运行完整示例。
参考文献有哪些?
wiki[EB/OL]
Shaffer C. A. Data Structure and Algorithm Analysis in Java, 3rd Edition[M]. Dover Publications, New York. 2011.
目录
0 实现工具类
1 插入排序
2 选择排序
3 冒泡排序
4 合并排序
5 堆排序
6 快速排序
7 桶排序
8 基数排序
9 外排序
10 Funny methods
内容
0 实现工具类
//时间费用注解
/** * Description: 时间费用注解<br/> * Date: 2014-4-26 下午4:49:39 */ public @interface Expense { /** 标记 */ TAG tag(); /** 值 */ String value(); enum TAG { Θ, Ο, Ω } }
//数据结构与算法工具类
package util; /** * * Description: 数据结构与算法工具类<br/> * Date: 2014-5-6 下午10:27:18 */ public class DSUtil { // 单元测试 public static void main(String[] args) { Integer[] A = { 1, 2, 3, 4, 5 }; println(A); swap(A, 1, 3); println(A); } public static Integer[] SORT_TEST_ARRAY = new Integer[] { 42, 20, 17, 13, 28, 14, 23, 15 }; public static <E> void swap(E[] A, int i, int j) { E temp = A[i]; A[i] = A[j]; A[j] = temp; } public static <E> void println(E[] A) { for (E a : A) { System.out.print(a + " "); } System.out.println(); } }
//排序方法算法接口 - 一般用Integer数组表示待排序列表
package internalsort; /** * Description: 排序方法算法接口<br/> * Date: 2014-5-5 下午10:46:52 */ public interface Sort<E extends Comparable<? super E>> { /** * Description: 将E元素数组A排序<br/> * PRE: <br/> * POST: <br/> */ void sort(E[] A); }
1 插入排序
package internalsort; import util.DSUtil; import util.Expense; /** * Description: 插入排序<br/> * 思想:从列表第二个元素开始直到列表末尾,一次将每个元素放置到正确的位置<br/> * Date: 2014-5-5 下午10:43:54 */ public class InsertionSort implements Sort<Integer> { /* @see internalsort.Sort#sort(E[]) */ @Override @Expense(tag = Expense.TAG.Θ, value = "n^2") public void sort(Integer[] A) { int n = A.length; for (int i = 1; i < n; i++) {//从第二个元素开始,直至列表末尾 for (int j = i; j > 0; j--) {//从i开始向列表头部,若j处值<j-1处值,交换两者位置 if (A[j] < A[j - 1]) { DSUtil.swap(A, j, j - 1); } } } } public static void main(String[] args) { InsertionSort service = new InsertionSort(); Integer[] A = DSUtil.SORT_TEST_ARRAY; DSUtil.println(A); service.sort(A); DSUtil.println(A); } }
2 选择排序
package internalsort; import util.DSUtil; import util.Expense; /** * Description: 选择排序<br/> * 思想:从列表头部开始,将当前位置的值与后面的最小值交换<br/> * Date: 2014-5-5 下午11:15:30 */ public class SelectionSort implements Sort<Integer> { /* @see internalsort.Sort#sort(E[]) */ @Override @Expense(tag = Expense.TAG.Θ, value = "n^2") public void sort(Integer[] A) { int n = A.length; for (int i = 0; i < n - 1; i++) { int smallestIndex = i; for (int j = i + 1; j < n; j++) { if (A[smallestIndex] > A[j]) { smallestIndex = j; } } DSUtil.swap(A, i, smallestIndex); } } //方法1:缺陷交换次数过多 public void sort1(Integer[] A) { int n = A.length; for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { if (A[i] > A[j]) { DSUtil.swap(A, i, j); } } } } public static void main(String[] args) { SelectionSort service = new SelectionSort(); Integer[] A = DSUtil.SORT_TEST_ARRAY; DSUtil.println(A); service.sort(A); DSUtil.println(A); } }
3 冒泡排序
package internalsort; import util.DSUtil; import util.Expense; /** * Description: 冒泡排序<br/> * 思想:第i遍将第i小值排好序<br/> * Date: 2014-5-5 下午11:01:56 */ public class BubbleSort implements Sort<Integer> { /* @see internalsort.Sort#sort(E[]) */ @Override @Expense(tag = Expense.TAG.Θ, value = "n^2") public void sort(Integer[] A) { int n = A.length; for (int i = 0; i < n - 1; i++) {//遍数标识,共需n-1遍 for (int j = n - 1; j > i; j--) {//内部循环从列表尾部开始 if (A[j] < A[j - 1]) {//小元素上升 DSUtil.swap(A, j, j - 1); } } } } public static void main(String[] args) { BubbleSort service = new BubbleSort(); Integer[] A = DSUtil.SORT_TEST_ARRAY; DSUtil.println(A); service.sort(A); DSUtil.println(A); } }
4 合并排序
package internalsort; import util.DSUtil; import util.Expense; /** * Description: 合并排序<br/> * 思想:依然是分治策略,将待排序划分为两个子序列,分别将左右两个子序列排好序后将两个子序列合并<br/> * Date: 2014-5-6 下午9:32:14 */ public class MergeSort implements Sort<Integer> { /* @see internalsort.Sort#sort(E[]) */ @Override @Expense(tag = Expense.TAG.Θ, value = "nlogn") public void sort(Integer[] A) { Integer[] deepCopy = new Integer[A.length]; for (int i = 0; i < A.length; i++) { deepCopy[i] = A[i]; } //version 1 // int n = A.length; // Integer[] A2 = new Integer[n]; // for (int i = 0; i < n; i++) {//注意要初始化 // A2[i] = 0; // } // mergeSort(A, A2, 0, n - 1); //version 2 mergeSortV2(A, 0, A.length - 1); DSUtil.println(deepCopy); } private void mergeSort(Integer[] A, Integer[] A2, int l, int r) { if (l == r) return; int mid = (l + r) / 2; mergeSort(A, A2, l, mid); mergeSort(A, A2, mid + 1, r); for (int i = l; i <= r; i++) {//[l, r] A2[i] = A[i]; } int i = l, j = mid + 1;//左右子列表的索引 for (int curr = l; curr <= r; curr++) {//[l, r] if (i == mid + 1) {//左子列表空 A[curr] = A2[j++]; } else if (j > r) {//右子列表空 A[curr] = A2[i++]; } else if (A2[i] < A2[j]) {//选择较小的值 A[curr] = A2[i++]; } else { A[curr] = A2[j++]; } } } private void mergeSortV2(Integer[] A, int l, int r) { if (l == r) return; int mid = (l + r) / 2; mergeSortV2(A, l, mid); mergeSortV2(A, mid + 1, r); merge(A, l, r); } private void merge(Integer[] A, int l, int r) { int mid = (l + r) / 2; Integer[] A2 = new Integer[r + 1]; for (int i = l; i <= r; i++) {//[l, r] A2[i] = A[i]; } int i = l, j = mid + 1;//左右子列表的索引 for (int curr = l; curr <= r; curr++) {//[l, r] if (i == mid + 1) {//左子列表空 A[curr] = A2[j++]; } else if (j > r) {//右子列表空 A[curr] = A2[i++]; } else if (A2[i] < A2[j]) {//选择较小的值 A[curr] = A2[i++]; } else { A[curr] = A2[j++]; } } } public static void main(String[] args) { MergeSort service = new MergeSort(); Integer[] A = DSUtil.SORT_TEST_ARRAY; DSUtil.println("Before", A); service.sort(A); DSUtil.println("After", A); } }
在DSUtil工具类中添加方法
public static <E> void println(String comment, E[] A) { System.out.print(comment + ": "); for (E a : A) { System.out.print(a + " "); } System.out.println(); }
5 堆排序
package internalsort; import tree.heap.MaxHeap; import util.DSUtil; import util.Expense; /** * Description: 堆排序<br/> * 思想:将待排序列表组织成最大堆,依次移除最大元素,因最大堆实现中移除的元素放置于当前堆的末尾位置,故最终得到排好序的列表<br/> * Date: 2014-5-9 下午11:17:23 */ public class HeapSort implements Sort<Integer> { /* @see internalsort.Sort#sort(E[]) */ @Override @Expense(tag = Expense.TAG.Θ, value = "nlogn") public void sort(Integer[] A) { int n = A.length; MaxHeap<Integer> maxHeap = new MaxHeap<Integer>(A, n, n); for (int i = 0; i < n; i++) {//依次移除最大值,放于当前堆的末尾 maxHeap.removemax(); } } public static void main(String[] args) { HeapSort service = new HeapSort(); Integer[] A = DSUtil.SORT_TEST_ARRAY; DSUtil.println(A); service.sort(A); DSUtil.println(A); } }
最大堆实现类
package tree.heap; import org.junit.Assert; import util.Expense; /** * Description: 最大堆<br/> * 性质:父节点值>= 左、右节点值 * Date: 2014-4-27 下午11:34:32 */ public class MaxHeap<E extends Comparable<? super E>> { private E[] heap;//内部数组 private int size;//最大容量 private int n;//当前容量 public MaxHeap(E[] heap, int currentSize, int maxSize) { this.heap = heap; this.n = currentSize; this.size = maxSize; buildheap(); } /** * Description: 构建堆<br/> * PRE: <br/> * POST: <br/> */ @Expense(tag = Expense.TAG.Θ, value = "n") private void buildheap() { //将每个内部节点放置到正确的位置 for (int i = n / 2 - 1; i >= 0; i--) siftdown(i); } /** * Description: 将元素放置在正确的位置:与其子节点值最大者交换位置,一直向下处理<br/> * PRE: <br/> * POST: <br/> */ private void siftdown(int pos) { Assert.assertTrue("Heap postion out of range", pos >= 0 && pos < n); while (!isLeaf(pos)) { int childPos = leftChild(pos);//先选择左子节点位置 if (childPos < (n - 1) && heap[childPos].compareTo(heap[childPos + 1]) < 0) {//若右子节点值较大,选择右子节点 childPos++; } if (heap[pos].compareTo(heap[childPos]) >= 0) {//若该位置值大于其子节点的值,直接返回 return; } else {//否则交换两者的值,继续 swap(heap, pos, childPos); pos = childPos; } } } @Expense(tag = Expense.TAG.Θ, value = "logn") public E removemax() { Assert.assertTrue("Removing from empty heap", n > 0); swap(heap, 0, --n);//将最大值(root)与最后一位交换 if (n != 0) { siftdown(0);//将新堆的root放置到正确的位置 } return heap[n];//返回新堆的最后一个值 } /** * Description: 删除堆中位置pos处的值<br/> * PRE: <br/> * POST: <br/> */ public E remove(int pos) { Assert.assertTrue("Heap position out of range", pos >= 0 && pos < n); if (pos == (n - 1)) {//直接将堆最后一个值返回 n--; } else { swap(heap, pos, --n);//将该位置值与堆中最后一个值交换 //如果交换的值是一个大值,向上推, example 7362154 =(1)=> 746235,按层次输出 while ((pos > 0) && heap[pos].compareTo(heap[parent(pos)]) > 0) { swap(heap, pos, parent(pos)); pos = parent(pos); } if (n != 0) { siftdown(pos);//如果交换的值是一个小值,向下推, example 7635432 =(6)=>753412 } } return heap[n]; } /** * Description:当前堆容量<br/> * PRE: <br/> * POST: <br/> */ public int heapsize() { return n; } /** * Description: 若该位置是叶子的位置返回true<br/> * PRE: <br/> * POST: <br/> */ public boolean isLeaf(int pos) { return (pos >= n / 2) && (pos < n); } public int leftChild(int pos) { Assert.assertTrue("Position has no left child", pos < n / 2); return 2 * pos + 1; } public int rightChild(int pos) { Assert.assertTrue("Position has no right child", pos < (n - 1) / 2); return 2 * pos + 2; } public int parent(int pos) { Assert.assertTrue("Position has no parent", pos > 0); return (pos - 1) / 2; } /** * Description: 插入值<br/> * PRE: <br/> * POST: <br/> */ @Expense(tag = Expense.TAG.Θ, value = "logn") public void insert(E e) { Assert.assertTrue("Heap is full", n < size); int curr = n++; heap[curr] = e; //若curr的值大于其父节点的值,交换两者的位置 while ((curr != 0) && heap[curr].compareTo(heap[parent(curr)]) > 0) { swap(heap, curr, parent(curr)); curr = parent(curr); } } //交换数组中两个位置的值 private void swap(E[] array, int pos1, int pos2) { E temp = array[pos1]; array[pos1] = array[pos2]; array[pos2] = temp; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { sb.append(heap[i] + " "); } return sb.toString(); } }
6 快速排序
package internalsort; import util.DSUtil; import util.Expense; /** * Description: 快速排序<br/> * 思想:将待排序列表按中心点pivot划分为两个子列表,将两个子列表分别排序后再合并<br/> * Date: 2014-5-6 下午9:03:56 */ public class QuickSort { static Integer[] A = new Integer[] { 72, 6, 57, 88, 85, 42, 83, 73, 48, 60 }; public static void main(String[] args) { DSUtil.println(A); // 测试partition // System.out.println(partition(A, -1, A.length - 1, 60)); // 测试quickSort quickSort(A, 0, A.length - 1); DSUtil.println(A); } /** * 根据pivot划分A中[l, r]标识的子序列,返回pivot应该放置的位置索引<br/> * 此时左边的元素小于pivot,右边的元素大于pivot */ @Expense(tag = Expense.TAG.Θ, value = "r-l+1") static int partition(Integer[] A, int l, int r, int pivotValue) { do { // 从小索引开始找值大于pivotValue的 while (A[++l] < pivotValue); // 从大索引开始找值小于pivotValue的 while ((r != 0) && A[--r] > pivotValue); // System.out.println("l=" + l + ", r=" + r); DSUtil.swap(A, l, r); // DSUtil.println(A); } while (l < r); // System.out.println("l=" + l + ", r=" + r); DSUtil.swap(A, l, r); // DSUtil.println(A); return l; } @Expense(tag = Expense.TAG.Θ, value = "nlogn") static void quickSort(Integer[] A, int i, int j) { int pivotIndex = findpivot(i, j); DSUtil.swap(A, pivotIndex, j);// 将中心点值放于数组末尾 int pivotValue = A[j]; int k = partition(A, i - 1, j, pivotValue); DSUtil.swap(A, k, j);// 将中心点值放在正确的位置 // 递归处理左右子序列 if ((k - i) > 1) { quickSort(A, i, k - 1); } if ((j - k) > 1) { quickSort(A, k + 1, j); } } // 定位中心点,方法1:中间点 @Expense(tag = Expense.TAG.Θ, value = "1") static int findpivot(int i, int j) { return (i + j) / 2; } }
7 桶排序
8 基数排序
9 外排序
10 Funny methods
SleepSort, using Thread#sleep(ms)
public class SleepSort { public static void main(String[] args) { int[] ints = {1, 4, 7, 3, 8, 9, 2, 6, 5}; SortThread[] sortThreads = new SortThread[ints.length]; for (int i = 0; i < sortThreads.length; i++) { sortThreads[i] = new SortThread(ints[i]); } for (int i = 0; i < sortThreads.length; i++) { sortThreads[i].start(); } } } class SortThread extends Thread { int ms = 0; public SortThread(int ms) { this.ms = ms; } public void run() { try { sleep(ms * 10 + 10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ms); } }
BogoSort
import java.util.Random; public class Bogosort { public static void main(String[] args) { int[] array = new int[] {1, 4, 7, 3, 8, 9, 2, 6, 5}; while (!isInOrder(array)) { permute(array); renderArray(array); } } private static final void renderArray(int[] array) { for (int a : array) { System.out.print(a + " "); } System.out.println(); } private static <T> void permute(int[] array) { Random value = new Random(); int radomPosition = 0; for (int i = array.length; i > 0; i--) { radomPosition = Math.abs(value.nextInt()) % i; if (radomPosition < 0) { radomPosition = -radomPosition; } swap(array, i - 1, radomPosition); } } private static void swap(int[] array, int p1, int p2) { int temp = array[p1]; array[p1] = array[p2]; array[p2] = temp; } private static boolean isInOrder(int[] array) { for (int i = 0; i < array.length - 1; i++) { if (array[i] > array[i + 1]) { return false; } } return true; } }