插入排序(直接插入排序、折半插入排序和希尔排序)

插入排序是一种简单直观的排序方法,其基本思想是在于每次将一个待排序的记录, 按其关键字大小插入到前面已经排好序的子序列中,直到全部记录插入完成。

    插入排序可分为:直接插入排序、折半插入排序和希尔排序。

1、直接插入排序

      插入排序在实现上通常采用就地排序(空间复杂度为O(1)),因此在从后面的比较过程中,需要反复的把已经排好序的元素逐步向后挪位,为新元素插入空间。

public class InsertSort {

	public static void main(String[] args) {
		int[] arr = new int[]{1, 2, 3, 8, 6, 7, 9};
		insertSort(arr);
	}
	public static void insertSort(int[] arr) {
		int i, j, temp;
		for (i = 1; i < arr.length; i++) {
			if(arr[i] < arr[i-1]) {
				temp = arr[i]; // temp为哨兵
				for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
					arr[j+1] = arr[j];
				}
				arr[j+1] = temp;
			}
		}
		System.out.println(Arrays.toString(arr));
	}
}

    空间效率:仅使用了常数个辅助空间,因而空间复杂度为O(1)

    时间效率:在排序过程中,向有序子表中逐个的插入元素的操作进行了 n-1 趟,每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。

    在最好的情况下,表中的元素已经有序,此时每插入一个元素,都只需比较一次而不用移动元素,因此时间复杂度为O(n).

    在最坏的情况下,表中的元素顺序刚好与排序结果中的元素顺序相反(逆序)时,总的比较次数达到最大 ,总的移动数也达到最大。

    平均情况下,考虑待排序的表中的元素是随机的,此时可以取上述最好与最坏的平均值作为平均情况下的时间复杂度,总的比较次数与总的移动次数约为n^(2)/4。

    由此,直接插入排序的时间复杂度为O(n^(2)),虽然折半查找排序算法的时间复杂度也有O(n^(2)),但对于数据量比较小的排序表,折半插入排序往往能表现出很好的性能。

    直接插入排序是一个稳定的插入排序。适用于顺序排序存储和链式存储的线性表,当为链式存储时,可以从前往后查找指定元素的位置。注意:大部分排序算法都仅适用于顺序存储的线性表。

2、折半插入排序

直接插入排序中,总是边比较边移动元素。下面将比较和移动查找分离开来,即先折半查找出元素的待插入位置,然后再统一的移动待插入位置之后的所有元素。当排序表为顺序存储的线性表时,可以对直接插入排序算法作如下改进:

    由于是顺序存储的线性表,所以查找有序子表时可以用折半查找来实现。在确定出待插入位置后,既可以统一的向后移动元素了。

public class InsertSort {
	public static void main(String[] args) {
		int[] arr = new int[]{1, 2, 3, 8, 6, 7, 9};
		insertSort(arr);
	}
	
	public static void insertSort(int[] arr) {
		int i, j, high, low, mid, temp;
		
		for (i = 1; i < arr.length; i++) {
			low = 0;
			high = i - 1;
			temp = arr[i];
			while(low <= high) {
				mid = (low + high)/2;
				if(temp < arr[mid]) {
					high = mid - 1;
				}else {
					low = mid + 1;
				}
			}
			
			for (j = i-1; j > high; j--) {
				arr[j+1] = arr[j];
			}
			arr[high+1] = temp;
		}
		System.out.println(Arrays.toString(arr));
	}
} 

不难看出折半插入排序仅仅是减少了比较元素的个数,约为O(Nlog2N),该比较次数与待排序表的初始状态无关,仅仅取决于表中的元素个数n;而元素移动的次数并没有改变,他依赖于待排序表的初始状态。因此,折半插入排序的时间复杂度仍为O(n2)。折半插入排序是一个稳定的排序方法。

3、希尔排序

    直接插入排序适用于基本有序的排序表和数据量不大的排序表。基于这两点,提出了希尔排序,又称为缩小增量排序。

    希尔排序的基本思想是:先将待排序表分割成若干个子表,分别进行直接插入排序,当整个表中的元素已呈“基本有序”时,再对全体记录进行一次直接插入排序。过程如下:

    先取一个小于n的步长d1,把表中的全部记录分成d1个组,所有的距离为d1的倍数的记录放在同一组中,在各组中进行直接插入排序;然后取第二个步长d2<d1,重复上述过程,直到所取到的dt=1;即所有的记录已放在同一个组中,再进行直接插入排序,由于此时已经具有较好的局部有序性,故很快可以得到结果。希尔排序提出的增量序列是:d1=n/2,d(i+1)=di/2(向下取  整),并且最后一个增量为1。

public class ShellSort {

	@Test
	public void shellSortTest() {
		int[] arr = new int[]{1, 10, 15, 2, 3, 8, 6, 7, 9};
		shellSort(arr); 
	}
	
	public void shellSort(int[] arr) {
		int length = arr.length;
		int k = 0;
		for (int i = length/2; i > 0; i=i/2) {
			// 对子表进行插入排序
			for (int j = i; j < length; j++) {
				if(arr[j] < arr[j-i]) {
					int temp = arr[j];
					for (k = j-i; k>-1 && temp<arr[k]; k-=i) {
						arr[k+i] = arr[k];
					}
					arr[k+i] = temp;
				}
			}
		}
		System.out.println(Arrays.toString(arr));
	}
}

空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1);

时间效率:由于希尔排序的时间复杂度依赖于增量序列的函数,这涉及数学上尚未解决的难题,所以其时间复杂度分析比较困难。当n在某个特定范围内,希尔排序的时间复杂度约为O( n^(1.3)),在最坏的情况下时间复杂度是O(n^(2))。

希尔排序是不稳定的排序算法,仅适用于当线性表为顺序存储的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值