65.9K
CodeProject 正在变化。 阅读更多。
Home

C#.NET 中的排序算法代码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (45投票s)

2010年5月5日

CPOL

7分钟阅读

viewsIcon

278872

downloadIcon

8309

本文包含快速排序、归并排序、冒泡排序、堆排序的代码

sorting Algos

引言

我将一些排序算法放在一个文件中,初学者可以在其中找到不同排序技术的工作原理。

背景

当我需要实现这些排序算法时,我发现很难在一个地方找到所有技术。这就是我发布这个小应用程序的原因,它将帮助学生和初学者。

Using the Code

只需解压 zip 文件并在 Visual Studio 中打开它。运行项目。未排序的列表是预定义的,您可以自行更改它,也可以使其更具交互性。

快速排序

快速排序采用分而治之的工作方式。它将未排序的列表分成子列表,然后对各个列表进行排序。

快速排序的步骤是

  1. 从列表中选择一个元素,称为“枢轴”(Pivot)。
  2. 重新排列列表,使所有值小于枢轴的元素都位于枢轴之前,而所有值大于枢轴的元素都位于枢轴之后(相等的值可以放在任意一边)。分区后,枢轴位于其最终位置。这称为分区操作。
  3. 递归地对较小元素的子列表和较大元素的子列表进行排序。

Quicksort.png

在代码中,我们有一个类型为“整数列表”的 unsorted 列表,它将使用不同的算法进行排序。

我们通过启动 C# 新项目,选择 WindowsForm 类型并在您的 PC 硬盘上的某个位置创建它来开始编码。

或者,如果您正在使用上面提供的项目文件,只需下载它并将其解压到硬盘上的某个位置。然后打开 Visual Studio,打开项目,浏览到您解压下载文件的位置并打开项目文件。

查看源代码。在这里您将找到默认的 using 指令。在这个项目中,我没有包含任何其他命名空间。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

我们通过启动名为 quicksort 的新命名空间来开始编写代码。

namespace quicksort
{
public partial class Form1 : Form{public Form1()
{
InitializeComponent();
}
}

在这里,我们将定义未排序的列表,它将用于我们所有的排序算法。

List<int> unsorted = new List<int> { 9, 8, 7, 6 };

在我们的 button1_Click 事件中,我们调用 quicksort 函数,该函数然后将排序后的列表返回到 result 列表中。然后 showsort 函数将显示排序后的列表。

private void button1_Click(object sender, EventArgs e)
{
List<int> result = new List<int>(quicksort(unsorted));
showsort(result);
}
List<int> unsorted = new List<int> { 9, 8, 7, 6 };

quicksort 函数通过从未排序列表中选择一个随机值开始工作,此值称为枢轴值。然后我们从未排序列表中删除枢轴值。现在,未排序列表中的每个元素 x 都与此 pivot 进行比较。我们还有另外两个列表 lessgreater。如果任何元素小于枢轴,则将其放入 less 列表中;如果大于枢轴值,则将其放入 greater 列表中。然后我们调用包含三个参数的 concat 函数。

  1. 列表名称 less
  2. 变量 pivot
  3. 列表名称 greater
public List<int> quicksort( List<int> a)
{
Random r = new Random();
List<int> less = new List<int>();
List<int> greater = new List<int>();
if (a.Count <= 1)
return a;
int pos =r.Next(a.Count);

int pivot = a[pos];
a.RemoveAt(pos);
foreach (int x in a)
{
if (x <= pivot)
{
less.Add(x);
}
else
{
greater.Add(x);
}
}
return concat(quicksort(less), pivot, quicksort(greater));
}

这里的 concat 函数扮演着非常重要的角色,quicksort 算法通过 concat 函数使用递归方法。

这里 concat 函数接收三个参数,两个列表和 pivot 值。我们在这里定义了一个新的整数列表,名为 sorted。它将 less、pivotgreater 全部连接到 sorted 列表中,然后将此 sorted 列表返回给 quicksort 方法。quicksort 方法接收此 sorted 列表,然后将 sorted 列表返回给将 unsorted 列表发送到 quicksort 方法的函数。

concat 的列表是

public List<int> concat(List<int> less, int pivot, List<int> greater)
{
List<int> sorted = new List<int>(less);
sorted.Add(pivot);
foreach (int i in greater)
{

sorted.Add(i);
}

return sorted; 
}

归并排序

归并排序是分而治之范式的一个例子。它是一种基于比较的排序算法。它接受一个列表,并将该列表分成两个长度几乎相等的列表。然后它通过递归地应用归并排序来对列表进行排序,这会将分好的列表分成两个子列表,并对它们也应用归并排序。

归并排序的工作方式如下:

  1. 如果列表长度为 0 或 1,则它已经排序。否则
  2. 将未排序的列表分成大小约为一半的两个子列表。
  3. 通过重新应用归并排序递归地对每个子列表进行排序。
  4. 将两个子列表合并回一个已排序的列表。
Merge_sort_algorithm_diagram.png

在上图中,我们有一个列表,它展示了归并排序的工作原理。

在 mergesort 函数中,我们向项目中的 mergesort 函数提供名为 m 的整数列表。我们在函数中定义了另外两个列表,名为 rightleft。结果列表将包含已排序的列表,该列表将由该函数返回。

我们首先查看 m 列表的长度。如果 m 的长度等于或小于 1,则表示列表已排序,我们将此列表返回给调用函数。如果不是,我们将通过将给定 m 列表的长度除以 2 来获得 middle。例如,如果长度为 5 个元素,则 m 将具有 2 的值。同样,如果 m 列表的元素数量为 4,则 middle 也将具有 2 的值。实际上,长度/2 的整数部分将赋给 middle。5/2 = 2.5,因此 2 将分配给 middle

我们将列表从这个 middle 分成两个子列表。将位于 m 中点左侧的项分配给 left,并将位于 m 中点右侧的项分配给 right

left = mergesort(left);

right = mergesort(right);

此时,我们通过使用递归,将 leftright 列表传递给 mergesort 方法,并将排序后的列表重新获取到这些列表中。

一旦我们在 leftright 中收到了排序后的列表,我们就会比较这些列表,以确定哪个列表应该位于 result 的左侧,哪个列表应该位于 result 的右侧。如果排序后的列表已经有序,我们就调用 append 函数。

if (left[left.Count - 1] <= right[0])

return append(left, right);

mergesort 的列表是

public List<int> mergesort(List<int> m)
{
List<int> result = new List<int>();
List<int> left=new List<int>();
List<int> right = new List<int>();
if (m.Count <= 1)
return m;

int middle = m.Count / 2;
for(int i=0;i<middle; i ++)
left.Add(m[i]);
for (int i = middle; i < m.Count; i++)
right.Add(m[i]);
left = mergesort(left);
right = mergesort(right);
if (left[left.Count - 1] <= right[0])
return append(left, right);
result = merge(left, right);
return result;
}

append 函数除了按照给定的顺序连接参数中给定的列表外,没有其他作用。

public List<int> append(List<int> a, List<int> b)
{
List<int> result = new List<int>(a);
foreach (int x in b)
result.Add(x);
return result;
}

如果细分的列表没有顺序,那么我们在这里调用 merge 函数。Merge 函数接受两个列表作为参数,在代码中是列表 a 和列表 b。定义了另一个列表 s,它将保存排序后的列表。它比较两个列表的第一个元素。我们将最短的元素添加s 列表中,并从列表中删除该元素。

s.Add(a[0]);
a.RemoveAt(0);

当比较结束后,我们将把列表中剩余的元素(其中元素更多)放入 s 列表中,并将此列表返回给 mergesort 方法。

merge 方法的代码

public List<int> merge(List<int> a,List<int> b)
{
List<int> s = new List<int>();
while (a.Count > 0 && b.Count > 0)
{

if (a[0] < b[0])

{ 
s.Add(a[0]);
a.RemoveAt(0);
}
else
{
s.Add(b[0]);
b.RemoveAt(0);
}
while (a.Count >0)

{s.Add(a[0]);

a.RemoveAt(0);
}

堆排序

堆排序属于选择排序算法范式,是一种基于比较的算法。

Heapsort 以递归方式工作。它根据给定数据构建堆,然后对这些堆进行排序。

Binarytree.png

在堆排序的代码中,我们有一个 heapsort 方法,它接收一个数组 numbers 和一个变量 array_size,该变量包含该数组的长度。

int [] heapSort(int [] numbers, int array_size)
{
int i, temp;
for (i = (array_size / 2)-1; i >= 0; i--)
siftDown(numbers, i, array_size);
for (i = array_size-1; i >= 1; i--)
{
temp = numbers[0];
numbers[0] = numbers[i];
numbers[i] = temp;
siftDown(numbers, 0, i-1);
}
return numbers;
}

有一个函数 shifDown 是这个 heapsort 的核心。这个函数创建堆并对它们进行排序。

shiftDown 确保堆的根包含比其前身大的元素。如果是,则正常,否则交换元素以使其排序,然后将结果发送给 heapsort 函数。

void siftDown(int [] numbers, int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 <= bottom) && (done==0 ))
{
if (root*2 == bottom)
maxChild = root * 2;
else if (numbers[root * 2] > numbers[root * 2 + 1])
maxChild = root * 2;
else
maxChild = root * 2 + 1;
if (numbers[root] < numbers[maxChild])
{temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;}
else
done = 1;
}}

冒泡排序

冒泡排序是初学者非常常用的技术。它易于实现和理解。在冒泡排序中,未排序列表中的每个元素都与下一个元素进行比较,如果第一个元素的值大于第二个元素的值,则它们交换位置。

重复此操作,直到每个元素都与整个列表进行比较。

结果可能具有升序或降序排列的元素。

冒泡排序包含若干趟。趟数取决于列表中的元素数量。元素越多意味着趟数越多。

冒泡排序的代码非常容易编写和理解。它由两个循环组成——一个主循环和一个嵌套循环。主循环描述了趟数,嵌套循环定义了比较次数。列表如下:

public List<int> bubblesort(List<int> a)
{
int temp;
// foreach(int i in a)
for(int i=1; i<=a.Count; i++)
for(int j=0; j<a.Count-i; j++)
if (a[j] > a[j + 1])
{
temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
return a;
}

冒泡排序易于实现,但它是一种缓慢的排序算法。它在每种情况下都会遍历未排序列表中的每个元素。这就是为什么它的最坏情况和平均情况复杂度为0(n2)。

关注点

来源:本文中使用的图片均取自 en.wikipedia.com。

希望这能对初学者和信息寻求者有所帮助。您可以通过 jibran82@hotmail.com 与我联系。

© . All rights reserved.