直接插入排序与折半插入排序分析
一.直接插入排序原理
1.插入排序算法首先要把排序的数组分成两部分:第一部分是这个数组中所有已排序元素,而第二部分就是未排序的元素(即待插入元素)。
2.将待插入元素和已排序的元素逐一比较,若待插入的元素大,则直接将该元素算入已排序的元素中,否则交换两元素的位置,并且待插入元素继续和前一个元素进行比较,直到不需要交换元素位置后,将该元素算入已排序的元素中。
3.重复执行步骤2,直到未排序的元素个数为0。
插入排序的基本思想是:每步将一个待排序的记录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
插入排序是一个稳定的排序算法。
二.直接插入排序时间复杂度
最坏情况下,数组完全逆序,插入第2个元素时要考察前1个元素,插入第3个元素时,要考虑前2个元素,……,插入第N个元素,要考虑前 N - 1个元素。因此,最坏情况下的比较次数是1 + 2 + 3 + ... + (N-1),求和为 n2/2 - n / 2,忽略系数,取最高指数项,所以最坏情况下的复杂度为O(n2)。
最好情况下,数组已经是有序的,每插入一个元素,只需要考查前一个元素,因此最好情况下的时间复杂度为O(n)。
三.直接插入排序代码实现
public static void main(String[] args) { int[] arr = {1,6,5,2,4,3,7,9,8}; sort(arr); System.out.println(Arrays.toString(arr)); } public static void sort(int[] arr) { //插入的元素(从第二个开始,索引为1) int insert; //从1开始循环待插入的元素 for(int i = 1; i < arr.length; i++) { insert = arr[i]; int j; for(j = i - 1; j >= 0; j--) { if(arr[j] > insert) { arr[j+1] = arr[j]; }else{ break; } } arr[j+1] = insert; } } }
测试:运行后输出结果如下
四.折半插入排序原理
折半插入排序是对直接插入排序的改进。它不去一个个遍历已经排序的元素,而是将插入元素直接和已经排好序的数组的中间元素进行比较,如果插入的元素比较大,那么插入的元素肯定属于后半部分,否则属于前半部分。这样,不断遍历缩小范围,很快就能确定需要插入的位置。
五.折半插入排序时间复杂度
在不是很理想的情况下,折半插入排序算法比直接插入排序算法明显减少了关键字之间比较的次数,因此速度比直接插入排序算法快,但记录移动的次数没有变,所以折半插入排序算法的时间复杂度仍然为O(n2)。
折半插入排序算法是一种稳定的排序算法。
六.折半插入排序代码实现
public static void main(String[] args) { int[] arr = {5,3,1,7,8,4,2,9,6}; sort(arr); System.out.println(Arrays.toString(arr)); } public static void sort(int[] arr) { int size = arr.length; for (int i = 1; i < size; i++) { int temp = arr[i]; int begin = 0; // 标记排好序的数组的头部 int end = i - 1; // 标记排好序数组的尾部 // 只要头部一直小于尾部,说明temp还在2个标记范围内 while (begin <= end) { // 取2个标记的中间数据的值 int mid = (begin + end) / 2; // 比较,若比中间值大,则范围缩小一半 if (temp > arr[mid]) { begin = mid + 1; // 否则,范围也是缩小一半 } else { end = mid - 1; } // 循环结束时,end<begin,即i应该插入到begin所在的索引 } // 从begin到i,集体后移 for (int j = i; j > begin; j--) { arr[j] = arr[j - 1]; } // 插入i arr[begin] = temp; } }
测试:运行后控制台输出