归并排序

归并排序也是采用“分而治之”的方式。刚发现分治法是一种算法范式,我还一直以为是一种需要意会的思想呢。

不好意思了,孤陋寡闻了,哈哈!

原理:将两个有序的数列,通过比较,合并为一个有序数列。 维基入口

为方便理解,此处实现用了List<int>的一些方法,随后有IList<int>版本。

实现如下:

public static List<int> MergeSortOnlyList(List<int> data, int low, int high)
{
if (low == high)
return new List<int> { data[low] };
List<int> mergeData = new List<int>();
int mid = (low + high) / ;
List<int> leftData = MergeSortOnlyList(data, low, mid);
List<int> rightData = MergeSortOnlyList(data, mid + , high);
int i = , j = ;
while (true)
{
if (leftData[i] < rightData[j])
{
mergeData.Add(leftData[i]);
if (++i == leftData.Count)
{
mergeData.AddRange(rightData.GetRange(j, rightData.Count - j));
break;
}
}
else
{
mergeData.Add(rightData[j]);
if (++j == rightData.Count)
{
mergeData.AddRange(leftData.GetRange(i, leftData.Count - i));
break;
}
}
}
return mergeData;
} public static List<int> MergeSortOnlyList(List<int> data)
{
data = MergeSortOnlyList(data, , data.Count - ); //不会改变外部引用 参照C#参数传递
return data;
}

过程解析:将数列分为两部分,分别得到两部分数列的有序版本,然后逐个比较,将比较出的小数逐个放进

新的空数列中。当一个数列放完后,将另一个数列剩余数全部放进去。

IList<int>版本

实现如下:

public static IList<int> MergeSort(IList<int> data)
{
data = MergeSort(data, , data.Count - );
return data;
} public static IList<int> MergeSort(IList<int> data, int low, int high)
{
int length = high - low + ;
IList<int> mergeData = NewInstance(data, length);
if (low == high)
{
mergeData[] = data[low];
return mergeData;
}
int mid = (low + high) / ;
IList<int> leftData = MergeSort(data, low, mid);
IList<int> rightData = MergeSort(data, mid + , high);
int i = , j = ;
while (true)
{
if (leftData[i] < rightData[j])
{
mergeData[i + j] = leftData[i++]; //不能使用Add,Array Length不可变
if (i == leftData.Count)
{
int rightLeft = rightData.Count - j;
for (int m = ; m < rightLeft; m++)
{
mergeData[i + j] = rightData[j++];
}
break;
}
}
else
{
mergeData[i + j] = rightData[j++];
if (j == rightData.Count)
{
int leftleft = leftData.Count - i;
for (int n = ; n < leftleft; n++)
{
mergeData[i + j] = leftData[i++];
}
break;
}
}
}
return mergeData; }

过程原理与上个一样,此处就不赘述了。

堆排序

堆排序是根据堆这种数据结构设计的一种算法。堆的特性:父节点的值总是小于(或大于)它的子节点。近似二叉树。

原理:将数列构建为最大堆数列(即父节点总是最大值),将最大值(即根节点)交换到数列末尾。这样要排序的数列数总和减少,

同时根节点不再是最大值,调整最大堆数列。如此重复,最后得到有序数列。 维基入口   有趣的演示

实现准备:如何将数列构造为堆——父节点i的左子节点为2i+1,右子节点为2i+2。节点i的父节点为floor((i-1)/2)。

实现如下(这个实现判断和临时变量使用太多,导致效率低,评论中@小城故事提出了更好的实现):

public static void HeapSort(IList<int> data)
{
BuildMaxHeapify(data);
int j = data.Count;
for (int i = ; i < j; )
{
Swap(data, i, --j);
if (j - < ) //只剩下1个数 j代表余下要排列的数的个数
break;
int k = ;
while (true)
{
if (k > (j - ) / ) break; //即:k > ((j-1)-1)/2 超出最后一个父节点的位置
else
{
int temp = k;
k = ReSortMaxBranch(data, k, * k + , * k + , j - );
if (temp == k) break;
}
}
}
} public static void BuildMaxHeapify(IList<int> data)
{
for (int i = data.Count / - ; i >= ; i--) //(data.Count-1)-1)/2为数列最大父节点索引
{
int temp = i;
temp = ReSortMaxBranch(data, i, * i + , * i + , data.Count - );
if (temp != i)
{
int k = i;
while (k != temp && temp <= data.Count / - )
{
k = temp;
temp = ReSortMaxBranch(data, temp, * temp + , * temp + , data.Count - );
}
}
}
} public static int ReSortMaxBranch(IList<int> data, int maxIndex, int left, int right, int lastIndex)
{
int temp;
if (right > lastIndex) //父节点只有一个子节点
temp = left;
else
{
if (data[left] > data[right])
temp = left;
else temp = right;
} if (data[maxIndex] < data[temp])
Swap(data, maxIndex, temp);
else temp = maxIndex;
return temp;
}

过程解析:BuildMaxHeapify为排序前构建的最大堆数列方法,主要内容为从最后一个父节点开始往前将每个三角组合

(即父节点与它的两个子节点)符合父节点值最大的规则。ReSortMaxBranch为将三角调整为父节点值最大,

并返回该值之前的索引,用来判断是否进行了交换,以及原来的父节点值交换到了什么位置。在HeapSort里首先

构建了最大堆数列,然后将根节点交换到末尾,根节点不是最大值了,在while语句中对最大堆数列进行调整。

插曲:自从看了Martin Fowler大师《重构》第三版,我发现我更不喜欢写注释了。每次都想着尽量让方法的名字更贴切,

即使会造成方法的名字很长很丑。这算不算曲解了大师的意思啊!?上面的代码注释都是写博客的时候现加的(源代码很干净的。汗!)。

希尔排序

希尔排序是插入排序的一种更高效的改进版本。

在前面介绍的插入排序,我们知道1.它对有序数列排序的效率是非常高的 2.要排序的数向前移动是一步步进行的导致插入排序效率低。

希尔排序正是利用第一点,改善第二点,达到更理想的效果。

原理:通过奇妙的步长,插入排序间隔步长的元素,随后逐渐缩短步长至1,实现数列的插入排序。 维基入口

疑问:可以想象到排序间隔步长的数,会逐渐让数列变得有序,提升最后步长为1时标准插入排序的效率。在维基上看到这么

一句话“可能希尔排序最重要的地方在于当用较小步长排序后,以前用的较大步长仍然是有序的”注意用词是‘可能’。我的疑问是

这是个正确的命题吗?如何证明呢?看维基上也是由果推因,说是如果不是这样,就不会排序那么快了。可这我感觉还是太牵强了,

哪位大哥发现相关资料,希望能分享出来,不胜感激。

实现如下:

public static void ShellSortCorrect(IList<int> data)
{
int temp;
for (int gap = data.Count / ; gap > ; gap /= )
{
for (int i = gap; i < data.Count; i++) // i+ = gap 改为了 i++
{
temp = data[i];
for (int j = i - gap; j >= ; j -= gap)
{
if (data[j] > temp)
{
data[j + gap] = data[j];
if (j == )
{
data[j] = temp;
break;
}
}
else
{
data[j + gap] = temp;
break;
}
}
}
}
}

基数排序

基数排序是一种非比较型整数排序。

“非比较型”是什么意思呢?因为它内部使用的是桶排序,而桶排序是非比较型排序。

这里就要说说桶排序了。一个非常有意思的排序。

桶排序

原理:取一定数量(数列中的最大值)的编好序号的桶,将数列每个数放进编号为它的桶里,然后将不是空的桶依次倒出来,

就组成有序数列了。  维基入口

好吧!聪明的人一眼就看出桶排序的破绽了。假设只有两个数1,10000,岂不是要一万个桶!?这确实是个问题啊!我也

没想出解决办法。我起初也以为桶排序就是一个通过牺牲空间来换取时间的排序算法,它不需要比较,所以是非比较型算法。

但看了有趣的演示桶排序后,发现世界之大,你没有解决,不代表别人没解决,睿智的人总是很多。

1,9999的桶排序实现:new Int[2];总共有两个数,得出最大数9999的位数4,取10的4次幂即10000作为分母,

要排序的数(1或9999)作为分子,并乘以数列总数2,即1*2/10000,9999*2/10000得到各自的位置0,1,完成排序。

如果是1,10000进行排序的话,上面的做法就需要稍微加一些处理——发现最大数是10的n次幂,就将它作为分母,并

放在数列末尾就好了。

如果是9999,10000进行排序的话,那就需要二维数组了,两个都在位置1,位置0没数。这个时候就需要在放

入每个位置时采用其它排序(比如插入排序)办法对这个位置的多个数排序了。

为基数排序做个过渡,我这里实现了一个个位数桶排序

涉及到了当重复的数出现的处理。

实现如下:

public static void BucketSortOnlyUnitDigit(IList<int> data)
{
int[] indexCounter = new int[];
for (int i = ; i < data.Count; i++)
{
indexCounter[data[i]]++;
}
int[] indexBegin = new int[];
for (int i = ; i < ; i++)
{
indexBegin[i] = indexBegin[i-]+ indexCounter[i-];
}
IList<int> tempList = NewInstance(data, data.Count);
for (int i = ; i < data.Count; i++)
{
int number = data[i];
tempList[indexBegin[number]++] = data[i];
}
data = tempList;
}

过程解析:indexCounter进行对每个数出现的频率的统计。indexBegin存储每个数的起始索引。

比如 1 1 2,indexCounter统计到0个0,2个1,1个2。indexBegin计算出0,1,2的起始索引分别为

0,0,2。当1个1已取出排序,那索引将+1,变为0,1,2。这样就通过提前给重复的数空出位置,解决了

重复的数出现的问题。当然,你也可以考虑用二维数组来解决重复。

下面继续基数排序。

基数排序原理:将整数按位数切割成不同的数字,然后按每个位数分别比较。

取得最大数的位数,从低位开始,每个位上进行桶排序。

实现如下:

public static IList<int> RadixSort(IList<int> data)
{
int max = data[];
for (int i = ; i < data.Count; i++)
{
if (data[i] > max)
max = data[i];
}
int digit = ;
while (max / != )
{
digit++;
max /= ;
}
for (int i = ; i < digit; i++)
{
int[] indexCounter = new int[];
IList<int> tempList = NewInstance(data, data.Count);
for (int j = ; j < data.Count; j++)
{
int number = (data[j] % Convert.ToInt32(Math.Pow(, i + ))) / Convert.ToInt32(Math.Pow(, i)); //得出i+1位上的数
indexCounter[number]++;
}
int[] indexBegin = new int[];
for (int k = ; k < ; k++)
{
indexBegin[k] = indexBegin[k - ] + indexCounter[k - ];
}
for (int k = ; k < data.Count; k++)
{
int number = (data[k] % Convert.ToInt32(Math.Pow(, i + ))) / Convert.ToInt32(Math.Pow(, i));
tempList[indexBegin[number]++] = data[k];
}
data = tempList;
}
return data;
}

过程解析:得出最大数的位数,从低位开始桶排序。我写的这个实现代码并不简洁,但逻辑更清晰。

后面测试的时候我们就会发现,按理来说这个实现也还行吧! 但并不如想象的那么快!

循环的次数太多?(统计频率n次+9次计算+n次放到新的数组)*位数。

创建的新实例太多?(new int[10]两次+NewInstance is反射判断创建实例+new int[n])*位数

测试比较

添加随机数组,数组有序校验,微软Linq排序

代码如下:

public static int[] RandomSet(int length, int max)
{
int[] result = new int[length];
Random rand = new Random();
for (int i = ; i < result.Length; i++)
{
result[i] = rand.Next(max);
}
return result;
} public static bool IsAscOrdered(IList<int> data)
{
bool flag = true;
for (int i = ; i < data.Count - ; i++)
{
if (data[i] > data[i + ])
flag = false;
}
return flag;
} public static void TestMicrosoft(IList<int> data)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<int> result = data.OrderBy(a => a).ToList();
stopwatch.Stop();
string methodName = "TestMicrosoft";
int length = methodName.Length;
for (int i = ; i < - length; i++)
{
methodName += " ";
}
Console.WriteLine(methodName +
" IsAscOrdered:" + IsAscOrdered(result) + " Time:" + stopwatch.Elapsed.TotalSeconds); }

测试主体如下:

static void Main(string[] args)
{
int[] aa = RandomSet(, );
//int[] aa = OrderedSet(5000);
Console.WriteLine("Array Length:" + aa.Length);
RunTheMethod((Action<IList<int>>)SelectSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)BubbleSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)BubbleSortImprovedWithFlag, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)BubbleCocktailSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)InsertSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)InsertSortImprovedWithBinarySearch, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)QuickSortStrict, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)QuickSortRelax, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)QuickSortRelaxImproved, aa.Clone() as int[]);
RunTheMethod((Func<IList<int>, IList<int>>)MergeSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)ShellSort, aa.Clone() as int[]);
RunTheMethod((Func<IList<int>, IList<int>>)RadixSort, aa.Clone() as int[]);
RunTheMethod((Action<IList<int>>)HeapSort, aa.Clone() as int[]);
TestMicrosoft(aa.Clone() as int[]);
Console.Read();
} public static void RunTheMethod(Func<IList<int>, IList<int>> method, IList<int> data)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
IList<int> result = method(data);
stopwatch.Stop();
string methodName = method.Method.Name;
int length = methodName.Length;
for (int i = ; i < - length; i++)
{
methodName += " ";
}
Console.WriteLine(methodName +
" IsAscOrdered:" + IsAscOrdered(result) + " Time:" + stopwatch.Elapsed.TotalSeconds);
} public static void RunTheMethod(Action<IList<int>> method, IList<int> data)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
method(data);
stopwatch.Stop();
string methodName = method.Method.Name;
int length = methodName.Length;
for (int i = ; i < - length; i++)
{
methodName += " ";
}
Console.WriteLine(methodName +
" IsAscOrdered:" + IsAscOrdered(data) + " Time:" + stopwatch.Elapsed.TotalSeconds);
}

剩余代码折叠在此处

public static void Swap(IList<int> data, int a, int b)
{
int temp = data[a];
data[a] = data[b];
data[b] = temp;
} public static int[] OrderedSet(int length)
{
int[] result = new int[length];
for (int i = ; i < length; i++)
{
result[i] = i;
}
return result;
} public static IList<int> NewInstance(IList<int> data, int length)
{
IList<int> instance;
if (data is Array)
{
instance = new int[length];
}
else
{
instance = new List<int>(length);
for (int n = ; n < length; n++)
{
instance.Add(); // 初始添加
}
}
return instance;
}

以上动图由“图斗罗”提供

8种主要排序算法的C#实现 (二)的更多相关文章

  1. JavaScript版几种常见排序算法

    今天发现一篇文章讲“JavaScript版几种常见排序算法”,看着不错,推荐一下原文:http://www.w3cfuns.com/blog-5456021-5404137.html 算法描述: * ...

  2. php四种基础排序算法的运行时间比较

    /** * php四种基础排序算法的运行时间比较 * @authors Jesse (jesse152@163.com) * @date 2016-08-11 07:12:14 */ //冒泡排序法 ...

  3. 7种基本排序算法的Java实现

    7种基本排序算法的Java实现 转自我的Github 以下为7种基本排序算法的Java实现,以及复杂度和稳定性的相关信息. 以下为代码片段,完整的代码见Sort.java 插入排序 /** * 直接插 ...

  4. PHP四种基本排序算法

    PHP的四种基本排序算法为:冒泡排序.插入排序.选择排序和快速排序. 下面是我整理出来的算法代码: 1. 冒泡排序: 思路:对数组进行多轮冒泡,每一轮对数组中的元素两两比较,调整位置,冒出一个最大的数 ...

  5. 七种经典排序算法及Java实现

    排序算法稳定性表示两个值相同的元素在排序前后是否有位置变化.如果前后位置变化,则排序算法是不稳定的,否则是稳定的.稳定性的定义符合常理,两个值相同的元素无需再次交换位置,交换位置是做了一次无用功. 下 ...

  6. php四种基础排序算法的运行时间比较!

    /** * php四种基础排序算法的运行时间比较 * @authors Jesse (jesse152@163.com) * @date 2016-08-11 07:12:14 */ //冒泡排序法 ...

  7. 基于python的七种经典排序算法

    参考书目:<大话数据结构> 一.排序的基本概念和分类 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作.排序算法,就是如何使得记录按照要求排列的方法. ...

  8. 几种经典排序算法的R语言描述

    1.数据准备 # 测试数组 vector = c(,,,,,,,,,,,,,,) vector ## [] 2.R语言内置排序函数 在R中和排序相关的函数主要有三个:sort(),rank(),ord ...

  9. 8种主要排序算法的C#实现

    作者:胖鸟低飞 出处:http://www.cnblogs.com/fatbird/ 简介 排序算法是我们编程中遇到的最多的算法.目前主流的算法有8种. 平均时间复杂度从高到低依次是: 冒泡排序(o( ...

  10. Java中几种常见排序算法

    日常操作中常见的排序方法有:冒泡排序.快速排序.选择排序.插入排序.希尔排序等. 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来.走访数 ...

随机推荐

  1. Python安装相关的机器学习库以及图像处理库

    安装 sudo apt-get install python-scipy sudo apt-get install python-numpy sudo apt-get install python-m ...

  2. 010杰信-创建购销合同Excel报表系列-3-新增合同货物:这里涉及到子表的新增(合同货物表是购销合同表的子表)

    效果说明: 前面分析过购销合同的Excel报表需要四张表,这篇讲的是合同货物表. 这个合同货物表是购销合同的子表,是一个购销合同有多个合同货物的关系.在合同货物表中有购销合同的主键作为外键.所以这张表 ...

  3. Web项目的WEB-INF目录使用说明以及重定向与转发

    写这篇文章的原因是看到了Tomcat下面的Webapps下面的,就想看一下. 总结一下这篇文章的内容: WEB-INF下面的内容都是只能由服务器级别才能访问,客户端并不能访问.什么是客户端级别?什么是 ...

  4. ThinkPHP整合cropper剪裁图片上传功能

    1.先下载核心文件:https://github.com/fengyuanchen/cropper 2. 3.对于index.html文件 4.对于main.js文件 5.对于crop.php文件 & ...

  5. Thinkphp新增字段无法插入到数据库问题

    Thinkphp框架开发过程中,因需求需要改动数据表,新增了几个字段. 调用 M(‘xxx’)->add($data) 插入值时,新增的字段数据总是插入不进去,每次都是默认的值,于是恍然—-缓存 ...

  6. hdu 4003(树形dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 思路:dp[i][j]表示以i为根选择j个机器人的最小花费,然后就是背包了:dp[u][i]=m ...

  7. HttpServletRequest和HttpServletResponse简介

    http://blog.csdn.net/tong_xinglong/article/details/12972819

  8. CSDN日报20170411 ——《怎样给自己的私活项目标价》

    [程序人生]怎样给自己的私活项目标价 作者:瞬息之间 非常早之前讲过我们"怎么接私活的心得技巧".相信非常多同学听了心里痒痒的.据我认识的(无论是现实生活还是网上接触的)朋友来看. ...

  9. ios 的EditBox点击空白处不隐藏的解决方案

    原因:参数少了前缀CC 解决方案:修改 cocos/platform/ios/CCEAGLView-ios.mm 中的 handleTouchesAfterKeyboardShow -(void) h ...

  10. 理解CSS3 isolation: isolate的表现和作用

    转自:http://www.zhangxinxu.com/wordpress/?p=5155 只要元素可以创建层叠上下文,就可以阻断mix-blend-mode! 于是,不仅仅是isolation:i ...