-
1.冒泡排序(Bubble Sort)
- 思想:众所皆知,无需多言
- 平均时间复杂度:O(n2)
public class BubleSort { public static void bubleSort(int[] array) { if (array.length <= 1) { return; } else { for (int i = 0; i < array.length - 1; i ++) { for (int j = i; j < array.length; j ++) { if (array[i] > array[j]) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } } } } } }
-
2.桶排序(Bucket Sort,或者称箱子排序)
- 基于数据“分发-收集”模型
- 算法描述:按照一定规则设置确定数量的“桶”或者称“箱子”,显然设置“桶”或者“箱子”的规则必须满足两个条件:
- 数据经过你设计的规则必然能分发到某个“桶”中且只能唯一分发到这个桶中
- 按照你设计的规则从各个“桶”中按一定次序收集出来的数据便是有序的
- 平均时间复杂度:设上设置的“桶”个数为k,数据量为n,则为O(n+k)
- 显然此算法已不基于传统的“比较”模型,故时间复杂度不受O(nlogn)下限的限制,当要排序的数据按照一定规则均匀分布时,算法时间复杂度逼近线性O(n)
public class BucketSort { private static String appendZeroAtLeft(String str, int specifiedLength) { if (str.length() == specifiedLength) { return str; } else { StringBuffer sb = new StringBuffer(); if (str.length() < specifiedLength) { for (int i = 0; i < specifiedLength - str.length(); i ++) { sb.append("0"); } sb.append(str); } return sb.toString(); } } public static void bucketSort(int[] array) { if (array.length <= 1) { return; } else { /* * Find the max and min value in this array, and after this for loop * the value of the variable "max" and "min" must be one value of this array. */ int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for (int i = 0; i < array.length; i++) { max = max > array[i] ? max : array[i]; min = min < array[i] ? min : array[i]; } /* * Cover the condition that there are negative numbers in this array. */ List<Integer> negativeNumberList = null; boolean additionModificationTag = false; if (min < 0) { if (max + Math.abs(min) == 0) { return; } else if (max + Math.abs(min) < 0) { negativeNumberList = new ArrayList<Integer>(); } else { additionModificationTag = true; for (int i = 0; i < array.length; i++) { array[i] += Math.abs(min); } } } /* * Create new buckets at most 256 + one negative numbers bucket. */ Map<String, List<Integer>> bucketMap = new HashMap<String, List<Integer>>(); for (int i = 0; i <= 255; i ++) { String key = appendZeroAtLeft(Integer.toBinaryString(i << 24 >>> 24), 8); bucketMap.put(key, new ArrayList<Integer>()); } /* * Distribute elements of the array to be sorted. */ for (int i = 0; i < array.length; i++) { if (array[i] < 0) { if (negativeNumberList.size() == 0) { negativeNumberList.add(array[i]); } else { for (int j = 0; j <= negativeNumberList.size(); j ++) { if (j == negativeNumberList.size()) { negativeNumberList.add(array[i]); break; } if (array[i] <= negativeNumberList.get(j)) { negativeNumberList.add(j, array[i]); break; } } } } else { String key = appendZeroAtLeft( Integer.toBinaryString(array[i] << 12 >>> 24), 8); if (bucketMap.get(key).size() == 0) { bucketMap.get(key).add(array[i]); } else { for (int j = 0; j <= bucketMap.get(key).size(); j ++) { if (j == bucketMap.get(key).size()) { bucketMap.get(key).add(array[i]); break; } if (array[i] <= bucketMap.get(key).get(j)) { bucketMap.get(key).add(j, array[i]); break; } } } } } /* * Gather the elements dispersed in those different buckets. */ for (int i = 0; i < array.length;) { if (!additionModificationTag) { for (Integer element : negativeNumberList) { array[i] = element; i ++; } } for (int j = 0; j < bucketMap.size(); j ++) { String key = appendZeroAtLeft( Integer.toBinaryString(j << 24 >>> 24), 8); if (bucketMap.get(key).size() != 0) { for (Integer element : bucketMap.get(key)) { array[i] = element; i ++; } } } } /* * Restoration dispose. */ if (additionModificationTag) { for (int i = 0; i < array.length; i++) { array[i] -= Math.abs(min); } } } } }
-
3.鸡尾酒排序(Cocktail Sort,双向的冒泡排序)
- 平均时间复杂度:O(n2)
- 此算法与冒泡排序的不同处在于排序时对序列双向冒泡,设置“交换标志位”,当不再发生交换时认为序列有序
public class CocktailSort { public static void cocktailSort(int[] array) { if (array.length <= 1) { return; } else { boolean flag = true; while (flag) { flag = false; int fromIndex = 0; int toIndex = array.length - 1; for (int i = 0; i < toIndex; i ++) { if (array[i] > array[i + 1]) { flag = true; array[i] ^= array[i + 1]; array[i + 1] ^= array[i]; array[i] ^= array[i + 1]; } } toIndex --; for (int j = toIndex; j > fromIndex; j --) { if (array[j] < array[j - 1]) { flag = true; array[j] ^= array[j - 1]; array[j - 1] ^= array[j]; array[j] ^= array[j - 1]; } } fromIndex ++; } } } }
-
4.Gnome Sort
- 平均时间复杂度:O(n2)
- 算法思想:每趟循环找到序列中第一个逆序的元素,把它和在它前面的已有序的元素逐个进行比较、交换(注意是交换,不是元素的依次后移),所以只是类似“插入排序”
- 算法特点:代码极其精简
public class GnomeSort { public static int[] gnomeSort(int[] array) { int i = 0; while (i < array.length) { if (i == 0 || array[i - 1] <= array[i]) { i ++; } else { int temp = array[i - 1]; array[i - 1] = array[i]; array[i] = temp; i --; } } return array; } }
-
5.插入排序(Insertion Sort)
- 平均时间复杂度:O(n2)
- 算法思想:以首元素为初始有序序列,依次取后面的待排序数据在有序序列中寻找它应该处于的位置,这样不断扩大有序序列,直到整个序列有序
- 算法缺点很明显:可能发生数据的频繁移位
public class InsertionSort { public static void insertionSort(int[] array) { if (array.length <= 1) { return; } else { for (int i = 0, j = i; i < array.length - 1; j = ++ i) { int temp = array[i + 1]; while (temp < array[j]) { array[j + 1] = array[j]; if (j -- == 0) { break; } } array[j + 1] = temp; } } } }
-
6.图书馆排序(Library Sort)
- 平均时间复杂度:O(nlogn)
- 算法思想:排列图书时,如果在每本书之间留一定的空隙,那么在插入新书时就有可能会少移动一些书,说白了就是在插入排序的基础上,给元素与元素之间留一定的空隙,这个空隙越大,需要移动的书就会越少
- 典型的以空间换时间的做法
- 元素与元素之间的“空隙”没有理论上的最佳实践,依据具体情况而定
- 图书馆排序的关键是分配空间,分配完空间后直接使用插入排序即可
public class LibrarySort { private static final double FACTOR = Math.E; private static List<Integer> dispenser(int[] array) { List<Integer> longestSortedSequence = FindLongestSortedSequence.findLongestSortedSequence(array); int tempSize = longestSortedSequence.size(); int extendCapacity = (int) ((array.length - longestSortedSequence.size()) * FACTOR); int index = 0; int firstFillingQuantity = (int) (extendCapacity / FACTOR); for (int i = 0; i < firstFillingQuantity; i ++) { longestSortedSequence.add(index, null); } index += firstFillingQuantity + 1; int remainingPositions = extendCapacity - firstFillingQuantity; int remainder = remainingPositions % tempSize; int secondFillingQuantity = remainder + remainingPositions / tempSize; for (int i = 0; i < secondFillingQuantity; i++) { longestSortedSequence.add(index, null); } index += secondFillingQuantity + 1; int stepLength = (extendCapacity - firstFillingQuantity - secondFillingQuantity) / (tempSize - 2); for (int i = 0; i < tempSize - 2; i ++) { for (int j = 0; j < stepLength; j ++) { longestSortedSequence.add(index, null); } index += stepLength + 1; } return longestSortedSequence; } public static int[] librarySort(int[] array) { int[] resultArray = new int[array.length]; List<Integer> extendList = dispenser(array); for (int i = 0; i < array.length; i++) { if (!extendList.contains(array[i])) { int j = 0; while (j < extendList.size()) { if (extendList.get(j) != null) { if (extendList.get(j) >= array[i]) { if (extendList.get(j - 1) == null) { extendList.set(j - 1, array[i]); break; } else { extendList.add(j, array[i]); break; } } } j ++; } } } int index = 0; for (Integer element : extendList) { if (element != null) { resultArray[index ++] = element; } } return resultArray; } }
-
7.归并排序(Merge Sort,此处介绍2-路归并)
- 平均时间复杂度:O(nlogn)
- 建立在归并基础上的一种排序算法
- 是“分治(Divide and Conquer)”思想的应用
- 算法思想见图:
public class MergeSort { private static void merge(int[] array, int fromIndex, int splitIndex, int toIndex) { int[] tempArray = new int[toIndex - fromIndex + 1]; int index = 0; int i = fromIndex; int j = splitIndex + 1; while (i <= splitIndex && j <= toIndex) { if (array[i] < array[j]) { tempArray[index ++] = array[i ++]; } else if (array[i] == array[j]) { tempArray[index ++] = array[i ++]; tempArray[index ++] = array[j ++]; } else { tempArray[index ++] = array[j ++]; } } while (i <= splitIndex) { tempArray[index ++] = array[i ++]; } while (j <= toIndex) { tempArray[index ++] = array[j ++]; } for (int k = fromIndex, m = 0; k <= toIndex;) { array[k ++] = tempArray[m ++]; } } public static void mergeSort(int[] array, int fromIndex, int toIndex) { int length = toIndex - fromIndex + 1; if (length <= 1 || fromIndex < 0 || toIndex <= 0) { return; } int stepLength = 2; while (length >= stepLength) { int startIndex = fromIndex; int counter = 0; while ((length - counter * stepLength) >= stepLength) { merge(array, startIndex, startIndex + (stepLength - 1) / 2, startIndex + stepLength - 1); startIndex += stepLength; counter ++; } if ((length - counter * stepLength) > stepLength / 2) { merge(array, counter * stepLength, counter * stepLength + stepLength / 2 - 1, toIndex); } stepLength *= 2; } merge(array, fromIndex, fromIndex + stepLength / 2 - 1, toIndex); } }
-
8.基数排序(Radix Sort)
- 个人认为可以看作多次的“桶排序”、“箱子排序”,即:多关键字,按照各个关键字对数据进行多次“分发-收集”
public class RadixSort { private static Map<String, List<Integer>> buildSortingMap() { Map<String, List<Integer>> sortingMap= new HashMap<String, List<Integer>>(); for (int i = 9; i >= -9; i --) { if (i != 0) { sortingMap.put(String.valueOf(i), new ArrayList<Integer>()); } else { sortingMap.put("+0", new ArrayList<Integer>()); sortingMap.put("-0", new ArrayList<Integer>()); } } return sortingMap; } private static int getLongestLength(int[] array) { int longestLength = 0; for (int i = 0; i < array.length; i++) { int tempLength = String.valueOf(array[i]).length(); if (array[i] < 0) { longestLength = tempLength - 1 > longestLength ? tempLength - 1 : longestLength; } else { longestLength = tempLength > longestLength ? tempLength : longestLength; } } return longestLength; } private static void distributionCollection(int[] array, int which, Map<String, List<Integer>> sortingMap) { int[] tempArray = new int[array.length]; int index = 0; /* * Distribution. */ for (int i = 0; i < array.length; i++) { String key = null; int temp = 0; if (which > 1) { temp = (int) (array[i] % Math.pow(10, which) / Math.pow(10, which - 1)); } else { temp = (int) (array[i] % Math.pow(10, which)); } if (temp == 0) { if (array[i] >= 0) { key = "+0"; } else { key = "-0"; } } else { key = String.valueOf(temp); } List<Integer> tempList = sortingMap.get(key); if (tempList.size() == 0) { tempList.add(array[i]); } else { for (int j = 0; j <= tempList.size(); j ++) { if (j == tempList.size()) { tempList.add(array[i]); break; } if (array[i] < tempList.get(j)) { tempList.add(j, array[i]); break; } } } } /* * Collection. */ for (int k = -9; k <= 9; k ++) { if (k != 0) { for (Integer element : sortingMap.get(String.valueOf(k))) { tempArray[index ++] = element; } } else { for (Integer element : sortingMap.get("-0")) { tempArray[index ++] = element; } for (Integer element : sortingMap.get("+0")) { tempArray[index ++] = element; } } } for (int j = 0; j < tempArray.length; j++) { array[j] = tempArray[j]; } } public static int[] radixSort(int[] array) { int longestLength = getLongestLength(array); for (int i = 1; i <= longestLength; i ++) { Map<String, List<Integer>> sortingMap = buildSortingMap(); distributionCollection(array, i, sortingMap); } return array; } }
- PS:
- Code都是去年工作那半年闲暇时间写的,很大目的是为了弥补自己本科学习期间没动手写这些基础code的缺憾
- 实现完全是按照个人对维基百科上算法的描述理解来的,有谬误之处望大家多指正
- 所有code都经过UT测试