算法1:选择排序
基本思想
- 首先找到数组中最小的那个元素,然后,将它和数组的第一个元素交换位置(如果第一个元素就是最小元素,那么就和它自己交换)。
- 除去数组第一个元素以外,在剩余的元素中找到最小的元素,将它与数组的第二个元素交换位置。
- 循环往复,直到数组排序完成。
特点
- 排序算法总共有两个动作:比较和交换。
- 假如数组有N个元素,那么选择排序仅做N次交换,交换的总次数是N,交换的次数和数组的大小是线性关系。因此算法的时间效率取决于比较的次数。
- 假如数组有N个元素,那么选择排序要进行多少次比较呢?3.假如数组有N个元素,那么选择排序要进行多少次比较呢?
第1次有N个待排元素,N个元素之间要进行N-1次比较
第2次有N-1个待排元素,N-1个元素要进行N-2次比较
第N-1次有2个待排元素,2个元素要进行1次比较
第N次有1个待排元素,1个元素进行0次比较
所以,N个元素的数组,使用选择排序算法总的比较次数为
- 所以选择排序算法的时间复杂度≈N^2
代码实现
//选择排序
void swap(int *a,int i,int j)
{
int t=a[i];
a[i]=a[j];
a[j]=t;
}
void selection_sort(int *a,int len)
{
int m=len-1;
int i,j,k;
for(i=0;i<m;i++)
{
k=1;
for(j=i+1;j<len;j++)
{
if(a[j]<a[k])
k=j;
}
if(k!=i)
{
swap(a,k,i);
}
}
}
亮点
- 选择排序有一个特点,是其他的排序算法没有的,就是选择排序的数据移动是最少的,每一次交换都会改变两个数组元素的值,将一个数组元素排好序放在应在的位置上,所以N个元素的数组只需要进行N次交换—交换的次数和数组的大小是成线性关系的。其他算法都不具备这个特征(大部分的增长次数都是线性对数或者是平方级别的)。
- 选择排序的运行时间是和数组的输入状态没有关系的,你会发现一个有序的数组和一个无序的数组所用的排序时间竟然一样长(基本上取决于比较的次数)。而其他算法会更加善于利用输入的初识状态。
算法2:插入排序
基本思想
- 就像人们打牌一样,一张一张的来,将每一张牌插入到其他已经有些的牌中的适当位置。插入一张牌,我们要将已经有序的牌中一部分的牌都往右移动一位。
- 插入排序和选择排序有一点是相同的,就是当前索引的左边的所有元素都是有序的,但是它们的最终位置还不确定,为了给更小的元素腾出空间,它们可能会被移动。
- 当当前索引到达数组的最右端时,数组的排序就完成了。到达数组的最右端时,数组的排序就完成了。
特点
-
在最好情况下,即数组N个元素有序的情况下,插入排序需要进行的交换次数为0次。需要进行的比较次数为N-1。
-
最差的情况下,即数组N个元素逆序排列,插入排序需要进行的交换次数为 2.差的情况下,即数组N个元素逆序排列,插入排序需要进行的交换次数为zui的情况下,即数组N个元素逆序排列,插入排序需要进行的交换次数为 2.差的情况下,即数组N个元素逆序排列,插入排序需要进行的交换次数为
计算过程如下:
所以我们总结上面这个表。N个数组元素逆序排序的情况下,需要进行N-1次内循环,第i次的循环中,需要进行i次交换和i次比较。
所以,我们得到在插入排序在最坏情况下,交换和比较的次数相等,都等于:
-
平均情况下,即对于随机排列的数组,每个元素都有可能向后移动半个数组的长度(最坏情况下,每个元素都有可能向后移动整个数组的长度,所以平均情况移动的次数刚好是最坏情况下的1/2)因此,平均情况下,插入排序的交换和选择次数为:
代码实现
//插入排序
void swap(int *a,int i,int j)
{
int t=a[i];
a[i]=a[j];
a[j]=t;
}
void insert_sort(int a*,int len)
{
int i,j,k;
for(int i=1;i<len;i++)
{
for(j=i;j>0&&a[j]<a[j-1];j--)
{
swap(a,j,j-1);
}
}
}
亮点
- 相比较选择排序,插入排序可以对数组的初始输入状态进行感知(逆序、有序、随机),插入排序对实际应用中常见的某种类型的非随机数组(可能部分有序)很有效。当你用插入排序对一个有序数组进行排序,插入排序能够立刻发现每个元素已经在合适的位置上了,因此运行时间也是线性的(而选择排序遇到这种有序的状况,运行时间依然是平方级别的。)
- 插入排序对部分有序的数组十分高效,也很适合小规模数组。这些类型的数组在实际应用中经常出现,而且它们也是高级排序算法的中间过程,因此在高级排序算法部分也会遇到插入排序。