基础排序算法
在读大学的时候,有门专业课,《数据结构》,当时也就是上课老师在上面讲,我们在下面听,不知道其他同学什么情况,反正我课后很少会再去看,考完试之后,那本书也被我封印了。有个室友考研,买个本《数据结构》,严蔚敏与吴伟民著,现在放在我床头,在失眠的时候看一看。现在意识到,算法真的很重要。对一些算法要进行重新学习。当然,不是C语言版本的。C#语言描述的。当做自己学习,以后也方便自己回顾,特意在博客里面记录下来。
排序算法
在日常生活中,人们所接触到的绝大多数数据都是经过排序的。比如,按照字母顺序查找字典中的定义,或者按照字母顺序在电话本中查询电话号码等等。尽管已经对一些比较老的算法进行了改进,但是仍然应该先学习集中简单的排序算法,这些简单排序算法就是插入排序算法、冒泡排序以及选择排序算法。这些算法的每一种都很容易理解和实现。
为了检验这些算法,首先需要构造一个可以实现并测试算法的测试环境,这里将构造一个 封装租处理的一些常规操作,即元素的插入操作,元素存取访问操作,以及显示数组内容的操作。下面就是程序代码:
public class CArray { private int[] arr; private int upper; private int numElements; public CArray(int size) { arr = new int[size]; upper = size - 1; numElements = 0; } public void Insert(int item) { arr[numElements] = item; numElements++; } public void DisplayElements() { for (int i = 0; i <= upper; i++) { Console.Write($"{arr[i]} "); } } public void Clear() { for (int i = 0; i <= upper; i++) { arr[i] = 0; } numElements = 0; } }
static void Main(string[] args) { CArray nums = new CArray(10); Random random = new Random(100); for (int i = 0; i < 10; i++) { nums.Insert(random.Next(0, 100)); } nums.DisplayElements(); Console.ReadLine(); }
程序运行的结果如下:
冒泡排序
这种排序算法的得名是由于数值“像气泡一样”从序列的一段浮动到另一端。 假设现在要把一列数按照圣墟方式进行排序,即较大的数字浮动到列的右侧,较小的数值浮动到列的左侧。多次遍历整个列,并且比较相邻的数值,如果左侧的数值大于右侧数值就进行交换。
BubbleSort算法的代码如下所示:
public void BubbleSort() { int temp; for (int outer = upper; outer >= 1; outer--) { for (int inner = 0; inner <= outer - 1; inner++) { if ((int)arr[inner]>arr[inner+1]) { temp = arr[inner]; arr[inner] = arr[inner + 1]; arr[inner + 1] = temp; } } } }
这段代码又几个地方需要注意一下。首先,交换数组元素的代码是写在主程序中的一行,而没有子程序,如果多次调用交换子程序,就可能会降低排序的速度。既然交换代码只有短短三行的长度,所以不把代码放到子程序内也不会影响代码的清晰度。
更加需要主要的是程序中最外层的循环是从数组的末尾处开始,并且向数组 的开始处移动。这以为着数组的索引比外层循环的值更大,而且他们已经在恰当的位置上了,因而算法就不需要再访问这些数值了。内层循环从数组的第一个元素开始,并且在几乎达到数组最后位置的时候结束。内层循环会对inner和inner+1标识的两个相邻位置的数值进行比较,并且在必要时交换他们的数值。
在开发算法的过程中可能要做的事情之一就是在程序运行期间观察代码的中间结果,用Visual Studio的时候,可以用IDE自带的调试工具来实现。然而有些时候全部真正要观测的却是数组的内容。一种简便的方式就是在代码的释放位置插入显示的方法。代码修改如下:
public void BubbleSort() { int temp; for (int outer = upper; outer >= 1; outer--) { for (int inner = 0; inner <= outer - 1; inner++) { if ((int)arr[inner] > arr[inner + 1]) { temp = arr[inner]; arr[inner] = arr[inner + 1]; arr[inner + 1] = temp; } } this.DisplayElements(); } }
这里把DisplayElements()方法放置在了两个for循环之间,如果对主程序按照如下方式进行修改:
static void Main(string[] args) { CArray nums = new CArray(10); Random random = new Random(100); for (int i = 0; i < 10; i++) { nums.Insert(random.Next(0, 100)); } Console.WriteLine("Before sorting:"); nums.DisplayElements(); Console.WriteLine("During sorting:"); nums.BubbleSort(); Console.WriteLine("After sorting:"); nums.DisplayElements(); Console.ReadLine(); }
程序输出如下所示:
选择排序
这种排序是从数组的起始位置处开始,把第一个元素与数组中的其他元素进行比较。然后将最小的元素防止在第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[inner] < arr[min]) { min = inner; } } temp = arr[outer]; arr[outer] = arr[min]; arr[min] = temp; this.DisplayElements(); } }
运行结果如下:
插入排序
插入排序算法类似于人们通常按照数字顺序或者字母顺序进行排序的方法。加入我要求全班同学上交填有本人姓名、学号以及简短自我介绍的索引卡片。而学生们交回来的卡片上是随机排序的。如果把卡片按照字母排序排列,就可以构建出一张座次表了。
插入排序的代码如下所示:
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; this.DisplayElements(); } }
运行结果如下所示:
插入排序算法有两层循环,外层循环会逐个遍历数组元素,而内层循环则会把外层循环所选择的元素与该元素在数组内的下一个元素进行比较。如果外层循环选择的元素小于内层循环选择的元素,那么数组元素都向右移动以便为内层循环元素留出位置。
基础排序算法的时间比较
时间测试程序要分别运行处理元素量为100,1000,10000的集中情况,下面是代码:
Random random = new Random(100); int numItems = 1000; CArray theArray = new CArray(numItems); { Stopwatch watch = new Stopwatch(); for (int i = 0; i < numItems; i++) { theArray.Insert(random.Next(0, 100)); } watch.Start(); theArray.SelectionSort(); watch.Stop(); Console.WriteLine($"Time for Selection sort :{watch.ElapsedMilliseconds}"); theArray.Clear(); } { Stopwatch watch = new Stopwatch(); for (int i = 0; i < numItems; i++) { theArray.Insert(random.Next(0, 100)); } watch.Start(); theArray.BubbleSort(); watch.Stop(); Console.WriteLine($"Time for Bubble sort :{watch.ElapsedMilliseconds}"); theArray.Clear(); } { Stopwatch watch = new Stopwatch(); for (int i = 0; i < numItems; i++) { theArray.Insert(random.Next(0, 100)); } watch.Start(); theArray.InsertionSort(); watch.Stop(); Console.WriteLine($"Time for InsertionSort sort :{watch.ElapsedMilliseconds}"); }
这三种排序算法的性能还是相当低的。这些算法没有一种在对庞大数据集合进行排序时是理想选择。但是还是存在这种算法的。
小结
选择排序、冒泡排序、插入排序,它们都可以很好地处理少量的数据集合。算则排序是三种算法中效率最高的,其次是冒泡排序和插入排序。