冒泡排序:相邻的两个元素比较,如果a[j]>a[j+1],则交换位置。一次循环,最大值放到最后位置
选择排序:以第一个元素为基准,如果a[i]>a[j],则交换位置。将最小的数放在索引0的位置,将第二个较小的数放在索引1的位置...
插入排序:将第一个元素认为是最小的,放到索引0的位置,如果第二个元素比第一个元素小,则第二个元素放到索引0的位置,第一个元素被挤到后面。第N个元素插入合适的位置:从索引0的元素开始查找,插入的位置满足a[j] <= a[N] <= a[j+1]。
快速排序:比如以第一个元素pivot作为分割数,第一次大循环将大于pivot的数放在pivot的后面,小于pivot的数放在pivot的前面。此时split所在的索引为splitIndex. 将数组分为前[0,splitIndex]和 后[splitIndex,length-1] 两部分。前、后分别进行排序,以此递归即可
在.net core3.1新建控制台应用程序SortAlgorithmDemo。
测试源程序如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
namespace SortAlgorithmDemo
{
class Program
{
static void Main(string[] args)
{
int[] array = GenerateRandomArray();
TestSort("冒泡排序", BubbleSort, array);
array = GenerateRandomArray();
TestSort("选择排序", SelectSort, array);
array = GenerateRandomArray();
TestSort("插入排序", InsertSort, array);
array = GenerateRandomArray();
TestSort("快速排序", QuickSort, array);
Console.ReadLine();
}
/// <summary>
/// 测试各种排序算法的统一入口
/// </summary>
/// <param name="sortCategory">排序算法类型</param>
/// <param name="actionSort">一个委托,对应使用的排序算法函数</param>
/// <param name="array">要排序的数组</param>
static void TestSort(string sortCategory, Action<int[]> actionSort, int[] array)
{
Console.WriteLine($"开始测试【{sortCategory}】...");
PrintArray(array);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
actionSort(array);
stopwatch.Stop();
Console.WriteLine($"【{sortCategory}】花费时间:【{stopwatch.Elapsed.TotalMilliseconds}】ms。排序后结果显示:");
PrintArray(array);
}
/// <summary>
/// 生成一个32个元素的随机数组
/// </summary>
/// <returns></returns>
static int[] GenerateRandomArray()
{
int[] array = new int[32];
for (int i = 0; i < array.Length; i++)
{
array[i] = new Random(Guid.NewGuid().GetHashCode()).Next(1, 1000);
}
return array;
}
/// <summary>
/// 将数组平均分成两行,来打印
/// </summary>
/// <param name="array"></param>
static void PrintArray(int[] array)
{
int middle = array.Length / 2;//将数组一份为2
Span<int> spanFront = new Span<int>(array, 0, middle);
Console.WriteLine(string.Join(",", spanFront.ToArray()));
Span<int> spanBehind = new Span<int>(array, middle, array.Length - middle);
Console.WriteLine(string.Join(",", spanBehind.ToArray()));
}
/// <summary>
/// 冒泡排序:相邻的两个元素比较,如果a[j]>a[j+1],则交换位置。一次循环,最大值放到最后位置
/// </summary>
/// <param name="array"></param>
static void BubbleSort(int[] array)
{
for (int i = 0; i < array.Length - 1; i++)
{
for (int j = 0; j < array.Length - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
/// <summary>
/// 选择排序:以第一个元素为基准,如果a[i]>a[j],则交换位置。将最小的数放在索引0的位置,将第二个较小的数放在索引1的位置...
/// </summary>
/// <param name="array"></param>
static void SelectSort(int[] array)
{
for (int i = 0; i < array.Length; i++)
{
for (int j = i + 1; j < array.Length; j++)
{
if (array[i] > array[j])
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
/// <summary>
/// 插入排序:将第一个元素认为是最小的,放到索引0的位置,如果第二个元素比第一个元素小,则第二个元素放到索引0的位置,第一个元素被挤到后面。
/// 第N个元素插入合适的位置:从索引0的元素开始查找,插入的位置满足a[j] <= a[N] <= a[j+1]。
/// </summary>
/// <param name="array"></param>
static void InsertSort(int[] array)
{
for (int i = 1; i < array.Length; i++)
{
//插入的索引位置共有3种:头、中间、尾
//当前元素需要插入的索引:默认插入位置不变,就是尾巴位置
int insertIndex = i;
//如果当前索引小于最小的,则插入索引0的位置,就是头部位置
if (array[i] < array[0])
{
//插入索引0的位置
insertIndex = 0;
}
else
{
//找出第一个 当前值 在 array[j]与array[j + 1]之间,则插入的索引(j + 1)
for (int j = 0; j + 1 < i; j++)
{
if (array[j] < array[i] && array[i] <= array[j + 1])
{
insertIndex = j + 1;
break;
}
}
}
//从insertIndex到i之间的元素整体向后移动一个索引,将当前元素插入到索引【insertIndex】位置
int temp = array[i];//记录当前值
for (int j = i; j > insertIndex; j--)
{
array[j] = array[j - 1];
}
array[insertIndex] = temp;
}
}
/// <summary>
/// 快速排序:比如以第一个元素pivot作为分割数,第一次大循环将大于pivot的数放在pivot的后面,小于pivot的数放在pivot的前面。此时split所在的索引为splitIndex
/// 将数组分为前[0,splitIndex]和 后[splitIndex,length-1] 两部分。前、后分别进行排序,以此递归即可
/// </summary>
/// <param name="array"></param>
static void QuickSort(int[] array)
{
PartialSort(array, 0, array.Length - 1);
}
/// <summary>
/// 获取分割点索引,并将大值放在分割点的后面,小值放在分割点的前面。
/// 然后独自对分割后的两个【部分数组】进行独自排序
/// </summary>
/// <param name="array"></param>
/// <param name="fromIndex"></param>
/// <param name="toIndex"></param>
static void PartialSort(int[] array, int fromIndex, int toIndex)
{
if (toIndex <= fromIndex)
return;
//获取分割索引,本次分割将小值放在分割索引的前面,将大值放在分割索引的后面
int splitIndex = GetPartialIndex(array, fromIndex, toIndex);
//前面的一部分独自排序
PartialSort(array, fromIndex, splitIndex - 1);
//后面的一部分独自排序
PartialSort(array, splitIndex + 1, toIndex);
}
/// <summary>
/// 进行一次大的排序,以起始索引的值作为支点pivot(分割点),将大于pivot放在右边,小于pivot放在左边
/// </summary>
/// <param name="array"></param>
/// <param name="fromIndex"></param>
/// <param name="toIndex"></param>
/// <returns></returns>
static int GetPartialIndex(int[] array, int fromIndex, int toIndex)
{
//比如以第一个元素为支点pivot,分割点
int pivotValue = array[fromIndex];
while (fromIndex < toIndex)
{
//从后向前查找比支点pivot小的值
while (fromIndex < toIndex && pivotValue < array[toIndex])
{
toIndex--;
}
//比pivot小的放左边
array[fromIndex] = array[toIndex];
//从前向后搜索比pivot大的值,比pivot大的放右边
while (fromIndex < toIndex && array[fromIndex] < pivotValue)
{
fromIndex++;
}
//比pivot大的放右边
array[toIndex] = array[fromIndex];
}
//循环结束,此时 fromIndex=toIndex。
//左边都比pivot小,右边都比pivot大。
array[fromIndex] = pivotValue;
return toIndex;
}
}
}
程序运行结果:
很多人都普遍认为 快速排序 是最快的。但实际测试反而比冒泡,插入排序还要慢。
原因:只有32个元素进行排序。递归对栈stack进行操作需要耗时少许时间。
快速排序多用于随机海量数据进行排序,当数组元素较少时,建议不要使用快速排序。