基础排序算法算法
——.NET数据结构与算法系列之二编程
追忆,2013年11月13日数组
前言数据结构
在计算机中实现存储数据最广泛的两种操做就是排序和查找。这是从计算机产业初始就已经确认的了。这意味着排序和查找也是计算机科学领域最值得研究的两种操做。本书提到的许dom
多数据结构的主要设计目的就是为了使排序和/或查找更加简单,同时也是为了数据在结构内的存储更加有效。性能
本章会介绍有关数据排序和查找的基础算法。这些算法仅依赖数组做为数据结构,并且所采用的“高级”编程技术只是递归。本章还介绍了用来非正式分析不一样算法之间速度与效率的方法,此方法贯穿全书。学习
1.排序算法测试
人们在平常生活中所接触到的绝大多数数据都是通过排序的。好比,按照字母顺序查询字典中的定义。或者按照名字的字母顺序在电话本中查询电话号码。再或者邮局会按照下列几个大数据
步骤对邮件进行排序分发:即首先按照邮政编码,而后再按照街道名称,最后还要按照姓名。排序在数据处理中是十分基础的过程,于是值得认真学习研究。正如先前提到的那样,这里对不一样排序算法的操做有很是少许的分析研究。尽管已经对一些很是古老的算法作了改进,可是仍然应该先学习几种简单的排序算法。这些简单算法就是插入排序算法、冒泡排序算法以及选择排序算法。这些算法的每一种都很容易理解和实现。对于任意状况而言这些算法不是最好的全面算法,可是对于少许数据集合或者其余特殊状况而言,它们是可用的最好算法。this
1.1数组类测试环境
为了检验这些算法,首先须要构造一个能够实现并测试算法的测试环境。这里将构造一个类来封装数组处理的一些常规操做,即元素插入操做,元素存取访问操做,以及显示数组内容的操组。下面就是程序的代码:
在保留CArray 类以便开始检测排序和查找算法以前,仍是先来讨论一下如何在CArray 类对象内实际存储数据的问题。为了更有效地说明不一样排序算法是如何运行的,数组内数据需
要随机放置。最好的实现方法就是使用随机数生成器来给数组的每一个元素进行赋值。在C#中用Random 类能够产生随机数。这种类型的对象能够产生随机数。为了实例化Random 对象,须要给这个类的构造器传递一个种子。这里把这个种子看做是随机数生成器所能产生的随机数范围的上界。
下面另外看一个用CArray 类来存储数的程序,并且采用了随机数生成器来选择存储到数组内的数据:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chapter2
{
class Program
{
static void Main(string[] args)
{
CArray nums = new CArray(10);
Random rnd = new Random(100);
for (int i = 0; i <10; i++)
{
nums.Insert(rnd.Next(0,100));
}
nums.DisplayElements();
Console.ReadKey();
}
}
}
1. 2冒泡排序
首先要讨论的排序算法就是冒泡排序。冒泡排序是可用的最慢排序算法之一,可是它也是最容易理解和实现的排序算法之一,因此这里把它做为最早介绍的排序算法。
这种排序算法的得名是因为数值“像气泡同样”从序列的一端浮动到另外一端。假设如今要把一列数按升序方式进行排序,即较大数值浮动到列的右侧,而较小数值则浮动到列的左侧。
这种效果能够经过下列操做来实现:屡次遍历整个列,而且比较相邻的数值,若是左侧的数值大于右侧数值就进行交换。
//冒泡排序
public void BubbleSort()
{
int temp;
int num = 0;
for (int outer = upper; outer >= 1; outer--)
{
num++;
for (int inner = 0; inner <= outer - 1; inner++)
{
if (arr[inner] > arr[inner + 1])
{
temp = arr[inner];
arr[inner] = arr[inner + 1];
arr[inner + 1] = temp;
}
}
Console.WriteLine(string.Format("\n第{0}次排序", num));
this.DisplayElements();
}
}
这段代码有几个地方须要注意。首先,交换数组元素的代码是写在主程序中的一行,而没有用子程序。若是屡次调用交换子程序,就可能会下降排序的速度。既然交换代码只有短短三
行的长度,因此不把代码放在子程序内也不会影响代码的清晰度。更加须要注意的是程序中最外层的循环是从数组的末尾处开始,而且向数组的开始处移动。若是回顾上图过程就会知道,数组内最大值就在数组末尾的适当位置上。这意味着数组的索引比外层循环的值更大,并且它们已经在恰当的位置上了,于是算法不须要再访问这些数值了。内层循环从数组的第一个元素开始,而且在几乎达到数组最后位置的时候结束。内层循环会对用inner 和inner+1 标识的两个相邻位置的数值进行比较,而且在必要时交换它们的数值。
1.3选择排序
这种排序是从数组的起始处开始,把第一个元素与数组中其余元素进行比较。而后,将最小的元素放置在第0 个位置上,接着再从第1 个位置开始再次进行排序操做。这种操做会一直到除最后一个元素外的每个元素都做为新循环的起始点操做事后才终止。
在选择排序算法中使用了两层循环。外层循环从数组的第一个元素移动到数组最后一个元素以前的元素,而内层循环则从数组的第二个元素移动到数组的最后一个元素,而且查找比当前外层循环所指元素更小的数值。在内循环遍历一遍以后,就会把数组内最小值赋值到数组中合适的位置上。
实现SelectionSort 算法的代码以下所示:
//选择排序
public void SelectionSort()
{
int min, temp;
for (int outer = 0; outer <= upper; outer++)
{
min = outer;
for (int inner = outer + 1; inner <= upper; inner++)
{
if (arr[min] > arr[inner] )
min = inner;
}
temp = arr[outer];
arr[outer] = arr[min];
arr[min] = temp;
Console.WriteLine(string.Format("\n第{0}次排序", outer+1));
this.DisplayElements();
}
}
1.4插入排序
插入排序算法相似于人们一般按照数字顺序或者字母顺序进行排序的方法。假如我要求全班同窗上交填有本人姓名、学号以及简短自我介绍的索引卡片。而学生们交回来的卡片是随机排列的。若是要把卡片按照字母排序排列,就能够构建出一张座次表了。因此,我把这些卡片带回了办公室,而且清理出了办公桌。紧接着我拿出了第一张卡片。卡片上的名字是Smith。我把它放在办公桌最左侧的位置上,而后又拿出了第二张卡片。这张是Brown。因而,我把Smith 的卡片移动到右侧,而且把Brown 的卡片放到Smith 原来的位置上。下一张卡片是Williams。不须要移动任何其余的卡片就能够把它放在最右侧的位置上。接下来的卡片是Acklin。它须要放置在队列的开始处,因此其余全部的卡片都必须向右移动一个位置以便腾出空间放Acklin。这就是插入排序算法的工做原理。
插入排序的代码以下所示,跟着的是对此算法工做原理的解释说明:
//插入排序
public void InsertionSort()
{
int inner, temp;
for (int outer = 1; outer <= upper; outer++)
{
temp = arr[outer];
inner = outer;
while (inner > 0 && arr[inner - 1] >= temp)
{
arr[inner] = arr[inner-1];
inner-=1;
}
arr[inner] = temp;
Console.WriteLine(string.Format("\n第{0}次排序", outer));
this.DisplayElements();
}
}
插入排序算法有两层循环。外层循环会逐个遍历数组元素,而内层循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。若是外层循环选择的元素小于内层循环选择的元素,那么数组元素都向右移以便为内层循环元素留出位置,这就像前面例子描述的那样。如今就来看看选择排序是如何处理前面实例中用来排序的数据集合的。下面是程序的输出结果:
这个输出清楚地代表插入排序不是经过交换来处理的,而是经过把较大的数组元素向右移动来为数组左侧较小元素留出空间的方式进行操做的。
2.基础排序算法的时间比较
上述三种排序算法在复杂度和理论上都是十分类似的,因此在互相进行比较的时候应该操做近似。这里用Timing 类来比较三种算法,根据它们对庞大数据集合进行排序时所花费的时间断定出是否有算法会不同凡响。为了进行测试,这里用到基本代码和以前为了说明每种算法的工做原理而使用的代码彻底同样。可是,在下面这些测试中,为了说明三种算法是如何处理较小数据集合和较大数据集合的,数组的大小是有变化的。时间测试程序要分别运行处理元素量为100、1000、10000至更多的几种状况。下面是代码:
//基础排序算法的时间比较
class Test
{
public static void RunTest(int numItems)
{
Timing sortTime = new Timing();
Random rnd = new Random(100);
CArray theArray = new CArray(numItems);
//选择排序
for (int i = 0; i < numItems; i++)
theArray.Insert(rnd.Next(1, int.MaxValue) * 100);
sortTime.StartTime();
theArray.SelectionSort();
sortTime.StopTime();
Console.WriteLine("Time for Selection sort: " + sortTime.Result().TotalMilliseconds);
theArray.Clear();
//冒泡排序
for (int i = 0; i < numItems; i++)
theArray.Insert(rnd.Next(1, int.MaxValue) * 100);
sortTime.StartTime();
theArray.BubbleSort();
sortTime.StopTime();
Console.WriteLine("Time for Bubble sort: " + sortTime.Result().TotalMilliseconds);
theArray.Clear();
//插入排序
for (int i = 0; i < numItems; i++)
theArray.Insert(rnd.Next(1, int.MaxValue) * 100);
sortTime.StartTime();
theArray.InsertionSort();
sortTime.StopTime();
Console.WriteLine("Time for Insertion sort: " + sortTime.Result().TotalMilliseconds);
}
}
结果大概如上所示,实在是等不及啦,没等后两个测试出来,就把它给闭了!你们见谅!
尽管选择排序始终比其余两种算法快出许多倍,可是全部这三种排序算法的性能仍是至关低的。准确地说,这些算法没有一种在对庞大数据集合进行排序时是理想选择。可是存在能高效处理庞大数据集合的排序算法。在后面的内容里将会和你们一块儿探讨。
小结
本章讨论了针对数据排序的三种算法,即选择排序、冒泡排序以及插入排序。全部这三种算法都是很是容易实现的,并且它们均可以很好地处理少许的数据集合。选择排序是三种算法中效率最高的,其次是冒泡排序和插入排序。正如本章末尾看到的那样,这三种算法没有一种是十分适合庞大数据集合的。(好比,多于万个元素的数据集合)。
源程序下载:DataStructAndAlgorithm.zip
参考书箱:<<数据结构与算法>>