Visual C# 诠释常用排序算法
前段时间因为项目需要,做了个用来对数组排序的类,顺便把以前学过的几种排序算法用C#实现一下。用C#的一些机制来诠释了一下算法的是实现。在阅读本之前,需要一些对C#的有些基本的了解,了解方法参数中out ,ref的作用,掌握面向对象的一些基本思想。
1. 插入排序
1.1. 基本思想:
每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
1.2. 排序过程:
【示例】:
[初始关键字]
[49] 38 65 97 76 13 27 49
(38) [38 49] 65 97 76 13 27 49
(65) [38 49 65] 97 76 13 27 49
(97) [38 49 65 97] 76 13 27 49
(76) [38 49 65 76 97] 13 27 49
(13) [13 38 49 65 76 97] 27 49
(27) [13 27 38 49 65 76 97] 49
(49) [13 27 38 49 49 65 76 97]
1.3. 程序实现
2. 选择排序
2.1. 基本思想:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
2.2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最后排序结果 13 27 38 49 49 76 76 97
2.3. 程序实现
3. 冒泡排序
3.1. 基本思想:
两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
3.2. 排序过程:
设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止
【示例】:
49 13 13 13 13 13 13 13
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97
3.3. 程序实现
程序支持顺序和倒序排列。
4. 快速排序
4.1. 基本思想:
在当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
4.2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一次交换后 [27 38 65 97 76 13 49 49]
第二次交换后 [27 38 49 97 76 13 65 49]
第三次交换后 [27 38 13 97 76 49 65 49]
第四次交换后 [27 38 13 49 76 97 65 49]
[27 38 13 49 76 97 65 49]
(一次划分过程)
初始关键字 [49 38 65 97 76 13 27 49]
一趟排序之后 [27 38 13] 49 [76 97 65 49]
二趟排序之后 [13] 27 [38] 49 [49 65]76 [97]
三趟排序之后 13 27 38 49 49 [65]76 97
最后的排序结果 13 27 38 49 49 65 76 97
各趟排序之后的状态
4.3. 程序实现
5. 堆排序
5.1. 基本思想:
堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
5.2. 堆的定义:
N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])。
堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等于其孩子的关键字,则称之为大根堆。
5.3. 排序过程:
堆排序正是利用小根堆(或大根堆)来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐步向前扩大到整个记录区。
【示例】:对关键字序列42,13,91,23,24,16,05,88建堆。
5.4. 程序实现
总结:
人常说算法是程序的灵魂,在作项目的过程中时常注意且不可灵魂出窍。时常去回顾一下以前的数据重要性就如同基督徒每周要做礼拜一样。不能因为有了C# 和Java这种平台之后,就忽略了基础的重要性。
1. 插入排序
1.1. 基本思想:
每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
1.2. 排序过程:
【示例】:
[初始关键字]
[49] 38 65 97 76 13 27 49
(38) [38 49] 65 97 76 13 27 49
(65) [38 49 65] 97 76 13 27 49
(97) [38 49 65 97] 76 13 27 49
(76) [38 49 65 76 97] 13 27 49
(13) [13 38 49 65 76 97] 27 49
(27) [13 27 38 49 65 76 97] 49
(49) [13 27 38 49 49 65 76 97]
1.3. 程序实现
<summary>
/// 插入排序算法
/// </summary>
/// <param name="dblArray"></param>
static void InsertSort(ref double[] dblArray)
{
for(int i = 1 ; i < dblArray.Length ; i++)
{
int frontArrayIndex = i-1 ;
int CurrentChangeIndex = i ;
while(frontArrayIndex>=0)
{
if(dblArray[CurrentChangeIndex] < dblArray[frontArrayIndex])
{
ChangeValue(ref dblArray[CurrentChangeIndex],ref dblArray[frontArrayIndex]);
CurrentChangeIndex = frontArrayIndex ;
}
frontArrayIndex--;
}
}
}
/// <summary>
/// 在内存中交换两个数字的值
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
static void ChangeValue (ref double A ,ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// 插入排序算法
/// </summary>
/// <param name="dblArray"></param>
static void InsertSort(ref double[] dblArray)
{
for(int i = 1 ; i < dblArray.Length ; i++)
{
int frontArrayIndex = i-1 ;
int CurrentChangeIndex = i ;
while(frontArrayIndex>=0)
{
if(dblArray[CurrentChangeIndex] < dblArray[frontArrayIndex])
{
ChangeValue(ref dblArray[CurrentChangeIndex],ref dblArray[frontArrayIndex]);
CurrentChangeIndex = frontArrayIndex ;
}
frontArrayIndex--;
}
}
}
/// <summary>
/// 在内存中交换两个数字的值
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
static void ChangeValue (ref double A ,ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
2. 选择排序
2.1. 基本思想:
每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
2.2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一趟排序后 13 [38 65 97 76 49 27 49]
第二趟排序后 13 27 [65 97 76 49 38 49]
第三趟排序后 13 27 38 [97 76 49 65 49]
第四趟排序后 13 27 38 49 [49 97 65 76]
第五趟排序后 13 27 38 49 49 [97 97 76]
第六趟排序后 13 27 38 49 49 76 [76 97]
第七趟排序后 13 27 38 49 49 76 76 [ 97]
最后排序结果 13 27 38 49 49 76 76 97
2.3. 程序实现
/// <summary>
/// 选择排序
/// </summary>
/// <param name="dblArray"></param>
private static void SelectSort(ref double[] dblArray)
{
for(int i =0 ; i< dblArray.Length; i++)
{
double MinValue = dblArray[i] ;
int MinValueIndex = i ;
for(int j = i; j< dblArray.Length; j++)
{
if(MinValue > dblArray[j] )
{
MinValue = dblArray[j] ;
MinValueIndex = j ;
}
}
ExChangeValue(ref dblArray[i], ref dblArray[MinValueIndex]);
}
}
/// <summary>
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// 选择排序
/// </summary>
/// <param name="dblArray"></param>
private static void SelectSort(ref double[] dblArray)
{
for(int i =0 ; i< dblArray.Length; i++)
{
double MinValue = dblArray[i] ;
int MinValueIndex = i ;
for(int j = i; j< dblArray.Length; j++)
{
if(MinValue > dblArray[j] )
{
MinValue = dblArray[j] ;
MinValueIndex = j ;
}
}
ExChangeValue(ref dblArray[i], ref dblArray[MinValueIndex]);
}
}
/// <summary>
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
3. 冒泡排序
3.1. 基本思想:
两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
3.2. 排序过程:
设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止
【示例】:
49 13 13 13 13 13 13 13
38 49 27 27 27 27 27 27
65 38 49 38 38 38 38 38
97 65 38 49 49 49 49 49
76 97 65 49 49 49 49 49
13 76 97 65 65 65 65 65
27 27 76 97 76 76 76 76
49 49 49 76 97 97 97 97
3.3. 程序实现
程序支持顺序和倒序排列。
/// <summary>
/// 冒泡算法
/// </summary>
/// <param name="abarray"></param>
/// <param name="IsAscending">是否顺序排序</param>
/// <returns></returns>
private static double[] BubbleArithmetic(double[] abarray ,bool IsAscending)
{
if(abarray.Length > 0 )
{
for(int i = abarray.Length-1 ;i >=0 ;i--)
{
for(int j = i-1 ; j>=0 ; j--)
{
if(CheckAccordCondition(abarray[i],abarray[j],IsAscending))
{
ExChangeValue(ref abarray[i],ref abarray[j]);
}
}
}
}
return abarray;
}
/// <summary>
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// <summary>
/// 是否符合条件
/// </summary>
/// <returns></returns>
private static bool CheckAccordCondition(double data1 ,double data2, bool IsAscending)
{
if(data1 > data2)
{
return IsAscending == true ? true :false;
}
else
{
return IsAscending == true ? false :true ;
}
}
/// 冒泡算法
/// </summary>
/// <param name="abarray"></param>
/// <param name="IsAscending">是否顺序排序</param>
/// <returns></returns>
private static double[] BubbleArithmetic(double[] abarray ,bool IsAscending)
{
if(abarray.Length > 0 )
{
for(int i = abarray.Length-1 ;i >=0 ;i--)
{
for(int j = i-1 ; j>=0 ; j--)
{
if(CheckAccordCondition(abarray[i],abarray[j],IsAscending))
{
ExChangeValue(ref abarray[i],ref abarray[j]);
}
}
}
}
return abarray;
}
/// <summary>
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// <summary>
/// 是否符合条件
/// </summary>
/// <returns></returns>
private static bool CheckAccordCondition(double data1 ,double data2, bool IsAscending)
{
if(data1 > data2)
{
return IsAscending == true ? true :false;
}
else
{
return IsAscending == true ? false :true ;
}
}
4. 快速排序
4.1. 基本思想:
在当前无序区R[1..H]中任取一个数据元素作为比较的"基准"(不妨记为X),用此基准将当前无序区划分为左右两个较小的无序区:R[1..I-1]和R[I+1..H],且左边的无序子区中数据元素均小于等于基准元素,右边的无序子区中数据元素均大于等于基准元素,而基准X则位于最终排序的位置上,即R[1..I-1]≤X.Key≤R[I+1..H](1≤I≤H),当R[1..I-1]和R[I+1..H]均非空时,分别对它们进行上述的划分过程,直至所有无序子区中的数据元素均已排序为止。
4.2. 排序过程:
【示例】:
初始关键字 [49 38 65 97 76 13 27 49]
第一次交换后 [27 38 65 97 76 13 49 49]
第二次交换后 [27 38 49 97 76 13 65 49]
第三次交换后 [27 38 13 97 76 49 65 49]
第四次交换后 [27 38 13 49 76 97 65 49]
[27 38 13 49 76 97 65 49]
(一次划分过程)
初始关键字 [49 38 65 97 76 13 27 49]
一趟排序之后 [27 38 13] 49 [76 97 65 49]
二趟排序之后 [13] 27 [38] 49 [49 65]76 [97]
三趟排序之后 13 27 38 49 49 [65]76 97
最后的排序结果 13 27 38 49 49 65 76 97
各趟排序之后的状态
4.3. 程序实现
/// <summary>
/// 快速排序法
/// </summary>
/// <param name="dbArray"></param>
/// <param name="StartIndex"></param>
/// <param name="EndIndex"></param>
private static void QuickSort( ref double[] dbArray ,int StartIndex ,int EndIndex)
{
//基数
int CurrentIndex = StartIndex ;
//顺序查找
bool IsOrderSearched = true ;
//反序查找
bool IsDisOrderSearched = true ;
while(IsOrderSearched || IsDisOrderSearched)
{
IsDisOrderSearched = false ;
IsOrderSearched = false ;
for(int i =EndIndex ; i>CurrentIndex ;i--)
{
if(dbArray[i] < dbArray[CurrentIndex])
{
ExChangeValue(ref dbArray[i] ,ref dbArray[CurrentIndex]);
CurrentIndex = i ;
IsDisOrderSearched = true ;
break ;
}
}
for(int i = StartIndex ; i < CurrentIndex ; i++)
{
if(dbArray[i] > dbArray[CurrentIndex])
{
ExChangeValue(ref dbArray[i] ,ref dbArray[CurrentIndex]);
CurrentIndex = i ;
IsOrderSearched = true ;
break ;
}
}
}
if( EndIndex - StartIndex > 0 )
{
if(CurrentIndex != StartIndex )
{
QuickSort(ref dbArray ,StartIndex,CurrentIndex -1);
}
if(CurrentIndex != EndIndex)
{
QuickSort(ref dbArray ,CurrentIndex+1,EndIndex);
}
}
}
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// 快速排序法
/// </summary>
/// <param name="dbArray"></param>
/// <param name="StartIndex"></param>
/// <param name="EndIndex"></param>
private static void QuickSort( ref double[] dbArray ,int StartIndex ,int EndIndex)
{
//基数
int CurrentIndex = StartIndex ;
//顺序查找
bool IsOrderSearched = true ;
//反序查找
bool IsDisOrderSearched = true ;
while(IsOrderSearched || IsDisOrderSearched)
{
IsDisOrderSearched = false ;
IsOrderSearched = false ;
for(int i =EndIndex ; i>CurrentIndex ;i--)
{
if(dbArray[i] < dbArray[CurrentIndex])
{
ExChangeValue(ref dbArray[i] ,ref dbArray[CurrentIndex]);
CurrentIndex = i ;
IsDisOrderSearched = true ;
break ;
}
}
for(int i = StartIndex ; i < CurrentIndex ; i++)
{
if(dbArray[i] > dbArray[CurrentIndex])
{
ExChangeValue(ref dbArray[i] ,ref dbArray[CurrentIndex]);
CurrentIndex = i ;
IsOrderSearched = true ;
break ;
}
}
}
if( EndIndex - StartIndex > 0 )
{
if(CurrentIndex != StartIndex )
{
QuickSort(ref dbArray ,StartIndex,CurrentIndex -1);
}
if(CurrentIndex != EndIndex)
{
QuickSort(ref dbArray ,CurrentIndex+1,EndIndex);
}
}
}
/// 交换数据
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExChangeValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
5. 堆排序
5.1. 基本思想:
堆排序是一树形选择排序,在排序过程中,将R[1..N]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。
5.2. 堆的定义:
N个元素的序列K1,K2,K3,...,Kn.称为堆,当且仅当该序列满足特性:Ki≤K2i Ki ≤K2i+1(1≤ I≤ [N/2])。
堆实质上是满足如下性质的完全二叉树:树中任一非叶子结点的关键字均大于等于其孩子结点的关键字。例如序列10,15,56,25,30,70就是一个堆,它对应的完全二叉树如上图所示。这种堆中根结点(称为堆顶)的关键字最小,我们把它称为小根堆。反之,若完全二叉树中任一非叶子结点的关键字均大于等于其孩子的关键字,则称之为大根堆。
5.3. 排序过程:
堆排序正是利用小根堆(或大根堆)来选取当前无序区中关键字小(或最大)的记录实现排序的。我们不妨利用大根堆来排序。每一趟排序的基本操作是:将当前无序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和无序区中的最后一个记录交换。这样,正好和直接选择排序相反,有序区是在原记录区的尾部形成并逐步向前扩大到整个记录区。
【示例】:对关键字序列42,13,91,23,24,16,05,88建堆。
|
5.4. 程序实现
/// <summary>
/// 小根堆排序
/// </summary>
/// <param name="dblArray"></param>
/// <param name="StartIndex"></param>
/// <returns></returns>
private static void HeapSort(ref double[] dblArray )
{
for(int i = dblArray.Length -1 ; i >= 0; i--)
{
if(2*i+1<dblArray.Length)
{
int MinChildrenIndex = 2*i+1 ;
//比较左子树和右子树,记录最小值的Index
if(2*i+2 < dblArray.Length )
{
if(dblArray[2*i+1]>dblArray[2*i+2])
MinChildrenIndex = 2*i+2;
}
if(dblArray[i] > dblArray[MinChildrenIndex])
{
ExchageValue(ref dblArray[i],ref dblArray[MinChildrenIndex]);
NodeSort(ref dblArray ,MinChildrenIndex);
}
}
}
}
/// <summary>
/// 节点排序
/// </summary>
/// <param name="dblArray"></param>
/// <param name="StartIndex"></param>
private static void NodeSort(ref double[] dblArray,int StartIndex)
{
while(2*StartIndex+1 < dblArray.Length)
{
int MinChildrenIndex = 2*StartIndex+1 ;
if(2*StartIndex+2 < dblArray.Length )
{
if(dblArray[2*StartIndex+1]>dblArray[2*StartIndex+2])
{
MinChildrenIndex = 2*StartIndex+2;
}
}
if(dblArray[StartIndex] > dblArray[MinChildrenIndex])
{
ExchageValue(ref dblArray[StartIndex],ref dblArray[MinChildrenIndex]);
StartIndex = MinChildrenIndex ;
}
}
}
/// <summary>
/// 交换值
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExchageValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
/// 小根堆排序
/// </summary>
/// <param name="dblArray"></param>
/// <param name="StartIndex"></param>
/// <returns></returns>
private static void HeapSort(ref double[] dblArray )
{
for(int i = dblArray.Length -1 ; i >= 0; i--)
{
if(2*i+1<dblArray.Length)
{
int MinChildrenIndex = 2*i+1 ;
//比较左子树和右子树,记录最小值的Index
if(2*i+2 < dblArray.Length )
{
if(dblArray[2*i+1]>dblArray[2*i+2])
MinChildrenIndex = 2*i+2;
}
if(dblArray[i] > dblArray[MinChildrenIndex])
{
ExchageValue(ref dblArray[i],ref dblArray[MinChildrenIndex]);
NodeSort(ref dblArray ,MinChildrenIndex);
}
}
}
}
/// <summary>
/// 节点排序
/// </summary>
/// <param name="dblArray"></param>
/// <param name="StartIndex"></param>
private static void NodeSort(ref double[] dblArray,int StartIndex)
{
while(2*StartIndex+1 < dblArray.Length)
{
int MinChildrenIndex = 2*StartIndex+1 ;
if(2*StartIndex+2 < dblArray.Length )
{
if(dblArray[2*StartIndex+1]>dblArray[2*StartIndex+2])
{
MinChildrenIndex = 2*StartIndex+2;
}
}
if(dblArray[StartIndex] > dblArray[MinChildrenIndex])
{
ExchageValue(ref dblArray[StartIndex],ref dblArray[MinChildrenIndex]);
StartIndex = MinChildrenIndex ;
}
}
}
/// <summary>
/// 交换值
/// </summary>
/// <param name="A"></param>
/// <param name="B"></param>
private static void ExchageValue(ref double A , ref double B)
{
double Temp = A ;
A = B ;
B = Temp ;
}
总结:
人常说算法是程序的灵魂,在作项目的过程中时常注意且不可灵魂出窍。时常去回顾一下以前的数据重要性就如同基督徒每周要做礼拜一样。不能因为有了C# 和Java这种平台之后,就忽略了基础的重要性。