插入排序
插入排序(Insert Sort)即从前向后,每取一个元素,将其放入到已经排列好的前面的队列之中,并保证加入该元素的队列仍然为有序的队列,直到取到最后一个元素并完整排序为止。
下面给出Java代码,
/**
* 插入排序
* @param data
*/
private static void insertSort(int[] data) {
if (null == data || data.length < 2) {
return;
}
for (int i = 1; i < data.length; i++) {
//如果算上当前元素前面的队列不是顺序的
if (data[i-1]>data[i]) {
//先将当前元素取出
int temp = data[i];
//要记住比较的元素位置
int j = i;
//从后向前检查元素,如果满足大于当前元素,那么依次向后移位
for (; j>0 && data[j-1]>temp; j--) {
data[j] = data[j-1];
}
//将当前元素放入合适的位置
data[j] = temp;
}
}
}
实际上,我们还有另外一种实现方式,它的每一轮外层循环的结果与插入排序的结果是一致的,但是其内部实现方式却是不一样的。我这里称其为伪插入排序,实际上它仍然是一种选择排序。其代码如下,
/**
* 伪插入排序,实际上是一种选择排序
* @param data
*/
private static void fakeInsertSort(int[] data) {
if (null == data || data.length < 2) {
return;
}
for (int i = 1; i < data.length; i++) {
//使前i+1个数有序
for (int j = 0; j < i; j++) {
//保证第i+1个位置一定是前i+1个元素的最大值
if(data[j]>data[i]){
swap(data, i, j);
}
}
}
}
/**
* 数组内 i 坐标元素与 j 坐标元素互换
* @param data
* @param i
* @param j
*/
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
选择排序始终保证第k轮外层循环的第k个位置上一定是后面length-k+1个元素的最优值,而这里则始终保证第k轮外层循环的第k个位置上一定是前k个元素的最优值,二者恰好相对称。因为该实现方式没有“插入”的特点,因此我不认为它是一种插入算法,而应该算作选择排序的一种。
同样的,我们也可以给出冒泡形式的伪插入排序,代码如下,
/**
* 伪插入排序(冒泡排序)
* @param data
*/
private static void fakeInsertSort2(int[] data) {
if (null == data || data.length < 2) {
return;
}
for (int i = 1; i < data.length; i++) {
//使前i+1个数有序
for (int j = i; j > 0; j--) {
if(data[j-1]>data[j]){
swap(data, j - 1, j);
}
}
}
}
/**
* 数组内 i 坐标元素与 j 坐标元素互换
* @param data
* @param i
* @param j
*/
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
折半插入排序
因为我们的插入排序算法始终保证插入某一数据元素之前,该元素之前的数据是有序的,因此,可以通过折半查找的方式获得插入元素的终点。折半插入排序的Java代码如下:
/**
* 折半插入排序
* @param data
*/
private static void binaryInsertionSort(int[] data) {
if (null == data || data.length < 2) {
return;
}
for (int i = 1; i < data.length; i++) {
//如果算上当前元素前面的队列不是顺序的
if (data[i-1]>data[i]) {
//先将当前元素取出
int temp = data[i];
//要记住比较的元素位置
int j = i;
//左端位置
int left = 0;
//右端位置
int right = i-1;
//中间位置
int mid = (left + right)/2;
//进行折半查找
while (left<right) {
if (data[mid]<=temp) {
left = mid+1;
}else {
right = mid;
}
mid = (left + right)/2;
}
//依次向后移位
for (; j>mid; j--) {
data[j] = data[j-1];
}
//将当前元素放入合适的位置
data[j] = temp;
}
}
}
/**
* 数组内 i 坐标元素与 j 坐标元素互换
* @param data
* @param i
* @param j
*/
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
折半插入排序相对于插入排序减少了插入判断的次数,但是数据交换次数并没有减少。