算法笔记(排序算法)
2011-11-17 20:17 java线程例子 阅读(227) 评论(0) 编辑 收藏 举报 比较常见的比较排序有插入排序,选择排序,冒泡排序,合并排序,快速排序,堆排序等方法,这些排序的期望时间T(n)>O(nlgn).线性排序的方法有计数排序,基数排序,桶排序。排序方法的选择应该考虑几个比较重要的特性:时间复杂度,空间复杂度,稳定性,常数因子,算法实现难度,输入依赖性等。综合这些因素来讲,在实际应用中,线性排序未必就比比较排序要好。
下面是常见的排序实现(计数排序和快速排序可参见前面的博文):
public class MySort { #region 插入排序 /// <summary> /// 插入排序算法 /// 原地置换:是 /// 平均性能:O(n^2) /// 最好性能O(n):已符合目标排序; /// 最坏情况O(n^2):目标排序的倒排; /// </summary> /// <param name="Source">待排数组</param> public static void InsertionSort(int[] Source) { int theLength = Source.Length; //从第2个开始,将当前数插入到0..i-1数组中去. for (int i = 1; i < theLength; i++) { int theCurrVal = Source[i]; int j = i - 1; //找到插入位置,并后移元素. while (j>=0 && theCurrVal < Source[j]) { Source[j + 1] = Source[j]; j--; } Source[j + 1] = theCurrVal; } } #endregion #region 合并排序. /// <summary> /// 归并排序 /// 性能:O(nlog(n)),很稳定,不依赖于输入 /// 原地置换:非原地置换排序,需要O(n)辅助空间. /// 稳定性:稳定的. /// </summary> /// <param name="Source">待排序数组</param> /// <param name="Start">开始位置</param> /// <param name="End">结束位置</param> public static void MergeSort(int[] Source, int Start, int End) { if (Start + 1 == End) { if (Source[Start] > Source[End]) { int theTmp = Source[End]; Source[End] = Source[Start]; Source[Start] = theTmp; } return; } if (Start == End) { return; } int theCenter = (Start + End) / 2; MergeSort(Source, Start, theCenter); MergeSort(Source, theCenter + 1, End); MergeArray(Source, Start, theCenter, theCenter + 1, End); } /// <summary> /// 归并两组. /// </summary> /// <param name="Source">源数组</param> /// <param name="S1">数组1开始索引</param> /// <param name="E1">数组1结束索引</param> /// <param name="S2">数组2开始索引</param> /// <param name="E2">数组2结束索引</param> private static void MergeArray(int[] Source, int S1, int E1, int S2, int E2) { int theLen = E2 - S1 + 1; int[] theTemp = new int[theLen]; int i = S1; int j = S2; int k=0; while (i <= E1 && j <= E2) { if (Source[i] < Source[j]) { theTemp[k] = Source[i]; i++; } else { theTemp[k] = Source[j]; j++; } k++; } while (i <= E1) { theTemp[k] = Source[i]; i++; k++; } while (j <= E2) { theTemp[k] = Source[j]; j++; k++; } for (i = 0; i < theLen; i++) { Source[S1 + i] = theTemp[i]; } } #endregion #region 堆排序 /// <summary> /// 堆排序: /// 平均性能:O(n*lgn),性能依赖于输入,性能最好是输入倒序排列(最大堆,目标为升序) /// 稳定性:不稳定; /// 原地置换:原地置换排序; /// </summary> /// <param name="A"></param> public static void HeapSort(int[] A) { //建堆O(n) HeapBuilder(A); int theHeapSize = A.Length; //循环取当前堆的最大值,并整理剩余堆 //O(n*lgn) for (int i = A.Length-1; i > 0; i--) { int theTmp = A[0]; A[0] = A[i]; A[i] = theTmp; theHeapSize--; HeapMax(A, 0, theHeapSize); } } /// <summary> /// 取当前节点的左儿子 /// </summary> /// <param name="i">当前节点索引</param> /// <returns>左儿子节点索引</returns> private static int HeapL(int i) { return i * 2 + 1; } /// <summary> /// 取当前节点的右儿子 /// </summary> /// <param name="i">当前节点索引</param> /// <returns>右儿子节点索引</returns> private static int HeapR(int i) { return i * 2 + 2; } /// <summary> /// 取当前节点的父节点索引 /// </summary> /// <param name="i">当前节点索引</param> /// <returns>父节点索引</returns> private static int HeapP(int i) { return (i + 1) / 2 - 1; } /// <summary> /// 向下调整指定节点的,使得当前堆保持最大堆性质。最多调整lgn次. /// </summary> /// <param name="A">要调整的堆数组</param> /// <param name="i">要调整的节点</param> /// <param name="HeapSize">堆大小</param> private static void HeapMax(int[] A, int i, int HeapSize) { int theL = HeapL(i); int theR = HeapR(i); int thelargest = i; if (theL < HeapSize && A[theL] > A[thelargest]) { thelargest = theL; } if (theR < HeapSize && A[theR] > A[thelargest]) { thelargest = theR; } if (thelargest != i) { int theTmp = A[i]; A[i] = A[thelargest]; A[thelargest] = theTmp; HeapMax(A, thelargest, HeapSize); } } /// <summary> /// 构建最大堆 /// </summary> /// <param name="A">待排序数组</param> private static void HeapBuilder(int[] A) { int theHeapSize = A.Length; //满2叉树的性质,内节点数2^(H)-1,外节点数:2^H (H为树的高度) int theP = A.Length / 2 - 1; for (int i = theP; i >= 0; i--) { HeapMax(A,i,theHeapSize); } } #endregion #region 桶排序 /// <summary> /// 桶排序: /// 性能:桶排序的期望时间为O(n),最坏时间为O(n^2).桶排序的性能依赖于输入序列的分布情况 /// 如果重复元素或者分布不均匀的情况下,桶排序就不太适合.另外桶排序需要O(n)额外存储空间。 /// 原地置换:非原地置换排序. /// 稳定性:桶排序的稳定性依赖于桶内排序算法。 /// </summary> /// <param name="A"></param> public static void BucketSort(double[] A) { //double theMax = double.MinValue; //for (int i = 0; i < A.Length; i++) //{ // if (theMax < A[i]) // { // theMax = A[i]; // } //} int theLength = A.Length; //准备桶.O(n) List<List<double>> theBuckets = new List<List<double>>(); for (int i = 0; i < theLength; i++) { theBuckets.Add(new List<double>()); } //分桶O(n) for (int i = 0; i < theLength; i++) { int theIndex =Convert.ToInt32(Math.Floor(A[i] * theLength)); theBuckets[theIndex].Add(A[i]); } //桶内排序期望:O(n),如果分布均衡,则每个桶的元素都在个位数的情况下, //总的桶内排序时间可认为是线性的。 for (int i = 0; i < theLength; i++) { InsertionSort(theBuckets[i]); } //桶合并.O(n) int theCurrIndex = 0; for (int i = 0; i < theLength; i++) { for (int j = 0; j < theBuckets[i].Count; j++) { A[theCurrIndex] = theBuckets[i][j]; theCurrIndex++; } theBuckets[i].Clear(); } theBuckets.Clear(); } /// <summary> /// 插入排序算法,原地置换排序,O(n^2) /// </summary> /// <param name="Source">待排数组</param> private static void InsertionSort(List<double> Source) { int theLength = Source.Count; //从第2个开始,将当前数插入到0..i-1数组中去. for (int i = 1; i < theLength; i++) { double theCurrVal = Source[i]; int j = i - 1; //找到插入位置,并后移元素. while (j >= 0 && theCurrVal < Source[j]) { Source[j + 1] = Source[j]; j--; } Source[j + 1] = theCurrVal; } } #endregion }