在最好的状况下,序列已是有序的,每次插入元素最多只须要与有序表中最后一个元素进行比较,时间复杂度为O(n)。在最坏的状况下,每次插入元素须要与前面全部的元素进行比较,时间复杂度为O(n^2),平均时间复杂度为O(n^2)。算法
代码分析数组
public static <T extends Comparable<T>> void insertionSort(T[] data) { //外层循环次数为n,时间复杂度为O(n) for (int index = 1; index < data.length; index++) { T key = data[index]; int position = index; //内层循环次数为n,时间复杂度为O(n) while (position > 0 && data[position-1].compareTo(key) > 0) { data[position] = data[position-1]; position--; } data[position] = key; } }
它的基本思想是:假设序列中有n个元素,首先选择一个间隔gap,将所有的序列分为gap个子序列,而后分别在子序列内部进行简单插入排序,获得一个新的主序列;然后缩小gap,再获得子序列,对子序列进行简单插入排序,又再次获得新的主序列,直到gap=1为止。在算法中,排序前期,因为gap值比较大,插入排序的元素个数少,排序快,到了排序后期,因为前面的排序致使序列已经基本有序,插入排序对于有序的序列效率很高。因此说希尔排序最好的状况:缩小增量的插入排序,待排序已经有序。时间复杂度O(n),通常状况为下平均时间复杂度o(n^1.3),最差也是时间复杂度o(n^1.3)。希尔排序的时间复杂度与gap的选择有很大的关系,通常时间复杂度是低于O(n^2)。函数
须要注意的是:希尔排序的时间复杂度依赖于所取希尔序列的函数,可是到目前为止尚未一个最好的希尔序列。有人在大量的实验后得出结论:当n在某个特定的范围后希尔排序的比较和移动次数减小至n^1.3 无论增量序列如何取值,都应该知足最后一个增量值为1.ui
简单选择排序不管是否序列已经有序每一个数都须要进行n-1次最小数选择,因此它的最好、最坏以及平均时间复杂度都是O(n^2)。.net
代码分析code
public static <T extends Comparable<T>> void selectionSort(T[] data) { int min; T temp; //外层循环次数为n-1,时间复杂度为O(n) for (int index = 0; index < data.length-1; index++) { min = index; //内层循环次数为n,时间复杂度为O(n) for (int scan = index+1; scan < data.length; scan++) { if (data[scan].compareTo(data[min])<0) { min = scan; } } swap(data, min, index); } }
把待排序的元素按照大小在二叉树位置上排列,排序好的元素要知足:父节点的元素要大于等于其子节点;这个过程叫作堆化过程,若是根节点存放的是最大的数,则叫作大根堆;若是是最小的数,天然就叫作小根堆了。根据这个特性(大根堆根最大,小根堆根最小),就能够把根节点拿出来,而后再堆化下,再把根节点拿出来,,,,循环到最后一个节点,就排序好了。整个排序主要核心就是堆化过程,堆化过程通常是用父节点和他的孩子节点进行比较,取最大的孩子节点和其进行交换;可是要注意这应该是个逆序的,先排序好子树的顺序,而后再一步步往上,到排序根节点上。而后又相反(由于根节点也多是很小的)的,从根节点往子树上排序。最后才能把全部元素排序好。对象
时间复杂度在任何状况下都为O(nlogn)
堆排序的时间复杂度为O(nlogn),须要一个临时空间用于交换元素,因此空间复杂度为O(1)。blog
排序包括两个阶段,初始化建堆和重建堆。因此堆排序的时间复杂度由这两方面组成。排序
初始化堆:假设高度为k,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较而后交换(若是顺序是对的就不用交换);倒数第三层,则会选择其子节点进行比较和交换,若是没交换就能够不用再执行下去了。若是交换了,那么又要选择一支子树进行比较和交换;高层也是这样逐渐递归。
那么总的时间计算为:s = 2^( i - 1 ) * ( k - i );其中 i 表示第几层,2^( i - 1) 表示该层上有多少个元素,( k - i) 表示子树上要比较的次数。
S = 2^(k-2) * 1 + 2^(k-3)2…..+2(k-2)+2^(0)*(k-1) ===> 由于叶子层不用交换,因此i从 k-1 开始到 1;
S = 2^k -k -1;又由于k为彻底二叉树的深度,而log(n) =k,把此式带入;
获得:S = n - log(n) -1,因此时间复杂度为:O(n)递归
排序重建堆:在每次重建时,随着堆的容量的减少,层数会降低,函数时间复杂度会变化。重建堆一共须要n-1次循环,每次循环的比较次数为log(i),相加约为nlog(n)。
因此总的时间复杂度为O(n+nlogn)=O(nlogn)。
在最好的状况下,序列已是有序的,只进行了第一趟冒泡比较,此时算法的时间复杂度为O(n)。在最坏的状况下,执行了n-1次冒泡,时间复杂度为O(n^2)。
代码分析:
public static <T extends Comparable<T>> void bubbleSort(T[] data) { int position, scan; T temp; //循环次数为n-1,时间复杂度为O(n) for (position = data.length - 1; position >= 0; position--) { //循环次数为n,时间复杂度为O(n) for (scan = 0; scan <= position - 1; scan++) { if (data[scan].compareTo(data[scan+1]) > 0) { swap(data, scan, scan + 1); } } } }
归并排序是先递归的把数组划分为两个子数组,一直递归到数组中只有一个元素,而后再调用函数把两个子数组排好序,由于该函数在递归划分数组时会被压入栈,因此这个函数真正的做用是对两个有序的子数组进行排序。
时间复杂度分析:每次归并时要将待排序列表中的全部元素遍历一遍,因次时间复杂度为O(n)。与快速排序相似,归并排序也先将列表不断分区直至每一个列表只剩余一个元素,这个过程须要进行log2n次分区。所以归并排序的平均时间复杂度为O(nlogn)。由于无论元素在什么状况下都要作这些步骤,因此花销的时间是不变的,因此该算法的最优时间复杂度和最差时间复杂度及平均时间复杂度都是同样的为:O( nlogn )。
复杂公式分析:总时间=分解时间+解决问题时间+合并时间。分解时间就是把一个待排序序列分解成两序列,时间为一常数,时间复杂度o(1)。解决问题时间是两个递归式,元素长度为n的归并排序所消耗的时间T[n],把一个规模为n的问题分红两个规模分别为n/2的子问题,时间为2T(n/2)。合并时间复杂度为o(n)。总时间T(n)=2T(n/2)+o(n),因此得出的结果为:T[n] = O( nlogn )。参考: http://blog.csdn.net/yuzhihui_no1/article/details/44198701#t2
基本思想就是把元素从个位排好序,而后再从十位排好序,,,,一直到元素中最大数的最高位排好序,那么整个元素就排好序了。
时间复杂度分析:对于有n个元素的序列,对每一位数放置和收集的时间为O(n+r),则其时间复杂度为 O(d(n+r))。(r为基数,d为位数)
既然基数排序的时间复杂度这么低,为何不是全部的排序都使用基数排序法呢?
首先,基数排序没法创造出一个使用于全部对象类型的泛型基数排序,由于在排序过程当中要进行关键字取值的切分,所以关键字的类型必须是肯定的。
其次,当基数排序中的基数大小与列表中的元素数目很是接近时,基数排序法的实际时间复杂度接近于O(n^2)。