整理一下常用的排序算法,用c#实现,以备日后再用。Code is cheap.看具体实现吧。
1.冒泡排序
将被排序的记录数组R[1..n]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其向上"飘浮"(冒泡因此得名)。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
Code
using System;
using System.Collections;
using System.Collections.Generic;
namespace BubbleSort
{
public class BubbleSorter
{
/// <summary>
/// 冒泡排序
/// </summary>
/// <param name="numArr"></param>
public void Sort(int[] numArr)
{
int tmpNum;
bool flag = false; //交换标志
for (int i = 1; i < numArr.Length; i++) //最多做numArr.Length-1趟排序
{
flag = false;
for (int j = numArr.Length - 1; j >= i; j--) //对当前无序区自下向上扫描
{
if (numArr[j] < numArr[j - 1]) //当前无序区: 轻的在下面,“冒泡”到上面
{
tmpNum = numArr[j];
numArr[j] = numArr[j - 1];
numArr[j - 1] = tmpNum;
flag = true;
}
}
if (!flag) //如果没有发生交换,终止算法
return;
}
}
public class Program
{
public static void Main()
{
int[] testArr = new int[] { 1, 5, 11, 6, 4, 21, 99, 2, 15, 11, 34, 0, 33, 47 };
BubbleSorter sh = new BubbleSorter();
sh.Sort(testArr);
for (int m = 0; m < testArr.Length; m++)
Console.Write("{0} ", testArr[m]);
Console.Read();
}
}
}
}
冒泡算法小结:
因为每一趟排序都使有序区增加了一个气泡,在经过n-1趟排序之后,有序区中就有n-1个气泡,而无序区中气泡的重量总是大于等于有序区中气泡的重量,所以整个冒泡排序过程至多需要进行n-1趟排序。若在某一趟排序中未发现气泡位置的交换,则说明待排序的无序区中所有气泡均满足轻者在上,重者在下的原则,因此,冒泡排序过程可在此趟排序后终止。为此,在下面给出的算法中,引入一个布尔量flag,在每趟排序开始前,先将其置为false,若排序过程中发生了交换,则将其置为true.各趟排序结束时检查flag,若未曾发生过交换则终止算法,不再进行下一趟排序。(不加flag也可以实现排序,但是会造成不必要的循环和比较)。
2.快速排序 ***
一、算法思想
快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。分治法的基本思想是:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。
二、快速排序的基本思想
设当前待排序的无序区为R[low..high],利用分治法可将快速排序的基本思想描述为:
(1)分解:
在R[low..high]中任选一个记录作为基准(Pivot),以此基准将当前无序区划分为左、右两个较小的子区间R[low..pivotpos-1)和R[pivotpos+1..high],并使左边子区间中所有记录的关键字均小于等于基准记录(不妨记为pivot)的关键字pivot.key,右边的子区间中所有记录的关键字均大于等于pivot.key,而基准记录pivot则位于正确的位置(pivotpos)上,它无须参加后续的排序。
注意:划分的关键是要求出基准记录所在的位置pivotpos。划分的结果可以简单地表示为(
注意 pivot=R[pivotpos] ): R[low..pivotpos-1].keys≤R[pivotpos].key≤R[pivotpos+1..high].keys,
其中low≤pivotpos≤high。(边界条件)
(2)求解:
通过递归调用快速排序对左、右子区间R[low..pivotpos-1]和R[pivotpos+1..high]快速排序。
(3)组合:
因为当"求解"步骤中的两个递归调用结束时,其左、右两个子区间已有序。对快速排序而言,"组合"步骤无须做什么,可看作是空操作。
Code
using System;
namespace QuickSorter
{
public class QuickSort
{
public static void Sort(int[] numArr)
{
Sort(numArr, 0, numArr.Length - 1);
}
private static void Sort(int[] numArr, int left, int right)
{
if (left < right)
{
int middle = numArr[(left + right) / 2];
int i = left - 1;
int j = right + 1;
while (true)
{
while (numArr[++i] < middle) ;
while (numArr[--j] > middle) ;
if (i >= j)
break;
Swap(numArr, i, j);
}
Sort(numArr, left, i - 1);
Sort(numArr, j + 1, right);
}
}
private static void Swap(int[] numArr, int i, int j)
{
int number = numArr[i];
numArr[i] = numArr[j];
numArr[j] = number;
}
}
public class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 20, 41, 27, 14, 16, 1, 8, 55, 9, 35, 22, 14 };
QuickSort.Sort(arr);
Console.WriteLine("Numbers after quicksort:");
foreach (int i in arr)
{
Console.WriteLine(i);
}
Console.Read();
}
}
}
网上还有一种相似的代码,一起贴出来:
Code
using System;
namespace QuickSorter
{
public class QuickSorter
{
/// <summary>
/// 快速排序算法
/// </summary>
/// 快速排序为不稳定排序,时间复杂度O(nlog2n),为同数量级中最快的排序方法
/// <param name="arr">划分的数组</param>
/// <param name="low">数组低端上标</param>
/// <param name="high">数组高端下标</param>
/// <returns></returns>
public static int Partition(int[] arr, int low, int high)
{
//进行一趟快速排序,返回中心轴记录位置
// arr[0] = arr[low];
int pivot = arr[low];//把中心轴置于arr[0]
while (low < high)
{
while (low < high && arr[high] >= pivot)
--high;
//将比中心轴记录小的移到低端
Swap(ref arr[high], ref arr[low]);
while (low < high && arr[low] <= pivot)
++low;
Swap(ref arr[high], ref arr[low]);
//将比中心轴记录大的移到高端
}
arr[low] = pivot; //中心轴移到正确位置
return low; //返回中心轴位置
}
public static void Swap(ref int i, ref int j)
{
int t;
t = i;
i = j;
j = t;
}
public static void Sort(int[] arr, int low, int high)
{
if (low < high - 1)//当 arr[low,high]为空或只一个记录无需排序
{
int pivot = Partition(arr, low, high);
Sort(arr, low, pivot - 1);
Sort(arr, pivot + 1, high);
}
}
}
public class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 20, 41, 27, 14, 16, 1, 8, 55, 9, 35, 22, 14 };
QuickSorter.Sort(arr, 0, arr.Length - 1);
Console.WriteLine("Numbers after quicksort:");
foreach (int i in arr)
{
Console.WriteLine(i);
}
Console.Read();
}
}
}
3.选择排序
Code
using System;
namespace SelectionSorter
{
/// <summary>
/// 选择排序(Selection Sort)的基本思想是:每一趟从待排序的记录中选出关键字最小的记录,顺序放在已排好序的子文件的最后,直到全部记录排序完毕。
/// </summary>
public class SelectionSort
{
public static void Sort(int[] numArray)
{
int min, tmp;
for (int i = 0; i < numArray.Length - 1; i++)
{
min = i;
for (int j = i + 1; j < numArray.Length; j++)
{
if (numArray[j] < numArray[min])
{
min = j;
}
}
tmp = numArray[i];
numArray[i] = numArray[min];
numArray[min] = tmp;
}
}
}
public class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 20, 41, 27, 14, 16, 1, 8, 55, 9, 35, 22, 14 };
SelectionSort.Sort(arr);
Console.WriteLine("Numbers after selectionsort:");
foreach (int i in arr)
{
Console.WriteLine(i);
}
Console.Read();
}
}
}
4.插入排序
Code
using System;
namespace InsertSorter
{
/// <summary>
/// 插入排序(Insertion Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,
/// 在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),
/// 因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位, 为最新元素提供插入空间。
/// </summary>
public class InsertSort
{
public static void Sort(int[] numArr)
{
for (int i = 1; i < numArr.Length; i++) //i从1开始
{
int t = numArr[i]; //标志当前未排序数据
int j = i;
while ((j > 0) && (numArr[j - 1] > t))
{
numArr[j] = numArr[j - 1];
j--;
}
numArr[j] = t; //在已排序序列中插入当前值
}
}
}
public class Program
{
static void Main(string[] args)
{
int[] arr = new int[] { 20, 41, 27, 14, 16, 1, 8, 55, 9, 35, 22, 14 };
InsertSort.Sort(arr);
Console.WriteLine("Numbers after insertsort:");
foreach (int i in arr)
{
Console.WriteLine(i);
}
Console.Read();
}
}
}
虽然在实际的项目中,我们的确很少用到这些或其他更高级的算法,但是”算法是程序的灵魂“。虽然算法确实很难,但是”当你用它们巧妙地解决问题的时候,那种纯粹的喜悦和快乐是任何不曾体验过的人所能感受到的“。很不幸,我还没有体验几次这样的快乐。