插入排序算法
插入排序,一般也被称为直接插入排序。它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动
插入排序的精髓在与将元素序列分成有序表和无序表,有序表初始为第一个元素(只有一个元素当然有序),然后一个个判断无序表的第一个元素,将其插入有序表中,保证有序表一直有序,直到无序表为空结束
Java实现插入排序算法的过程
先一步步实现插入排序的过程
第一轮:
设置第一个待插入元素insertValue=arr[1]
,插入位置的索引初始值为insertIndex=0
(带插入元素的前一位)
while循环,查找当前待插入的元素是否小于insertIndex指向的元素,如果小于,就arr[insertIndex+1] = arr[insertIndex];
把arr[0]赋值给arr[1],然后insertIndex-1,继续查找,直到到达数组边界或者待插入的元素大于insertIndex指向的元素
退出while循环,把insertValue赋值给arr[insertIndex-1]
后续第二轮,直到无序表为空
package com.company.sort;
import java.util.Arrays;
/**
* @author zfk
* 插入排序
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
insertSort(arr);
}
public static void insertSort(int[] arr){
//第一轮
//定义待插入的数
int insertValue = arr[1];
//插入位置的索引,待插入数的前一位
int insertIndex = 1 - 1;
//循环,给insertValue找插入的位置
//1.保证给insertValue找插入的位置时不越界
//2. insertValue < arr[insertIndex] 待插入的数还没有找到插入位置
//3. 将arr[insertIndex]后移
while (insertIndex >= 0 && insertValue < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex];
insertIndex--;
}
//当退出while循环时,插入位置找到,位置为insertIndex+1
arr[insertIndex+1] = insertValue;
System.out.println("第一轮插入后:"+ Arrays.toString(arr));
//第二轮
//定义待插入的数
insertValue = arr[2];
//插入位置的索引,待插入数的前一位
insertIndex = 2 - 1;
//循环,给insertValue找插入的位置
//1.保证给insertValue找插入的位置时不越界
//2. insertValue < arr[insertIndex] 待插入的数还没有找到插入位置
//3. 将arr[insertIndex]后移
while (insertIndex >= 0 && insertValue < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex];
insertIndex--;
}
//当退出while循环时,插入位置找到,位置为insertIndex+1
arr[insertIndex+1] = insertValue;
System.out.println("第二轮插入后:"+ Arrays.toString(arr));
//第三轮
//定义待插入的数
insertValue = arr[3];
//插入位置的索引,待插入数的前一位
insertIndex = 3 - 1;
//循环,给insertValue找插入的位置
//1.保证给insertValue找插入的位置时不越界
//2. insertValue < arr[insertIndex] 待插入的数还没有找到插入位置
//3. 将arr[insertIndex]后移
while (insertIndex >= 0 && insertValue < arr[insertIndex]){
arr[insertIndex+1] = arr[insertIndex];
insertIndex--;
}
//当退出while循环时,插入位置找到,位置为insertIndex+1
arr[insertIndex+1] = insertValue;
System.out.println("第三轮插入后:"+ Arrays.toString(arr));
}
}
很明显,这也是可以通过两个for循环嵌套简化的
注意,因为是循环处理无序表,所以外面的这个for循环应当从1开始
package com.company.sort;
import java.util.Arrays;
/**
* @author zfk
* 插入排序
*/
public class InsertSort {
public static void main(String[] args) {
int[] arr = {101,34,119,1};
insertSort(arr);
}
public static void insertSort(int[] arr){
//仅循环无序表的大小,所以是从1开始
for (int i = 1;i < arr.length;i++) {
//定义待插入的数
int insertValue = arr[i];
//插入位置的索引,待插入数的前一位
int insertIndex = i - 1;
//循环,给insertValue找插入的位置
//1.保证给insertValue找插入的位置时不越界
//2. insertValue < arr[insertIndex] 待插入的数还没有找到插入位置
//3. 将arr[insertIndex]后移
while (insertIndex >= 0 && insertValue < arr[insertIndex]) {
arr[insertIndex + 1] = arr[insertIndex];
insertIndex--;
}
//当退出while循环时,插入位置找到,位置为insertIndex+1
arr[insertIndex + 1] = insertValue;
System.out.println("第"+i+"轮插入后:" + Arrays.toString(arr));
}
}
}
速度检测
很明显,插入排序算法是两个for循环嵌套,时间复杂度是O(n^2),那么同为平方阶的时间复杂度,冒泡排序、选择排序、插入排序哪个更快?
排序8000个0~80000内的数据
public static void main(String[] args) {
int[] arr = new int[8000];
for(int i = 0 ;i <arr.length;i++){
//随机生成80000内的整数
arr[i] = (int) (Math.random()*80000);
}
Date dataBefore = new Date();
insertSort(arr);
Date dateAfter = new Date();
System.out.println("消耗了:"+(dateAfter.getTime()-dataBefore.getTime())+"ms");
}
多次执行,大概速度在20ms左右
当设置为8万个数据:插入排序为910ms左右;选择排序为3400ms左右;冒泡排序为13000ms左右
总的来说:在数组为存储数据的情况下,插入排序 优于 选择排序 优于 冒泡排序
为什么会这样?
冒泡排序是都要遍历一遍,且很大可能需要交换多次,最耗时没得说;选择排序也是需要遍历数组,只是需要交换一次;插入排序是从有序表的最边上开始遍历,平均遍历是少于选择排序的
开销问题:
- 比较开销:选择排序的比较开销是固定的n(n-1)/2,而插入排序平均下来是
n(n-1)/4. - 交换开销选择排序最多只需要执行2*(n-1)次交换,而插入排序平均的交换开销也是n(n-1)/4.这就取决于单次比较和交换的开销之比。如果是一个量级的,则插入排序优于选择排序,如果交换开销远大于插入开销,则插入排序可能比选择排序慢