插入排序与选择排序
在学习过程中我总是难以分清选择排序与插入排序的区别所在。粗看两个算法的实现原理,都是从数组的无序范围内选择一个元素,放入我们所维护的不断扩大的数组的有序范围中。为了区分两者的不同,并加深自己对插入排序和选择排序的理解,故有此文。
直接插入排序(Insertion Sort)
直接插入排序算法是一种稳定的、原地的排序算法,算法的时间复杂度为\(O(n^2)\)。
基本思想
每次将一个待排序的记录按其关键字大小插入到前面已经排好序的子表中的适当位置,直到全部记录插入完成为止。
实现(升序)
每一趟从无序范围中取出第一个数\(a\),在有序范围中将比\(a\)大的数全部移动一位,直到在有序范围内遍历的数比\(a\)小,再将\(a\)插入到该数之后。
void InsertionSort(int a[], int len)
{
int temp;
for(int i = 1; i < len; i++)
{
temp = a[i];
int j = i - 1;
while(j >= 0 && a[j] > temp)
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = temp;
}
}
最好情况与最坏情况
- 最好情况出现在数组已经正序有序的情况下。此时对数组进行排序,不会进入到while循环中,相当于遍历了一次数组,时间复杂度为\(O(n)\)。
- 最坏情况出现在逆序有序的情况下。此时对数组进行排序,每次都会进入到while循环中,并且每次都会从有序范围的尾部遍历到有序范围的首部,时间复杂度为\(O(n^2)\)。
简单选择排序(Selection Sort)
简单选择排序算法是一种不稳定的、原地的排序算法,算法的时间复杂度为\(O(n^2)\)。
思想
每一趟都选择出一个最大或最小的元素,并放在合适的位置。
实现(升序)
每一趟从无序范围中选择最小的数\(a\),并将\(a\)放入有序范围中的最后一个位置。
void SelectionSort(int a[], int len)
{
for(int i = 0; i < len; i++)
{
int min = i; // min记录无序范围最小元素的下标
for(int j = i + 1; j < len; j ++)
{
if(a[j] < a[min])
{
min = j;
}
}
int temp = a[i];
a[i] = a[min];
a[min] = temp;
}
}
最好情况与最坏情况
选择排序没有最好情况与最坏情况之分,无论数组有序与否,排序算法都需要进入到第二个for循环中遍历数组的无序部分找出最小元素,时间复杂度为\(O(n^2)\)。
插入排序与选择排序的异同
如前言所述,两算法都是从无序范围取数,放置到有序范围中,但两者依旧有不小的区别。
- 插入排序算法是从无序范围中取出第一个数,在有序范围中寻找合适的位置,将位置往右的元素全部右移一位,再将数插入到数组中。
- 选择排序算法是从无序范围中取出最大或最小的数,插入到有序范围中的最后一个位置。
如此来看,两算法的大体思路略有相似,但具体的实现细节是不同的。