排序算法——希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
  • 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

基本思想:
先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

算法过程演示如图,只标记了一组,每次按照一个步长进行分组,如第一组步长为5,则对arr[0]、arr[5]、arr[10]、arr[15]该分组内进行“插入排序”,这里只举例一组,arr[1]、arr[6]等不再赘述。每一次步长排序完成后,步长减少,再次进行分组的插入排序,直到步长为1,插入排序就是希尔排序中步长为1的排序,那么为什么还需要希尔排序呢?因为插入排序每一次都需要挪动数组内的元素,效率较差,而希尔排序因为步长在不断变化,导致整个数组中的元素基本已经在其正确的位置,前后差不出去几个位置,所以效率较高一些。算法复杂度为O(n log2 n)。
还有一个影响希尔排序算法效率的关键因素是步长,很多书籍中为了简单起见,都将步长设为了n/2,n为数组长度,这种步长是比较差的效率,现在多采用n=3*n+1的数列作为步长;关于步长: 步长的选择是希尔排序的重要部分。只要最终步长为1任何步长串行都可以工作。算法最开始以一定的步长进行排序。然后会继续以一定步长进行排序,最终算法以步长为1进行排序。当步长为1时,算法变为插入排序,这就保证了数据一定会被排序。
package sortAlgorithm;

public class ShellSort {
	private int[] theArray;
	private int nElems;
	
	public ShellSort(int max){
		theArray = new int[max];
		nElems = 0;
	}
	
	public void insert(int value){
		theArray[nElems++] = value;
	}
	
	public void display(){
		System.out.print("A=");
		for(int j=0;j<nElems;j++){
			System.out.print(theArray[j]+" ");
		}
		System.out.println("");
	}
	
	public void Shell(){
		int inner,outer,temp;
		
		int h = 1;
		while(h <= nElems / 3)
			h = h*3 + 1;
		while(h > 0){
			for(outer=h;outer<nElems;outer++){
				temp = theArray[outer];
				inner = outer;
				while(inner > h - 1 && theArray[inner-h] >= temp){
					theArray[inner] = theArray[inner-h];
					inner = inner - h;
				}
				theArray[inner] = temp;
			}
			h = (h - 1) / 3;
		}
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int maxSize = 10;
		ShellSort shell = new ShellSort(maxSize);
		for(int j=0;j<maxSize;j++){
			int n = (int)(Math.random()*99);
			shell.insert(n);
		}
		shell.display();
		shell.Shell();
		shell.display();
	}

}
其中,摘出其中一段,会发现,当h=1时,即步长为1时,以下这段代码把h换成1,它就是一段插入排序的代码,所以在理解的基础上,只要把插入排序的算法记住,再加上一段步长的算法公式,希尔排序的算法就很轻松的记住了。
for(outer=h;outer<nElems;outer++){
				temp = theArray[outer];
				inner = outer;
				while(inner > h - 1 && theArray[inner-h] >= temp){
					theArray[inner] = theArray[inner-h];
					inner = inner - h;
				}
				theArray[inner] = temp;
			}

附上插入排序的代码:对比~
/**
	 * 插入排序
	 * 默认arr[0]是一个已经排序好的数,虽然只有一个数,
	 * 第一轮开始,从第二个数arr[1]和arr[0]比较,如果小于arr[0],
	 * arr[0]就挪到arr[1],跳出内部循环,到外部循坏内部时,arr[0]已经被往后挪了(虽然他的值还有),
	 * 所以外部循环时再把被挤掉的数据放到arr[0]处
	 */
	public static int[] insertSort(int[] arr){
		for(int i=1;i<arr.length;i++){
			int temp = arr[i];
			int flag = i;
			while((temp < arr[flag-1]) && (flag > 0)){
				arr[flag] = arr[flag - 1];
				flag--;
			}
			arr[flag] = temp;
		}
		return null;
	}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值