目录
其他排序算法动画图 请期待 排序算法【动画】诠释排序过程【二】
语言:c#
Demo地址【Unity c#】:https://download.csdn.net/download/qq_30259857/10903321
1. 冒泡排序(bubble sort)
每一次排序过程:
- 介绍:冒泡排序是一种最简单的排序算法。冒泡排序得名与键值较小的元素如同“气泡”一样逐渐到序列的顶端。
- 优点:最简单的排序算法,
- 缺点:
- 慢,每次只能移动相邻两个数据
- 实现逻辑:基本思想是迭代的对输入序列中的第一个元素到最后一个元素进行两两比较,当需要时交换这两个位置。该过程持续迭代直到在一堂排序过程中不需要交换操作为止。
- 性能:
最坏情况下时间复杂度:O(n平方)
最好情况下时间复杂度:O(n) 平均情况下时间复杂度:O(n平方) 最坏情况下空间复杂度:O(1)
- 代码
大众版:
int[] arr = { 1, 7, 3, 6, 2 };
for (int i = arr.Length - 1; i >= 0; i--)
{
for (int j = 0; j < i ; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
改进版:在排序过程中,没有交换操作则意味着排序完成。如果序列已经是有序的,则可以通过判断该标记来结束算法
int[] arr = { 1, 7, 3, 6, 2 };
bool isSort = true; //加此字段为 改进版
for (int i = arr.Length - 1; i >= 0 && isSort; i--)
{
isSort = false;
for (int j = 0; j < i ; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSort = true;
}
}
}
2. 选择排序(selection sort)
每一次排序过程
- 介绍:选择排序是一种原地排序算法,适用于小文件。由于它重复选择最小的元素所以称之为选择排序。
- 优点:
- 容易实现
- 原地排序(不需要额外的存储空间)
- 移动数据的次数已知(n-1次);
- 缺点:
- 扩展性较差
- 比较次数多
- 实现逻辑:
- 寻找序列中的最小值
- 用当前的值交换最小值
- 对所有元素重复上述过程,直到整个序列排序完成
- 性能:
最坏情况下时间复杂度:O(n平方)
最好情况下时间复杂度:O(n) 平均情况下时间复杂度:O(n平方) 最坏情况下空间复杂度:O(1)
- 代码
int[] arr = { 5, 7, 3, 6, 4 };
int min = 0;
for(int i = 0; i <= arr.Length - 1; i++)
{
min = i;
for (int j = i + 1; j < arr.Length; j++)
{
if(arr[j] < arr[min])
{
min = j;
}
}
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
3. 插入排序(insertion sort)
- 介绍:插入排序是一种简单且有效的比较排序算法。
- 优点:
-
实现简单
-
数据量较少时效率高
-
适应性:如果输入序列已预排序,则时间复杂度为O(n+d),d是反转的次数
-
实际运行效率优于"选择排序"和"冒泡排序"
-
稳定性:键值相同时它能保持输入数据的原有次序
-
原地:仅需要常量O(1)的辅助内存空间
-
即时:插入排序能够在接收序列的同时对其进行排序
-
-
缺点:
-
比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候,但用链表可以解决这个问题。
-
- 实现逻辑:
- 原文:插入排序重复如下过程:每次从输入数据中移除一个元素并将其插入已排序序列的正确位置,直到所有输入元素都插入有序序列中。插入排序是典型的原地排序,经过k次迭代后数组具有性质:前k+1个元素已经排序。
- 代码
int val, j;
int[] arr = { 4, 2, 3, 1, 5 };
for (int i = 1; i < arr.Length; i++)
{
val = arr[i];
j = i;
while (j >= 1 && arr[j - 1] > val)
{
arr[j] = arr[j - 1];
j--;
}
arr[j] = val;
}
4. 归并排序(merge sort)
图解:
图资源来自:https://www.cnblogs.com/chengxiao/p/6194356.html
- 介绍:归并排序是分治的一个实例。归并是把两个已排序文件合并成一个更大的已排序文件的过程。
- 优点:
-
稳定
-
速度仅次于快速排序
-
-
缺点:
-
归并排序需要O(n)的辅助空间,而与之效率相同的快排和堆排分别需要O(logn)和O(1)的辅助空间,在同类算法中递归的空间复杂度略高。
-
- 实现逻辑:
- 原文:归并排序将输入序列分成两部分并递归地处理每一部分。当子问题解决后,算法又将问题的解合并。
- 性能:
最坏情况下时间复杂度:O(nlogn)
最好情况下时间复杂度:O(nlogn) 平均情况下时间复杂度:O(nlogn) 最坏情况下空间复杂度:O(n)
- 代码
/// <summary> /// 归并排序 /// </summary> void MergeSort() { int[] arr = { 9, 8, 10, 7, 2}; int left = 0; int right = arr.Length - 1; int[] temp = new int[arr.Length]; MergeSort(ref arr, left, right, ref temp); } void MergeSort(ref int[] arr, int left, int right, ref int[] temp) { if(left < right) { int mid = (left + right) / 2; MergeSort(ref arr, left, mid, ref temp); MergeSort(ref arr, mid + 1, right, ref temp); MergeSortArr(ref arr, left, mid, right,ref temp); } } void MergeSortArr(ref int[] arr, int left, int mid, int right, ref int[] temp) { int i = left; //左索引 int j = mid + 1; //右索引 int t = 0; //临时索引 while(i <= mid && j <= right) { if(arr[i] <= arr[j]) { temp[t++] = arr[i++]; } else { temp[t++] = arr[j++]; } } //将左边剩余元素填充进temp中 while(i <= mid) { temp[t++] = arr[i++]; } //将右边剩余元素填充进temp中 while(j <= right) { temp[t++] = arr[j++]; } t = 0; //将temp中的元素全部拷贝到原数组中 while(left <= right) { arr[left++] = temp[t++]; } }
5. 快速排序(quicksort)
-
上面排序动画中每一轮代码的解析 如下:(每一轮指的是每次递归调用排序函数的轮数)
- 介绍:快速排序是冒泡排序的改进版。快速排序是分治算法技术的一个实例,也称为分区交换排序。快速排序采用递归调用对元素进行排序,是基于比较的排序算法中的一个著名算法。
- 优点:
-
极快,数据移动少(目前已知的最快的排序算法)
-
-
缺点:
-
不稳定(是否稳定取决于如何处理枢轴)
-
- 实现逻辑:(递归算法)
- 如果数组中仅有一个元素或者没有元素需要排序,则返回。
- 选择数组中的一个元素作为枢轴(pivot)点(通常选择数组最左边的元素)
- 把数组分成两部分(1)一部分元素大于枢轴(pivot)点 (2)另一部分元素小于枢轴(pivot)点
- 对两部分数组递归调用排序算法
- 性能:
最坏情况下时间复杂度:O(n*n)
最好情况下时间复杂度:O(nlogn) 平均情况下时间复杂度:O(nlogn) 最坏情况下空间复杂度:O(1) - 代码:
-
public void StartQuickSort() { int[] arr = { 9, 8, 10, 7, 2 }; QuickSort(arr, 0, arr.Length - 1); } void QuickSort(int[] arr, int left, int right) { if (left >= right) return; int index = QuickSortArr(arr, left, right); QuickSort(arr, left, index - 1); QuickSort(arr, index + 1, right); } int QuickSortArr(int[] arr, int left, int right) { int key = arr[left]; while(left < right) { //从后往前 找 比key小的index while(left < right && arr[right] > key) { right--; } arr[left] = arr[right]; while(left < right && arr[left] <= key) { left++; } arr[right] = arr[left]; } arr[left] = key; return right; }
-
排序算法比较
注:n 表示输入元素的个数
算法名 | 平均情况 | 最坏情况 | 辅助存储器 | 是否稳定 | 其他注释 |
冒泡 | O(n*n) | O(n*n) | 1 | 是 | 代码量较少 |
选择 | O(n*n) | O(n*n) | 1 | 否 | 稳定性取决于实现 |
插入 | O(n*n) | O(n*n) | 1 | 是 | 平均情况下也可以是O(n+d),其中d为反转数 |
希尔 | -- | O(nlog平方n) | 1 | 否 | |
归并 | O(nlogn) | O(nlogn) | 不确定 | 是 | |
堆 | O(nlogn) | O(nlogn) | 1 | 否 | |
快速 | O(nlogn) | O(n*n) | O(logn) | 不确定 | 是否稳定取决于如何处理枢轴 |
树 | O(nlogn) | O(n*n) | O(n) | 不确定 | 能否实现稳定排序 |
其他排序算法动画图 请期待 排序算法【动画】诠释排序过程【二】