排序算法复习——插入类排序


排序的定义

假设数据集合S,一个排序算法sort的功能就是将S的任意元素序列重新排列成一个有序的序列。
显然,对于同一集合的实际数据来说,完全可能存在很多中不同但都有实际意义的序,而且在不同的计算情况下,可能需要对同一个数据序列做不同的排序。因此在对某些数据集做排序时,需要明确说明采用哪种序,比如常用的典型序:整数的“小于等于”、字符串的字典序等。


排序的基本操作、性质和分类

基本操作:(1)比较关键码的操作(这里的关键码指的是数据记录中支持排序的关键码,例如整数或字符串);(2)移动记录的操作,用于调整记录的位置。
性质:(1)稳定性:当待排序的序列中存在多个关键码相同的元素,如果在排序之后它们之间的相对位置不变,则称这种排序算法是稳定的;(2)适应性:排序操作可能会被用于处理不同长短、复杂程度不同的序列,如果一个排序算法对接近有序的序列工作得更快,则称这种算法具有适应性。
基于排序的基本操作方式或特点对排序算法分类:插入排序;选择排序;交换排序;分配排序;归并排序;外部排序(如果待排序的序列全都保存在内存中,就叫内排序,在磁盘等外存上的排序叫做外排序)。

插入类排序算法

逐个考察每个待排序的元素,将每一个新元素插入到前面已经排好序的序列中适当的位置上,使得新序列仍然是一个有序序列。

1、直接插入排序

对于具有n个元素的序列,从第二个元素开始直接到第n个元素,逐个向有序序列执行插入操作,如下图所示。

直接插入排序
代码实现:

import java.util.Arrays;
public class DirectInsert {
	public static void insertSort(int[] array,int low, int high) {
		for(int i=low+1;i<=high;i++) {
			System.out.print("Insert number:"+array[i]);
			if(array[i]<array[i-1]) {
				int temp = array[i];
				array[i] = array[i-1];
				int j = i-2;
				for(;j>=low && temp < array[j];j--) {
					array[j+1] = array[j];
				}
				array[j+1] = temp;
			}
			System.out.println(" result is"+Arrays.toString(array));
		}
		System.out.println("final:"+Arrays.toString(array));
	}
	public static void main(String[] args) {
		int[] array = {26,53,48,11,13,48,32,15};
		insertSort(array,0,array.length-1);
	}
}

运行结果:
运行结果

直接插入排序的时间复杂度为O(n^2),并且是一个稳定的排序方法。

2、折半插入排序

由于插入排序中需要检索元素的插入位置,而且是有序的部分序列里检索,因此可通过二分查找来检索插入位置。

代码实现:

public void binInsertSort(int[] array,int low,int high) {
		for(int i=low+1;i<=high;i++) {
			System.out.print("Insert number:"+array[i]);
			int temp = array[i]; //待插入的元素
			int left = low, right = i-1;
			while(left<=right) { //折半查找确定位置
				int mid = (left+right)/2;
				if(array[mid]<temp) {
					left = mid+1;
				} else {
					right = mid-1;
				}
			}
			//移动元素
			for(int j=i-1;j>right;j--) {
				array[j+1] = array[j];
			}
			array[right+1] = temp; //进行插入
			System.out.println(" result is"+Arrays.toString(array));
		}
		System.out.println("final:"+Arrays.toString(array));
	}

尽管折半插入排序减少了元素比较的次数,但是并没有减少元素移动的次数,因此折半插入排序的时间复杂度仍为O(n^2)

3、希尔排序

首先将待排序的元素分为多个子序列,使得每个子序列的元素相对较少,对各个子序列分别进行直接插入排序,待整个待排序列基本有序后,再对所有元素进行一次直接插入排序。
以序列{26,53,67,48,57,13,48,32,60,50}为例,排序过程如下:

  • 选择一个步长序列,步长逐渐减小,最后一个步长必须为1,假设我们选择的步长序列为{5,3,1}。
  • 按照步长序列个数k,对待排序元素序列进行k趟排序,因为我们假设的步长序列长度为3,因此需要进行3趟排序。
  • 每趟排序,根据对应的步长t,将待排序列分割成t个子序列,分别对各个子序列进行直接插入排序,例如第一个步长为5,所以在第一趟排序中将待排序列分成5个子序列{26,13},{53,48},{67,32},{48,60},{57,50},对它们分别进行直接插入排序,然后以此类推,进行二、三趟排序,得到最终有序的序列。
    希尔排序如下图所示:
    希尔排序

代码实现:

//对于步长为t的所有序列进行直接插入排序
public void shellInsert(int[] array,int low,int high,int t) {
		for(int i=low+t;i<=high;i++) {
			if(array[i]<array[i-t]) {
				int temp = array[i];
				int j = i-t;
				for(;j>=low && temp<array[j];j-=t) {
					array[j+t] = array[j];
				}
				array[j+t] =temp;
			}
		}
	}
	public void shellSort(int[] array,int low,int high,int[] tArray) {
		for(int k=0;k<tArray.length;k++) {
			shellInsert(array,low,high,tArray[k]); //第k趟排序
			System.out.println(k+"th turn:"+Arrays.toString(array));
		}
		System.out.println("final:"+Arrays.toString(array));
	}

从直观上看希尔排序的效率会高于直接插入排序,但是其时间复杂度与步长序列的选取密切相关,如何选择步长序列才能使得希尔排序的时间复杂度达到最佳,还是一个有待解决的问题。
在实际应用中,应使得步长序列中的步长互质。

参考

数据结构与算法(JAVA语言版—周鹏)
如有侵权,请联系作者删除

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值