标签:
1.原理
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
2.特点
①运行时间与输入无关。
无论输入初始状态如何,是否有序,都需要遍历数组来找出最小的元素。其他算法则更善于利用输入的初始状态来优化时间。
②数据移动次数最小。
如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
3.代码
public static void selection_sort(int[] arr) {
int i, j, min, temp, len = arr.length;
for (i = 0; i < len - 1; i++) {
min = i;
for (j = i + 1; j < len; j++)
if (arr[min] > arr[j])
min = j;
temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
4.复杂度
①交换次数O(n): 最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次
②比较次数O(n^2): (n-1)+(n-2)+…+1=n*(n-1)/2
③赋值次数O(n):0到3(n-1)次之间
5.概述
最差时间复杂度 О(n2)
最优时间复杂度 О(n2)
平均时间复杂度 О(n2)
最差空间复杂度 总共О(n), 需要辅助空间O(1)
动态图:
选择排序的示例动画。红色表示当前最小值,黄色表示已排序序列,蓝色表示当前位置。
1.原理
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
步凑如下:
①从第一个元素开始,该元素可以认为已经被排序
②取出下一个元素,在已经排序的元素序列中从后向前扫描
③如果该元素(已排序)大于新元素,将该元素移到下一位置
④重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
⑤将新元素插入到该位置后
⑥重复步骤②~⑤
如果比较操作的代价比交换操作大的话,可以采用二分查找法来减少比较操作的数目。该算法可以认为是插入排序的一个变种,称为二分查找插入排序。
2.特点
直接插入排序是一种稳定的排序。对部分有序的数组十分高效,也很适合小规模数组。
3.代码
public static void insertion_sort( int[] arr ) {
for( int i=0; i<arr.length-1; i++ ) {
for( int j=i+1; j>0; j-- ) {
if( arr[j-1] <= arr[j] )
break;
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
另一个版本:
public static void insertion_sort(int[] arr)
{
for (int i = 1; i < arr.length; i++ ) {
int temp = arr[i];
for (int j = i - 1; j >= 0 && arr[j] > temp; j-- ) {
arr[j + 1] = arr[j];
}
arr[j + 1] = temp;
}
4.复杂度
①交换次数O(0): 不需要
②比较次数O(n^2): 最好情况是,已经有序,需(n-1)次即可次;最坏情况是,逆序,需n(n-1)/2次
③赋值次数O(n^2):比较操作的次数加上(n-1)次
平均来说插入排序算法复杂度为O(n^2)。因而,插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。
5.概述
最差时间复杂度 О(n2)
最优时间复杂度 О(n)
平均时间复杂度 О(n2)
最差空间复杂度 总共О(n), 需要辅助空间O(1)
动态图:
使用插入排序为一列数字进行排序的过程:
1.原理
它重复地遍历过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
步凑如下:
①比较相邻的元素。如果第一个比第二个大,就交换他们两个。
②对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
③针对所有的元素重复以上的步骤,除了最后一个。
④持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2.特点
尽管这个算法是最简单了解和实现的排序算法之一,但它对于少数元素之外的数列排序是很没有效率的。
3.代码
public static void bubble_sort(int[] arr) {
int i, j, temp, len = arr.length;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
4.复杂度
①交换次数O(n^2)
②比较次数O(n^2): 可以原地排序。
③赋值次数O(0):不需要,交换就可以了。
冒泡排序是与插入排序拥有相等的运行时间,但是两种算法在需要的交换次数却很大地不同。在最好的情况,冒泡排序需要O(n^2)次交换,而插入排序只要最多O(n)交换。冒泡排序的实现通常会对已经排序好的数列拙劣地运行O(n^2),而插入排序在这个例子只需要O(n)个运算。
5.概述
最差时间复杂度 О(n2)
最优时间复杂度 О(n)
平均时间复杂度 О(n2)
最差空间复杂度 总共О(n), 需要辅助空间O(1)
动态图:
1.原理
将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size而不是i++)。
例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然后我们对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
將上述四行数字,依序接在一起时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了,然后再以3为步长进行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之后变为:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最后以1步长进行排序(此时就是简单的插入排序了)。
2.特点
步长的选择是希尔排序的重要部分。只要最终步长为1任何步长序列都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。
3.代码
public static void shell_sort(int[] arr) {
int gap = 1, i, j, len = arr.length;
int temp;
while (gap < len / 3)
gap = gap * 3 + 1; // <O(n^(3/2)) by Knuth,1973>: 1, 4, 13, 40, 121, ...
for (; gap > 0; gap /= 3)
for (i = gap; i < len; i++) {
temp = arr[i];
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
4.复杂度
5.概述
最差时间复杂度 根据步长序列的不同而不同,最好时为О(n(logn)^2)
最优时间复杂度 O(n)
平均时间复杂度 根据步长序列的不同而不同
最差空间复杂度 О(n)
动态图:
算法学习#09--用简单的思维理解选择、插入、冒泡和希尔排序
标签:
原文地址:http://blog.csdn.net/tclxspy/article/details/52120861