插入类排序算法总结(代码+数据测试+稳定性分析)


         资料表明,在当今计算机上,排序占用计算机CPU时间已经高达30%—50%,排序是计算机程序设计中的一种基础性操作,研究以及掌握各种排序算法非常重要,今天就总结一下插入类的三种排序算法
         下面主要是插入类排序算法的代码总结以及测试对比。其实描述一个算法用文字是相对比较抽象的,如果对这些算法不太熟悉,建议对于排序算法的具体学习推荐B站韩顺平老师讲的《数据结构与算法》,非常详细!
                插入类排序
                      a.直接插入排序基本操作:将第i个记录插入到前面i-1已经排好的记录中。代码如下

	// 直接插入排序算法
	static void inSort(int r[]) {
		int j;
		int r0;
		for (int i = 1; i < r.length; i++) {
			// r0备份待插入的记录
			r0 = r[i];
			// 此时j指向前面有序序列的最后一个记录
			j = i - 1;
			// for循环寻找应该插入的位置
			for (; j >= 0; j--) {
				// 如果待插入的记录小于r[j],则将r[j]向后移
				// 否则跳出循环
				if (r0 < r[j]) {
					r[j + 1] = r[j];
				} else
					break;
			}
			// 程序执行到这一步,意味着r0>r[j],则应该将r0赋给r[j+1]
			r[j + 1] = r0;

		}

	}


                  

 最好的情况(顺序):待排序的数据为有序的序列,此时每次内部循环只执行一次,且不移动记录,时间复杂度为O(n)

 最坏的情况(逆序):此时平均时间复杂度为O(n^2)

空间复杂度:在该算法当中,只需要一个辅助的空间r0,所以复杂度为O(1)

稳定性:稳定,待插入元素的比较是从后向前执行的,相同的关键字是没有必要进行交换的,所以相同的元素不会换到前面,所以是稳定的。

测试数据:(百万以上数据规模在该算法中过慢,没有纳入测试范围...)

      b.折半插入排序:此算法对直接插入算法做了优化,因为,对于有序表进行折半查找,性能是优于顺序查找的,所以折半查找的思想可以用于有序记录r[0...j-1]。代码如下

	
	//折半插入算法
	static void halfInSort(int[] r) {
		//定义三个边界下标值,分别记录插入的上界与下界
		int low, high, mid;
		//r0备份待插入的记录
		int r0;
		//从第二个记录开始遍历
		for (int i = 1; i < r.length; i++) {
			r0 = r[i];
			low = 0;
			high = i - 1;
			//while循环寻找插入的位置,当low=high,即找到位置的时候跳出循环
			while (low <= high) {
				//mid为中间下标
				mid = (low + high) / 2;
				//如果待插入的记录小于中间值
				if (r0 <= r[mid])
					//则将上界往前一半调
					high = mid - 1;
				else
					//否则将下界往上调
					low = mid + 1;
			}
			//程序执行到这里,说明已经找到该插入的位置,即下标值为low的地方
			for (int j = i - 1; j >= low; j--) {
				//将low到i-1中间的元素全部向后移
				r[j + 1] = r[j];
			}
			//元素移动完成之后,将待插入记录赋给r[low]
			r[low] = r0;
		}
	}

 折半查找可以减少比较的次数,平均关键字比较次数为nlog2n,n较大的时候,折半查找会比直接插入排序的最差情况要好得多,但比其最好情况要差,由于该算法只是改善了比较次数,在最坏的情况下,比较时间复杂度为O(nlogn),但移动元素的时间消耗没有减少,所以总的时间复杂度为O(n^2),在最好情况下,不需要移动,时间复杂度为O(nlogn)。同理,该算法是稳定的,空间复杂度O(1)。

数据测试:

数据规模

1000

10000

100000

时间/s

0.012324161

0.110314439

9.594870053

 还是好了些的~

     c.希尔排序:在直接插入排序中,如果较小的元素出现在后面,则需要花比较多的比较以及移动次数才能将其移到前面,效率比较低,而希尔排序可以通过增量解决这个问题,即使小的元素在后面,也能够“跳”到前面。

         个人理解,这是一种“宏观调控”的排序算法,先将待排序的序列分割成若干个“较稀疏”的子序列,对这些子序列进行直接插入排序。然后经过这样的调整,,整个序列已经基本有序了,最后对全部记录进行最后一次直接插入排序,利用了直接插入排序算法的最好情况。代码如下

public static void shellInsort(int[] r) {
		int r0;
		int j;
		//delta为增量,这里选择了d=[n/2],d=[d/2],直到d=1,当然,还有其他更多的选择
		for (int delta = r.length / 2; delta > 0; delta /= 2) {
			//直接让i从delta开始循环,每次让i+1
			for (int i = delta; i < r.length; i++) {
				//注意这里是i - delta,而不是i-1,因为在希尔排序算法中,元素是跳着比的,跳跃长度为当前增量delta
				if (r[i] < r[i - delta]) {
					//备份当前待插入的元素r[i]
					r0 = r[i];
					//寻找插入的位置,与直接插入排序有所不同的是,此时不是一个一个移,而是跳着移
					for (j = i - delta; j >= 0 && r0 < r[j]; j -= delta)
						r[j + delta] = r[j];
					r[j + delta] = r0;
				}
			}
		}
	}

    时间复杂度:O(n^1.5)   空间复杂度O(1)

稳定性:希尔排序是不稳定的,举个例子

例如,有待排序序列{2,4,1,2},采用希尔排序,设d1=2,则有

 得到一趟排序结果为{1,2,2,4},之前在后面的2换到了前面2的前面(不要在意这里的箭头),所以希尔排序是不稳定的

数据测试:

数据规模

时间/s

1000

0.002278115

10000

0.013082773

100000

0.06379576

1000000

0.557292275

10000000

8.554733172

 注意当数据规模变成10万时,希尔排序只用了0.06379576秒,比前面的折半插入排序快了100多倍呀,有木有感觉到算法的神奇

噢噢,最后附上我的测试代码

public static void main(String[] args) {
		// TODO Auto-generated method stub
		int n = 10000000;//数据规模
		int r[] = new int[n];
		Random rom = new Random();
		for (int i = 0; i < n; i++) {
			r[i] = rom.nextInt(Integer.MAX_VALUE);
		}
		long time1 = System.nanoTime();
		//halfInSort(r);
		 //inSort(r);
		shellInsort(r);
		long time2 = System.nanoTime();
		double time = (time2 - time1) / 1000000000.0;
		System.out.println(time);
	}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值