首先来看看排序算法有哪八种:
1.直接插入排序
原理 :每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
public void InsertSort
{
//直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的
for (int i = 1; i < arry.Length; i++)
{
T temp = arry[i];
int j = i - 1;
// 在已排好序的数列段中找到比新数值小的数值
while (j >= 0 && comparison(arry[j] , temp) == 1)
{
//将比新数值大的数值向后移
arry[j + 1] = arry[j];
j--;
}
// 如果在已排好序的数列段中找到了比新数值小的数值
// 将数值替换到此位置
arry[j + 1] = temp;
}
}
2.希尔排序(有点疑惑网上的希尔排序都只用三个循环,博主是没有理解透彻,按照原理 应该至少有四个循环,因为插入排序就有两个循环,希望各位看官有知道的能够为博主解惑)
原理 : 将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。
是非稳定排序算法
public void ShellSort
{
int Length = arry.Length;
for (int gap = Length / 2; gap > 0; gap = gap / 2) //分组,得到组数
{
for (int i = gap; i < gap * 2; i++)//对每一组进行插入排序
{
for (int j = i; j < Length; j += gap)
{
T temp = arry[j];
int k = j - gap;
while (k >= 0 && comparison(temp , arry[k]) == -1)
{
arry[k + gap] = arry[k];
k = k - gap;
}
arry[k + gap] = temp;
}
}
}
}
---------------------
3.简单选择排序
原理 : 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
进行移动操作的时间复杂度为O(n)。进行比较操作的时间复杂度为O(n^2)
public void SimpleSelectSort
{
int k = 0;
for (int i=0;i
k = i;
for (int j = i + 1;j
if(comparison(arry[k] , arry[j]) == 1)
{
k = j;
}
}
Exchange(ref arry[i],ref arry[k]);
}
}
4.堆排序
原理 : 是利用一种被称作二叉堆的数据结构进行排序的排序算法。
二叉堆中,有两个与所维护数组相关的属性。Length表示数组的元素个数,而HeapSize则表示二叉堆中所维护的数组中的元素的个数(并不是数组中的所有元素都一定是二叉堆的有效元素)。因此,根据上述定义有: 0 <= HeapSize <= Length。
二叉堆可分为最大堆和最小堆两种类型。在最大堆中,二叉树上所有的节点都不大于其父节点,即 A[Parent(i)] >= A[i]。最小堆正好相反:A[Parent(i)] <= A[i]。
因为在调用MaxHeapify(MinHeapify)方法使根节点为A[i]的二叉堆满足最大(小)堆性质时我们有其左右子堆均已满足最大(小)堆性质这个假设,所以如果我们在将一个待排序的数组构造成最大(小)堆时,需要自底向上地调用 MaxHeapify(MinHeapify)方法。
在利用最大堆进行排序时,我们先将待排序数组构造成一个最大堆,此时A[0](根节点)则为数组中的最大元素,将A[0]与A[n - 1]交换,则把A[0]放到了最终正确的排序位置。然后通过将HeapSize减去1,将(交换后的)最后一个元素从堆中去掉。然后通过MaxHeapify方法将余下的堆改造成最大堆,然后重复以上的交换。重复这一动作,直到堆中元素只有2个。则最终得到的数组为按照升序排列的数组。
public void HeapSort
{
BuildMHeap
for (int i = array.Length - 1; i > 0; i--)
{
Exchange(ref array[i], ref array[0]);
MHeapify
}
}
//计算节点的父节点和子节点
private int Parrent(int i)
{
return (i - 1) / 2;
}
private int Left(int i)
{
return 2 * i + 1;
}
private int Right(int i)
{
return 2 * i + 2;
}
//构建最大堆/最小堆
private void BuildMHeap
{
for (int i = array.Length / 2 - 1; i >= 0; i--)
{
MHeapify
}
}
private void MHeapify
{
int left = Left(i);
int right = Right(i);
int extremumIndex = i;
if (left < heapSize && comparison(array[left], array[i]) > 0)
{
extremumIndex = left;
}
if (right < heapSize && comparison(array[right], array[extremumIndex]) > 0)
{
extremumIndex = right;
}
if (extremumIndex != i)
{
Exchange
MHeapify
}
}
5.冒泡排序
原理 : 首先将一个记录的关键字和第二个关键字进行比较,若为逆序,则将两个记录交换,然后比较第2个记录和第3个记录的关键字。依次类推,直至第N-1个记录和第n个记录的关键字进行过比较为止。上述过程称为第一趟冒泡排序,执行n-1次上述过程后,排序完成。
优缺点 :
优点:稳定 时间复杂度:理想情况下(数组本来就是有序的),此时最好的时间复杂度为o(n),最坏的时间复杂度(数据反序的),此时的时间复杂度为o(n*n) 。 冒泡排序的平均时间负责度为o(n*n).
缺点:慢,每次只移动相邻的两个元素。
public void BubbleSort
{
for (int i = 0; i < array.Length; i++)
{
for (int j = 0; j < array.Length - 1 - i; j++)
{
//比较相邻的两个元素,如果前面的比后面的大,则交换位置
if (comparison(array[j] , array[j + 1]) == 1)
{
Exchange(ref array[j],ref array[j + 1]);
}
}
}
}
6.快速排序
原理 : 快速排序法是采用递归的方式对待排序的数列进行若干次的操作,每次操作使得被操作的数列部分以某个元素为分界值分成两部分,一部分小于该分界值,另一部分大于该分界值.该分界值一般被称为"枢轴". 一般先以左边第一个数作为分界值,将数列按该分界值分成左右两部分,左边部分小于该分界值,右边部分大于该分界值,然后再对左右两部分做重复的操作,直到最后完成排序。
快速排序是一种不稳定的排序算法
public void QuickSort
{
//左边小于右边说明排序还没有完成
if (left < right)
{
T middle = array[(left + right) / 2];
//注意初始化
int j = right + 1;
int i = left - 1;
while (true)
{
while (comparison(middle, array[++i]) > 0 && i < right) ;//左边,先加的原因是防止找到的最左边会超出界限
while (comparison(middle, array[--j]) < 0 && j > 0) ; //右边
if (i >= j)
break;
Exchange
}
for (int m = 0; m < array.Length; m++)
{
Console.Write(array[m] + " ");
}
Console.ReadLine();
QuickSort
QuickSort
}
}
7.归并排序
是分治法(Divide and Conquer)的一个非常典型的应用
原理 : 设归并排序的当前区间是R[low..high],分治法的三个步骤是:
①分解:将当前区间一分为二,即求分裂点
②求解:递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;
③组合:将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high]。
递归的终结条件:子区间长度为1(一个记录自然有序)。
public void MergeSortFunction
{
try
{
if (first < last) //子表的长度大于1,则进入下面的递归处理
{
int mid = (first + last) / 2; //子表划分的位置
MergeSortFunction(array,comparison, first, mid); //对划分出来的左侧子表进行递归划分
MergeSortFunction(array, comparison, mid + 1, last); //对划分出来的右侧子表进行递归划分
MergeSortCore(array, comparison, first, mid, last); //对左右子表进行有序的整合(归并排序的核心部分)
}
}
catch (Exception ex)
{ }
}
//归并排序的核心部分:将两个有序的左右子表(以mid区分),合并成一个有序的表
private void MergeSortCore
{
try
{
int indexA = first; //左侧子表的起始位置
int indexB = mid + 1; //右侧子表的起始位置
T[] temp = new T[last + 1]; //声明数组(暂存左右子表的所有有序数列):长度等于左右子表的长度之和。
int tempIndex = 0;
while (indexA <= mid && indexB <= last) //进行左右子表的遍历,如果其中有一个子表遍历完,则跳出循环
{
if (comparison(array[indexA] , array[indexB]) <= 0) //此时左子表的数 <= 右子表的数
{
temp[tempIndex++] = array[indexA++]; //将左子表的数放入暂存数组中,遍历左子表下标++
}
else//此时左子表的数 > 右子表的数
{
temp[tempIndex++] = array[indexB++]; //将右子表的数放入暂存数组中,遍历右子表下标++
}
}
//有一侧子表遍历完后,跳出循环,将另外一侧子表剩下的数一次放入暂存数组中(有序)
while (indexA <= mid)
{
temp[tempIndex++] = array[indexA++];
}
while (indexB <= last)
{
temp[tempIndex++] = array[indexB++];
}
//将暂存数组中有序的数列写入目标数组的制定位置,使进行归并的数组段有序
tempIndex = 0;
for (int i = first; i <= last; i++)
{
array[i] = temp[tempIndex++];
}
}
catch (Exception ex)
{ }
}
8.基数排序
属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,算法的时间复杂度是O(n)
public static void RadixSort( int[] array, int array_x = 10, int array_y = 100)
{
/* 最大数字不超过999999999...(array_x个9) */
for (int i = 0; i < array_x; i++)
{
int[,] bucket = new int[array_x, array_y];
foreach (var item in array)
{
int temp = (item / (int)Math.Pow(10, i)) % 10;
for (int l = 0; l < array_y; l++)
{
if (bucket[temp, l] == 0)
{
bucket[temp, l] = item;
break;
}
}
}
for (int o = 0, x = 0; x < array_x; x++)
{
for (int y = 0; y < array_y; y++)
{
if (bucket[x, y] == 0) continue;
array[o++] = bucket[x, y];
}
}
}
}
辅助函数:
#region 交换值
public void Exchange
{
T temp = x;
x = y;
y = temp;
}
#endregion
#region 比较两个int的值
public int ComparisonInt(int x,int y)
{
if(x > y)
{
return 1;
}
else if(x == y)
{
return 0;
}
else
{
return -1;
}
}
#endregion
源码地址:
https://github.com/webloverand/Interview
参考列表:
1.数据结构常见的八大排序算法(详细整理): https://www.cnblogs.com/hokky/p/8529042.html
2.堆排序——C#实现 : https://blog.csdn.net/zhuo_wp/article/details/78251777
3.归并排序算法(C#实现): https://www.cnblogs.com/mingmingruyuedlut/archive/2011/08/18/2144984.html