各种算法的C#实现系列1 - 合并排序的原理及代码分析
2008-12-02 11:14
1366 查看
本文算法主要整理自《计算机算法设计与分析(第3版)》电子工业出版社,出于学习目的写下此文。
合并排序算法是用分治策略实现对n个元素进行排序的算法。
其基本思想是:将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成所要求的排好序的集合。
合并排序算法递归实现的程序:
class MergeRecursion<T> where T: new() { public void MergeSort(List<T> li, int left, int right) { //临时存储数据区 List<T> li_b = new List<T>(100); for (int i = 1; i <= 100; i++) { T item = new T(); li_b.Add(item); } //判断,保证至少有个元素 if (left < right) { //取中点 int m = (left + right) / 2; MergeSort(li, left, m); MergeSort(li, m + 1, right); //合并到数组b MergeCommon<T>.Merge(li, li_b, left, m, right); //复制回数组a Copy(li, li_b, left, right); } else { return; } } private static void Copy(List<T> li, List<T> li_b, int left, int right) { for (int i = left; i <= right; i++) { li[i] = li_b[i]; } } }
Merge方法将两个排好序的数组段合并到一个新的数组b中,然后由Copy将合并后的数组段再复制回a中。合并算法Merge的实现如下:
class MergeCommon<T> { public static void Merge(List<T> li, List<T> li_b, int left, int m, int right) { //比较用 BComparer<T> bc = BComparer<T>.Instance; //合并li[left:m]和li[m+1:right]到d[left:right] int i = left; int j = m + 1; int k = left; while ((i <= m) && (j <= right)) { if(bc.Compare(li[i],li[j]) <= 0) { li_b[k++] = li[i++]; } else { li_b[k++] = li[j++]; } } if (i > m) { for (int q = j; q <= right; q++) { li_b[k++] = li[q]; } } else { for (int q = i; q <= m; q++) { li_b[k++] = li[q]; } } } }
合并排序的非递归实现
由于递归操作是一种较消耗资源的操作,可以考虑实现无递归的合并排序
思想:首先将数组a中相邻的元素两两配对。用合并算法将他们排序,构成n/2组长度为2排好序的子数组段,然后再将它们排序成长度为4的排好序的子数组段,如此继续下去,直至整个数组排序好。
此思想的算法实现:
class MergeNRecursion<T> where T: new() { public void MergeSort(List<T> li, int n) { //临时存储数据区 List<T> li_b = new List<T>(100); for (int i = 1; i <= 100; i++) { T item = new T(); li_b.Add(item); } int s = 1; while (s < n) { MergePass(li,li_b,s,n); s += s; MergePass(li_b,li,s,n); s += s; } } private static void MergePass(List<T> li, List<T> li_b, int s, int n) { … … } }
其中MergePass用于合并排好序的相邻数组段。而具体的合并算法同样由Merge来实现。
MergePass算法实现:
private static void MergePass(List<T> li, List<T> li_b, int s, int n) { //合并大小为s的相邻子数组 int i = 0; while (i <= n - 2 * s) { //合并大小为s的相邻2段子数组 MergeCommon<T>.Merge(li,li_b,i,i+s-1,i+2*s-1); i = i + 2 * s; } //剩下的元素个数少于2s if (i + s < n) { MergeCommon<T>.Merge(li, li_b, i, i + s - 1, n - 1); } else { for (int j = i; j <= n - 1; j++) { li_b[j] = li[j]; } } }
解释:剩余元素个数少于2s后的处理程序中,i + s < n 这句判断可以得出个数不足2s的那段数组是否已排过序,如果if条件成立,说明剩余元素还未排过序,需要调用Merge方法排序,否则说明在前一次MergePass执行过程中就已经对相同的数组段进行过排序,不用重复进行,只需要直接复制到另一个数组即可(else实现)。
这种非递归的合并排序拥有自然合并排序的基本思想
算法中一个很重要的代码是对泛型对象的大小比较,代码来自光辉的晨星的Blog原文见此。
把代码粘贴于此:
public class BComparer<T> { //比较委托 Comparison<T> _comparison; static readonly Dictionary<Type, Delegate> _map = new Dictionary<Type, Delegate>(); //实现单例(代替构造函数的功能) static readonly BComparer<T> _instance = new BComparer<T>(); //构造函数 private BComparer() { } public static BComparer<T> Instance { get { System.Diagnostics.Debug.Assert(_map.ContainsKey(typeof(T))); //强转为具体的比较委托 _instance._comparison = (Comparison<T>)_map[typeof(T)]; return _instance; } } //情态构造,初始化 static BComparer() { //基础类型比较器 Type t = typeof(T); if (t == typeof(bool)) { Comparison<bool> cmp_bool = delegate(bool t1, bool t2) { return t1 == t2 ? 0 : (t1 ? 1 : -1); }; _map.Add(typeof(bool), (Delegate)cmp_bool); } if (t == typeof(int)) { Comparison<int> cmp_int = delegate(int t1, int t2) { return t1 > t2 ? 1 : (t1 == t2 ? 0 : -1); }; _map.Add(typeof(int), (Delegate)cmp_int); } //....其他 } //注册自定义比较 public static void Register<NT>(Comparison<NT> comparison) { System.Diagnostics.Debug.Assert(_map.ContainsKey(typeof(NT)) == false); _map.Add(typeof(NT), (Delegate)comparison); } //比较函数,以后用来实现IComparer用 public int Compare(T t1, T t2) { System.Diagnostics.Debug.Assert(_comparison != null); return _comparison(t1, t2); } }
原书文章中提到了自然合并排序,自然合并排序是上述非递归合并排序算法的一个变形。其基本思想是初始序列中有自然排好序的子数组,用1次对数组线性扫描就足以找出所有这些排好序的子数组段。将相邻的排好序的子数组段两两合并,构成更大的排好序的子数组。这样继续下去直至真个数组排好序。这样就比从1开始排相邻的元素效率要高很多。
下面是个人实现的一种自然排序,请指点:
class MergeNature<T> where T : new() { public static List<int> GetChildArray(List<T> li) { //保存子数组长度(最后一个元素位置) List<int> arr = new List<int>(100); int i = 0; arr.Add(-1); BComparer<T> bc = BComparer<T>.Instance; while (i < li.Count - 1) { if (bc.Compare(li[i], li[i + 1]) <= 0) { i++; continue; } else { arr.Add(i); i++; } } if (arr[arr.Count - 1] != 29) { arr.Add(29); } return arr; } /// <summary> /// 合并子数组(即列表) /// </summary> /// <param name="li"></param> public static void MergeSort(List<T> li) { //调用GetChildArray获取arr List<int> arr = GetChildArray(li); //临时存储数据区 List<T> li_b = new List<T>(100); for (int i = 1; i <= 100; i++) { T item = new T(); li_b.Add(item); } int s = 1; while (s < arr.Count - 1) { MergePass(li, li_b, s, arr); s += s; MergePass(li_b, li, s, arr); s += s; } } private static void MergePass(List<T> li, List<T> li_b, int s, List<int> arr) { int i = 0; int length = arr.Count - 1; while (i <= length - 2 * s ) { MergeCommon<T>.Merge(li, li_b, arr[i] + 1, arr[i + s], arr[i + 2 * s]); i = i + 2 * s; } //arr数组中剩余元素个数少于s if (i + s < length) { MergeCommon<T>.Merge(li, li_b, arr[i] + 1, arr[i + s], arr[length]); } else { for (int j = arr[i] + 1; j <= arr[length]; j++) { li_b[j] = li[j]; } } } }
相关文章推荐
- 排序技术_各种算法原理 图解 代码实现
- 排序技术_各种算法原理 图解 代码实现
- 排序技术_各种算法原理 图解 代码实现
- 排序技术_各种算法原理 图解 代码实现
- 排序技术_各种算法原理 图解 代码实现
- 排序技术_各种算法原理 图解 代码实现
- 第一篇:K-近邻分类算法原理分析与代码实现
- 经典内部排序算法学习总结(算法思想、可视化、Java代码实现、改进、复杂度分析、稳定性分析)
- 合并(归并)排序 算法原理与实现
- 第五篇:朴素贝叶斯分类算法原理分析与代码实现
- 第七篇:Logistic回归分类算法原理分析与代码实现
- Lucas-Kanade算法原理介绍及OpenCV代码实现分析
- 第十四篇:Apriori 关联分析算法原理分析与代码实现
- Python实现的选择排序算法原理与用法实例分析
- 第五篇:朴素贝叶斯分类算法原理分析与代码实现
- 算法分析之合并排序——merge sort子函数实现
- 算法分析(一)堆排序原理及java实现
- 决策树分类算法原理分析与代码实现
- Logistic回归分类算法原理分析与代码实现
- 数据挖掘:Apriori 关联规则分析算法原理分析与代码实现