希尔排序
希尔排序(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序。希尔排序并不稳定,O(1)的额外空间,时间复杂度为O(N*(logN)^2)。最坏的情况下的执行效率和在平均情况下的执行效率相比相差不多。
希尔排序间隔序列函数 h = h * 3+ 1
希尔排序比插入排序快很多的原因:当h值很大时,数据项每一趟排序移动的元素个数少,但移动的距离很长,这是非常高效的;当h值减小时,每一趟排序移动的元素个数增加,但此时的数据项已经接近于他们最终排序后的位置,插入排序可以更有效
public class ShellSort {
static void sort(int[] array) {
int out, in, tmp;
int len = array.length;
int h = 1;
while(h < len / 3) // 计算间隔h最大值
h = h * 3 + 1;
while(h > 0){ // 能否继续通过缩小间隔h来分割数据列的判定
/*
* out为什么从h开始?你分割后的第一子序列应该是这样一个序列,0, h, 2h, 3h, ...
* 插入排序的while循环是从1开始的,因为第一个数始终有序,不需要比较,这个需要了解插入排序的算法,所以比较是从第二个数据线,就是数组的第h个下标开始
* out的判定为什么是out < len?
* 控制数组下标,下面的例子会说道
*
* 下面举一个例子来解释
* 假定有一个10个数据项的数组,数组下标从0 ~ 9 表示
* 当h = 4时的子序列情况是这样的,以下标表示
* (0 4 8)(1 5 9)(2 6)(3 7)
* 我第一次是这么理解的,真对每一组分别进行插入排序(当然也可以这样实现,但是下标不好控制),但是对下面的代码来说这是错误的理解。
* 正确的过程是这样的,外层for循环每次对每一分组的前两个数据项进行插入排序,然后前3个,然后前4个 ... 这个和子序列个数有关
* 排序过程只真对方括号进行
* 当out = 4时进行如下过程 ([0 4] 8)
* 当out = 5时([1 5] 9)
* 当out = 6时([2 6])
* 当out = 7时([3 7])
* 当out = 8时([0 4 8])
* 当out = 9时([1 5 9])
* h = 4执行完毕,然后h = (h - 1) / 3 = 1开始新的for循环
* h = 1时执行过程和h = 4时一样,不过这时的子数列就是原始的数列,蜕变为一个简单的插入排序,这是数组基本有序,数据项移动次数会大大减少
*
*/
for(out = h; out < len; out++){ // 外层通过out确定每组插入排序的第二个数据项
// 以下代码就是对子序列进行的插入排序算法
tmp = array[out];
in = out;
/*
* 比较插入排序while循环的写法,这里的while循环与h有关,所以判定就与h有关,包括 in -= h语句
* while(in > 0 && array[in - 1] > tmp){
* array[in] = array[in - 1];
* in--;
* }
* array[in] = tmp;
*
*/
while(in > h -1 && array[in - h] >= tmp){
array[in] = array[in - h];
in -= h;
}
array[in] = tmp;
}
// 缩小间隔
h = (h - 1) / 3;
}
}
}
快速排序
算法思想:基于分治的思想,是冒泡排序的改进型。首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(lo指向起始位置,hi指向末尾),首先从后半部分开始,如果发现有元素比该基准点的值小,就交换lo和hi位置的值,然后从前半部分开始扫秒,发现有元素大于基准点的值,就交换lo和hi位置的值,如此往复循环,直到lo>=hi,然后把基准点的值放到hi这个位置。一次排序就完成了。以后采用递归的方式分别对前半部分和后半部分排序,当前半部分和后半部分均有序时该数组就自然有序了。
快速排序的时间复杂度为O(NlogN).
public static int partition(int []array,int lo,int hi){
//固定的切分方式
int key=array[lo];//分组最右端为基准值
while(lo<hi){
while(array[hi]>=key&&hi>lo){//从后半部分向前扫描
hi--;
}
array[lo]=array[hi];
while(array[lo]<=key&&hi>lo){从前半部分向后扫描
lo++;
}
array[hi]=array[lo];
}
array[hi]=key;
return hi;
}
public static void sort(int[] array,int lo ,int hi){
if(lo>=hi){
return ;
}
int index=partition(array,lo,hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
快速排序的优化
对于基准位置的选取一般有三种方法:固定切分,随机切分和三取样切分。固定切分的效率并不是太好,随机切分是常用的一种切分,效率比较高,最坏情况下时间复杂度有可能为O(N2).对于三数取中选择基准点是最理想的一种。
下列代码替换每个分组基准值
//三数取中
int mid=lo+(hi-lo)/2;
if(array[mid]>array[hi]){
swap(array[mid],array[hi]);
}
if(array[lo]>array[hi]){
swap(array[lo],array[hi]);
}
if(array[mid]>array[lo]){
swap(array[mid],array[lo]);
}
int key=array[lo];
链接
希尔:http://blog.csdn.net/zhqingyun163/article/details/8961396
快排:http://www.cnblogs.com/coderising/p/5708801.html