算法笔记(排序算法)
2011-11-17 20:17
323 查看
比较常见的比较排序有插入排序,选择排序,冒泡排序,合并排序,快速排序,堆排序等方法,这些排序的期望时间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 }
相关文章推荐
- 算法分析学习笔记(三) - 排序算法(上)
- 《算法》阅读笔记-2.1初级排序算法
- STL学习笔记----14.STL算法之 (排序算法)
- [算法学习笔记]几个排序算法的比较
- 算法笔记(排序算法)
- 算法导论学习笔记-1-基础排序算法
- STL 算法 笔记 排序算法
- 算法笔记--八个常见排序算法总结
- 【算法学习笔记】-排序算法
- 数据结构与算法笔记 —— 排序算法及代码实现
- java 常用算法--复习笔记--基本排序算法
- 算法笔记--八个常见排序算法总结
- 【C++研发面试笔记】19. 常用算法-排序算法
- 算法第四版学习笔记之初级排序算法
- [笔记]算法复习笔记---排序算法(快速排序)
- 算法学习笔记:排序算法整理
- 算法笔记--排序算法-分治法-归并排序
- 【郝斌数据结构自学笔记】26_通过链表排序算法的演示再次详细讨论到底什么是算法以及到底什么是泛型【重点】
- STL学习笔记----14.STL算法之 (排序算法)
- [算法笔记]八种常见排序算法