可视化工具及动画展示:旧金山大学 (usfca)|数据结构可视化工具
排序算法概念及描述:1.0 十大经典排序算法(文章部分内容引用自改文章)
参考:邓俊辉 的数据结构
本文未对排序算法概念进行详细说明,只是提供已经验证过的代码及对算法核心进行简要说明
常用八种排序算法: 插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序·
全部代码(github) C#版本
0X00 前言
排序算法是《数据结构与算法》中最基本的算法之一。
排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。用一张图概括:


关于时间复杂度
平方阶 (O(n2)) 排序 各类简单排序:直接插入、直接选择和冒泡排序。
线性对数阶 (O(nlog2n)) ( log2n 是以2为底数的n的对数)排序: 快速排序、堆排序和归并排序;
O(n1+§)) 排序 ( § 是介于 0 和 1 之间的常数 ): 希尔排序
线性阶 (O(n)) 排序: 基数排序,此外还有桶、箱排序。
关于稳定性
稳定的排序算法:冒泡排序、插入排序、归并排序和基数排序。
不是稳定的排序算法:选择排序、快速排序、希尔排序、堆排序。
名词解释:
- n:数据规模
- k:"桶"的个数
- In-place:占用常数内存,不占用额外内存
- Out-place:占用额外内存
- 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同
0X01 冒泡排序(起泡排序)
/// <summary>
/// 冒泡排序(A版本)
/// 从后往前扫描待排序序列,如果前一个元素比后一个元素大,就交换它们两个,对每一对相邻元素作同样的工作;这样,第一次扫描待排序的序列会找到一个最小值并将其放置在第一位,第二次扫描待排序的序列会找到一个第二小的值并将其放置在第二位,第三次扫描待排序的序列会找到一个第三小的值并将其放置在第三位,以此类推,直到将所有元素排序完毕;排序的过程就像泡泡不断的往上冒,总是小的泡泡在最上面,大的泡泡在最下面。
/// 时间复杂度:
/// 双层循环次数:内循环次数 i=0(n-1),i=1(n-2),i=2(n-3),...,i=n-3(2),i=n-2(1)为等差数列,总次数=n*(0+n-1)/2=n*(n-1)/2
/// 假设每次比较都需要交换,执行内循环一次时复杂度为2(比较一次+交换一次),所以复杂度=2*n(n-1)/2=n(n-1)
/// 当n非常大时,多项式以幂次方最大的为标准所以复杂度O=n(n-1)=O(n*n)
/// </summary>
/// <param name="A"></param>
void BubbleSort(int[] A)
{
int n = A.Length;
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (A[j] > A[j + 1])
{
Swap(ref A[j + 1], ref A[j]);
}
}
}
}
/// <summary>
/// 冒泡排序(E版本)(最优版本)
/// 时间复杂度:
/// 最优的时间复杂度:当数据本身是有序的时候,只会比较但是不会交换,内循环执行一圈就结束了,复杂度O=n-1=O(n)
/// 最坏的时间复杂度:O=n(n-1)=O(n*n)
/// </summary>
/// <param name="A"></param>
void BubbleSort_E(int[] A)
{
int n = A.Length;
bool sorted = false; //整体排序标志,首先假定尚未排序
while (!sorted)
{
sorted = true;//假定有序
for (int i = 0; i < n - 1; i++)
{
if (A[i] > A[i + 1])
{
Swap(ref A[i + 1], ref A[i]);
sorted = false;
}
}
n--;//因整体排序不能保证,需要清除排序标志
}
}
0X02 选择排序(直接选择排序)
/// <summary>
/// 选择排序(直接选择排序)
/// 一次从待排序的序列中选出最小(或最大)的一个元素,存放在已排好序的序列的后一个位置,直到全部待排序的数据元素排完;
/// 时间复杂度:
/// 双层循环次数:内循环次数 i=0(n),i=1(n-1),i=2(n-2),...,i=n-2(2),i=n-1(1)为等差数列,总次数=(n-1)*(0+n)/2=n*(n-1)/2
/// 最坏情况每次比较都需要交换,执行内循环一次时复杂度为2(比较一次+交换一次),所以复杂度=2*n(n-1)/2=n(n-1),O=n(n-1)=O(n*n)
/// 最优情况每次比较都不需要交换,执行内循环一次时复杂度为1(比较一次),,所以复杂度=n(n-1)/2,O=n(n-1)/2=O(n*n)
/// </summary>
/// <param name="A"></param>
void SelectionSort(int[] A)
{
int n = A.Length;
int min;
for (int i = 0; i < n - 1; i++)
{
min = i;
for (int j = i; j < n; j++)
{
if (A[min] > A[j])
{
min = j;
}
}
Swap(ref A[min], ref A[i]);
//Console.Write($"{i},{min}"); PrintArray(A, i, min+1);
}
}
0X03 插入排序(直接插入排序)
适合少量元素排序
可视化工具及动画演示
/// <summary>
/// 插入排序(直接插入排序)
/// 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
/// 时间复杂度:
/// 最坏情况双层循环次数:内循环次数 i=1(1),i=2(2),...,i=n-2(n-2)为等差数列,总次数=(n-2)*(1+n-2)/2=(n-2)*(n-1)/2
/// 最坏情况每次比较都需要交换,执行内循环一次时复杂度为2(比较一次+交换一次),所以复杂度=2*(n-2)*(n-1)/2,O=(n-1)(n-2)=O(n*n)
/// </summary>
/// <param name="A"></param>
void InsertionSort(int[] A)
{
int n = A.Length;
for (int i = 1; i < n - 1; i++) //第一个当做有序序列
{
for (int j = i; j > 0 && A[j - 1] > A[j]; j--) //内循环使用冒泡方式对前面有序序列进行插入
{
Swap(ref A[j - 1], ref A[j]);
}
}
}
/// <summary>
/// 插入排序(E版本)(优化版本)
/// 时间复杂度:
/// 最优情况双层循环次数:内循环次数 i=1(1),i=2(1),...,i=n-2(1),总次数=(n-2)
/// 最优情况每次比较都不需要交换,执行内循环一次时复杂度为1(比较一次),所以复杂度=2*(n-2),O=2(n-2)=O(n)
/// </summary>
/// <param name="A"></param>
void InsertionSort_E(int[] A)
{
int n = A.Length;
int j, tmp;
for (int i = 1; i < n - 1; i++) //第一个当做有序序列
{
tmp = A[i];
for (j = i; j > 0 && A[j - 1] > tmp; j--) //内循环使用冒泡方式对前面有序序列进行插入
{
A[j] = A[j - 1]

本文深入讲解十大经典排序算法,包括冒泡排序、选择排序、插入排序等,对比分析各种算法的时间复杂度、空间复杂度及稳定性,附带C#代码实现与耗时测试,助您全面理解排序算法。
最低0.47元/天 解锁文章
1774

被折叠的 条评论
为什么被折叠?



