前言
折半插入排序算法是一种排序的算法,它通过“折半查找”在比较区查找插入点的位置,这样可以减少比较的次数,移动的次数不变,时间复杂度仍为
O(n^2);
算法描述
- 将第一个元素当做一个查找区的元素,刚开始这个查找区仅含ary[0](第一个元素)
- 取出下一个元素(这里循环获取,从第二个元素开始),和查找区的中间元素(半查法)比较。注:查找区里面的元素都排好序了
- 如果新的元素小于中间元素,将查找区的右边界(right变量表示)向左移动一位,如果大于等于中间元素,将左边界(left变量表示)左移一位
- 重复步骤3,直到左边界大于右边界
- 判断左边界是否在查找区里面,如果是就从找到的索引位置开始,到该比较元素索引位置结束,向后移动一位。如果在右边界外面,则忽略这一步
- 将找到的位置元素用比较的元素(第二步取出的数据)覆盖
- 重复第二步
提示:上面从第二步开始到第7步结束,每一次循环比较区里面的右边界都在递增,直到递增结束
算法描述图
以上是在开始写这篇文章的基础知识,如果还是看不懂没关系,我找到一个讲解比较好的链接,请看下面的参考资料
为什么写这篇文章
这篇文章与其说是来记录折半排序算法,还不如说是一次总结,从后面我贴出的代码可以看出我特意在封装类中应用了泛型、内部类、以及提供2个Comparable与Comparator的实现类调用的重载的排序方法,算是对自己前面阶段巩固java基础一次检验,我也希望能对看到这篇文章的人有所帮助,由于自身水平的关系,如有错误的地方,希望能指出。
正文
在前言中我描述了折半排序算法的步骤以及原理,我下面从一个实现该算法的代码入手来分析一下
public void binarySort(T[] obj , Comparator<T> comparator ) {
//定义折半插入算法所需的一些变量
int left, right, mid;
T curValue = null;
for( int start = 1; start < obj. length; start++) {
curValue = obj[ start];
left = 0;
right = start - 1;
//下面代码在比较区使用半查法找出可以插入的位置
while( left <= right) {
//使用位运算找出比较区中间位置
mid = ( left + right) >> 1;
if( comparator.compare( curValue, obj[ mid]) < 0) {
//当curValue小于obj[mid],右边界左移
right -= 1;
} else {
//这里需要注意一下,如果两个值相等也让左边界向左移动一位
left += 1;
}
}
//结束循环以后left = right + 1; 且 left <= start
//left就是需要插入元素的位置,moveStep表示将left - start之间的元素后移一位
int moveStep = start - left;
System. arraycopy(obj, left, obj, left+1, moveStep);
obj[ left] = curValue;
}
}
从上面我们可以看到代码有两个循环,第一个for循环是对应2-7步骤,第二个while循环对应2-4步骤,我们需要注意一下,start是从数组第二个元素开始的,第一个元素我设置成查找区间元素,右边界right和这个start有关,这样才能让查找区间不断的扩展。等while循环结束以后,表示我已经找到插入的位置了,接下去通过arraycopy函数,将符合条件的数组向右移动以后,然后用待比较数据去覆盖找到的索引上的值
我觉得有必要提醒一下就是半查法是针对已经排好序的数组,所以查找区间按照设定,不断扩张的以后,里面的数据也是排序好的
测试代码
package org.study.arithmetic;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;
public class BinarySortAc<T> {
/**
* 折半插入算法排序
* @param 外面传递一个需要排序的数组引用
*/
public void binarySort(T[] obj, Comparator<T> comparator) {
//定义折半插入算法所需的一些变量
int left, right, mid;
T curValue = null;
for( int start = 1; start < obj. length; start++) {
curValue = obj[ start];
left = 0;
right = start - 1;
//下面代码在比较区使用半查法找出可以插入的位置
while( left <= right) {
//使用位运算找出比较区中间位置
mid = ( left + right) >> 1;
if( comparator.compare( curValue, obj[ mid]) < 0) {
//当curValue小于ojb[mid],右边界左移
right -= 1;
} else {
//这里需要注意一下,如果两个值相等也让左边界向左移动一位
left += 1;
}
}
//结束循环以后left = right + 1; 且 left <= start
//left就是需要插入元素的位置,moveStep表示将left - start之间的元素后移一位
int moveStep = start - left;
System. arraycopy(obj, left, obj, left+1, moveStep);
obj[ left] = curValue;
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
/**
* 排序重载的方法,这里我无法在使用泛型了,因为泛型由于无法调用compareTo方法
* @param obj 需要比较的数据集合
*/
public void binarySort(Object[] obj) {
//定义折半插入算法所需的一些变量
int left, right, mid;
Comparable curValue = null;
for( int start = 1; start < obj. length; start++) {
curValue = (Comparable) obj[ start];
left = 0;
right = start - 1;
while( left <= right) {
mid = ( left + right) >> 1;
if( curValue.compareTo( obj[ mid]) < 0) {
right -= 1;
} else {
left += 1;
}
}
//结束循环以后left = right + 1; 且 left <= start
//left就是需要插入元素的位置,moveStep表示将left - start之间的元素后移一位
int moveStep = start - left;
System. arraycopy(obj, left, obj, left+1, moveStep);
obj[ left] = curValue;
}
}
@SuppressWarnings("unchecked")
/**
* @return 返回一个具体的集合
*/
public User<String>[] getList() {
User<String>[] users = new User[10];
for( int index = 0; index < 10; index++) {
User<String> user = new User<String>();
user.setName( "Abigail"+ index);
user.setAge( new Random().nextInt(100));
users[ index] = user;
}
return users;
}
/**
*
*
* @param <T> User使用外围类的泛型参数
*/
private static class User<V> {
private V name;
private int age;
public V getName() {
return name;
}
public void setName(V name) {
this. name = name;
}
public int getAge() {
return age;
}
public void setAge( int age) {
this. age = age;
}
public String toString() {
StringBuilder sbu = new StringBuilder();
return sbu.append( "[name:")
.append( name)
.append( ",age:")
.append( age)
.append( "]").toString();
}
}
/**
* @return 返回一个具体的比较器
*/
public Comparator<User<String>> getComparator() {
return new MyComparator();
}
/**
* 内部类实现一个User的比较器
* @param args
*/
private class MyComparator implements Comparator<User<String>>{
@Override
/**
* 用年龄来比较两个对象的大小
*/
public int compare(User<String> o1, User<String> o2) {
return o1.getAge() - o2.getAge();
}
}
public static void main(String[] args) {
BinarySortAc<Integer> ite = new BinarySortAc<Integer>();
Integer[] ites = new Integer[] {5,2,10,6,4,3,1,5,10};
System. out.format( "排序前%s\n", Arrays.toString(ites));
ite.binarySort( ites);
System. out.format( "排序后%s\n", Arrays.toString(ites));
BinarySortAc<User<String>> ite1 = new BinarySortAc<User<String>>();
User<String>[] users = ite1.getList();
System. out.format( "排序前%s\n", Arrays.toString(users));
ite1.binarySort( users, ite.getComparator());
System. out.format( "排序后%s\n", Arrays.toString(users));
}
}
参考资料
折半插入排序算法描述(左小右大)
http://wenku.baidu.com/link?url=16JgfPzd4KYlB8rMYULIINnnWSvTcAy916vgxlQpCE47H3D8x96k2wJx0CNaJ8urQyAQ70pKnHYVHeAxoeyK34tsKaasUgKkf5zSVgIZUJe