`

java实现插入排序(直接插入、二分查找插入、希尔排序)

阅读更多

直接插入排序


        直接插入排序的原理:先将原序列分为有序区和无序区,然后再经过比较和后移操作将无序区元素插入到有序区中,

        下面表格中,排序之前取第一个元素为有序区10,无序区9到4,带颜色为有序区。

 

待排序数组 10 9 8 7 6 5 4
第一次排序 9 10 8 7 6 5 4
第二次排序 8 9 10 7 6 5 4
第三次排序:详细步骤              
1:将7拿出来 7            
2:7跟10比较后 8 9   10 6 5 4
3:7跟9比较后 8   9 10 6 5 4
4:7跟8比较后   8 9 10 6 5 4
5:将7插入 7 8 9 10 6 5 4

 

 

            找到了一个比较直观的图片

来自维基百科

 

	/**
	 * 直接插入排序
	 * 
	 * @param ts
	 */
	public static <T extends Comparable<? super T>> void insertionSort(T[] ts) {
		int x;
		// 有序区:ts[0]
		// 无序区:ts[1]到ts[ts.length-1]
		for (int i = 1; i < ts.length; i++) {
			T tem = ts[i];
			// 从数组的ts[i]元素开始,向前(有序区)比较
			for (x = i; x > 0 && tem.compareTo(ts[x - 1]) < 0; x--)
				ts[x] = ts[x - 1]; // 如果已排序的元素大于新元素,将该元素移到下一位置
			ts[x] = tem; // 将 ts[i] 放到正确位置上
		}
	}

 

1.时间复杂度:O(n^2)


       直接插入排序耗时的操作有:比较+后移赋值。时间复杂度如下:

1)   最好情况:序列是升序排列,在这种情况下,需要进行的比较操作需(n-1)次。后移赋

      值操作为0次。即O(n)

2)   最坏情况:序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。后移赋值操作是

      比较操作的次数加上 (n-1)次。即O(n^2)

3)   渐进时间复杂度(平均时间复杂度):O(n^2)

2.空间复杂度:O(1)


        从实现原理可知,直接插入排序是在原输入数组上进行后移赋值操作的(称“就地排序”),所需开辟的辅助空间跟输入数组规模无关,所以空间复杂度为:O(1)

 

3.稳定性

         直接插入排序是稳定的,不会改变相同元素的相对顺序。

4.优化改进

1)   二分查找插入排序:因为在一个有序区中查找一个插入位置,所以可使用二分查找,减

      少元素比较次数提高效率。

2)   希尔排序:如果序列本来就是升序或部分元素升序,那么比较+后移赋值操作次数就会

      减少。希尔排序正是通过分组的办法让部分元素升序再进行整个序列排序。(原因是,

      当增量值很大时数据项每一趟排序需要的个数很少,但数据项的距离很长。当增量值减小

      时每一趟需要和动的数据增多,此时已经接近于它们排序后的最终位置。)

 

二分查找插入排序

 

        二分查找插入排序的原理:是直接插入排序的一个变种,区别是:在有序区中查找新元

素插入位置时,为了减少元素比较次数提高效率,采用二分查找算法进行插入位置的确定。

 

       注意下面,在用二分法查到插入位置时,移动已排序元素时,用java的本地方法copy数组,比用for循快很多。当然当数据量过10w跟希尔排序还不是一个级别

	/**
	 * 二分查找插入排序
	 * 移位用一般for循环:13-14行 
	 * 移位用java类库中本地方法  16行
	 * @param ts
	 */
	public static <T extends Comparable<? super T>> void insertionBinarySort(T[] ts) {
		// 有序区:ts[0]
		// 无序区:ts[1]到ts[ts.length-1]
		for (int i = 1; i < ts.length; i++) {

			T tem = ts[i]; // 待插入元素

			int index = getStartByBinary(ts, i - 1, tem); // 插入位置

//	        for (int x = i; x > index; x--)// 插入位置 到 已排序边界 统一后移一位  
//	            ts[x] = ts[x - 1];  

			// 将index后面i-index个元素(包含index) 移到 index+1后面(包含index+1)
			System.arraycopy(ts, index, ts, index + 1, i - index);

			ts[index] = tem; // 将待插入元素放到index位置上
		}
	}

	/**
	 * 查找待排序元素该插入位置
	 * @param ts数组
	 * @param i 已排序边界(不包括待排元素位置)
	 * @param t 待排元素
	 * @return  应该插入位置
	 */
	private static <T extends Comparable<? super T>> int getStartByBinary(T[] ts, int i, T t) {
		int index = 0, end = i;//每次查找的范围 index上界,end下界
		while (index <= end) {
			int binary = (index + end) / 2;
			if (t.compareTo(ts[binary]) < 0)
				end = binary - 1;
			else
				index = binary + 1;// 可插入位置 0~i+1
		}
		return index;
	}

  

1. 时间复杂度

           for循环: O(n^2)

           java本地方法copy:不确定

 

        二分查找插入位置,因为不是查找相等值,而是基于比较查插入合适的位置,所以必须

查到最后一个元素才知道插入位置。


        二分查找最坏时间复杂度:当2^X>=n时,查询结束,所以查询的次数就为x,而x等于log2n(以2为底,n的对数)。即O(log2n)


所以,二分查找排序比较次数为:x=log2n

 

二找插入排序耗时的操作有:比较 + 后移赋值。时间复杂度如下:

1)  最好情况:查找的位置是有序区的最后一位后面一位,则无须进行后移赋值操作,其比较

     次 数为:log2n  。即O(log2n)

2)  最坏情况:查找的位置是有序区的第一个位置,则需要的比较次数为:log2n,需要的赋

     值操作次数

     for循环: n(n-1)/2加上 (n-1) 次。即O(n^2)

     java本地方法copy:不确定


3)  渐进时间复杂度(平均时间复杂度)

     for循环:O(n^2)

     java本地方法copy:不确定


2. 空间复杂度:O(1)


         从实现原理可知,二分查找插入排序是在原输入数组上进行后移赋值操作的(称“就地

排序”),所需开辟的辅助空间跟输入数组规模无关,所以空间复杂度O(1)


3. 稳定性

二分查找排序是稳定的,不会改变相同元素的相对顺序。

 

 

希尔缩小增量排序(可能有更好的增量)

 

       希尔排序名称来源于它的发明者Donald Shell , 该算法是冲破二次时间屏障的第一批算法之一。不过直到它最初被发现的若干年后才证明了它的亚二次时间界。

       希尔排序使用一个序列h1<h2<h3.....ht,叫做增量序列只要h1=1,任何增量序列都是可行的。不过有些增量序列比另外一些增量序列更好(这里不讨论)。
       希尔排序又叫缩减增量排序,即增量顺序为ht,ht-1.....h2,h1,h1必须等于1(对每个元素进行插入排序,以保证数组最终排序)。下图10个元素进行希尔排序 h3=10/2=5h2=5/2=2h1=2/2=1
                                                                        此图为希尔排序代码执行顺序

 

	/**
	 * 希尔排序
	 * @param ts
	 */
	public static <T extends Comparable<? super T>> void shellsort(T[] ts) {
		int j;
		for (int gap = ts.length / 2; gap > 0; gap /= 2) {//增量计算(分组)
			for (int i = gap; i < ts.length; i++) { //对每个组排序
				T tem = ts[i];
				//对组进行插入排序
				for (j = i; j >= gap && tem.compareTo(ts[j - gap]) < 0; j -= gap)
					ts[j] = ts[j - gap];
				ts[j] = tem;
			}
		}
	}
 

 
1.  平均时间复杂度:希尔排序的时间复杂度和其增量序列有关系,这涉及到数学上尚未解决的难题;不过在某些序列中复杂度可以为O(n1.3);

希尔排序耗时的操作有:比较 + 后移赋值。时间复杂度如下:

1)  最好情况:序列是升序排列,在这种情况下,需要进行的比较操作需(n-1)次。后移赋值

     操 作为0次。即O(n)

2)   最坏情况:O(n^2)

3)   渐进时间复杂度(平均时间复杂度):O(n1.3)

 2.  空间复杂度:O(1)

       从实现原理可知,希尔排序是在原输入数组上进行后移赋值操作的(称“就地排序”),所需开辟的辅助空间跟输入数组规模无关,所以空间复杂度为:O(1)

3. 稳定性

 

        希尔排序是不稳定的(上面表格元素5,起始位置[5],先被移动到位置[0],排序结束在位置[4])。因为在进行分组时,相同元素可能分到不同组中,改变相同元素的相对顺序。

 

 

简单性能测试

 

	/**
	 * 简单的性能测试
	 * @param args
	 */
	public static void main(String[] args) {
		int num = 10000000;
		Integer[] inters = new Integer[num];
		Random inter = new Random();
		for (int i = 0; i < num; i++) {
			inters[i] = inter.nextInt(num);
		}
		long tims = System.currentTimeMillis();
		shellsort(inters);
		tims -= System.currentTimeMillis();
		System.err.println(tims * -1);
	}

 

 

排序方法\数组大小 1000 5000 10000 50000 10w 100w 1000w
直接插入排序 17ms 50ms 130ms 2.4s 10s 太长 太长
二分for循环 8ms 43ms 60ms 950ms 5s 太长 太长
二分java本地copy 1ms 10ms 23ms 165ms 470ms 50s 太长
希尔增量排序 2ms 11ms 20ms 38ms 55ms 1s 20s

 

 

 

  
  • 大小: 221.5 KB
6
8
分享到:
评论

相关推荐

    java实现常见排序算法

    折半插入排序是在直接插入排序的基础上优化了查找插入位置的过程,采用二分查找的方法来减少比较次数。这种方法在数据量较大的情况下能显著提高效率,但总体时间复杂度仍然保持在O(n^2)。 3. **希尔排序**: 希尔...

    数据结构中的直接插入排序

    在给定的`Sort`文件中,可能包含了实现直接插入排序的源代码,通常使用C、C++、Java或Python等编程语言。这些程序会包含一个循环结构,遍历数组并进行比较、移动和插入操作。通过阅读和理解这些代码,可以加深对直接...

    Java实现排序算法

    二分排序是对插入排序的一种优化,当插入新元素时,使用二分查找确定其正确位置,减少了插入操作的次数,提高了效率。虽然插入排序在处理小规模数据时性能良好,但在大规模数据中,二分插入排序能更快地达到目标。 ...

    Java实现遍历、排序、查找算法及简要说明.docx

    - 虽然文档中没有具体讨论查找算法,但在Java中,常见查找算法包括线性查找、二分查找、哈希查找等。线性查找适用于未排序或部分排序的数据,而二分查找适用于有序数组,哈希查找则通过哈希表提供快速的查找能力。 ...

    java实现数据结构内部排序.pdf

    折半插入排序是在直接插入排序的基础上优化了查找插入位置的过程,它使用二分查找的方法来确定待插入元素应该插入的位置,从而减少了比较的次数。在Java代码中,`BInsertSort`方法首先将待插入元素存储在数组的第一...

    Java实现遍历、排序、查找算法及简要说明

    这里提到了插入类排序,包括直接插入排序、折半插入排序和希尔排序。 2.2.1 直接插入排序:基本思想是将每个元素插入到已排序的序列中的正确位置。当新元素小于已排序序列的元素时,会进行一系列的交换。在提供的...

    java各种数组排序插入交换选择归类基数排序.pdf

    折半插入排序则是通过二分查找来确定插入位置,减少了比较的次数。希尔排序是一种改进的插入排序,它通过设置间隔序列来减少元素的移动次数。 2. 交换排序: 包括冒泡排序和快速排序。冒泡排序通过不断交换相邻的...

    JAVA排序汇总 java应用中一些比较经典的排序算法

    直接插入排序是将每个元素逐个插入已排序部分,而折半插入排序则是利用二分查找减少比较次数。希尔排序是插入排序的改进版,通过设置间隔序列来优化插入过程。 2. **交换排序**: 包括冒泡排序和快速排序。冒泡...

    java各种数组排序(插入,交换,选择,归类,基数排序).pdf

    折半插入排序在直接插入的基础上引入二分查找来减少比较次数;希尔排序则是对插入排序的优化,通过间隔序列来减少元素移动次数。 - 交换排序:主要包含冒泡排序和快速排序。冒泡排序通过不断交换相邻的逆序元素实现...

    java实现各种排序

    以下是一个简单的二分查找实现: ```java public int binarySearch(int[] array, int target) { int left = 0; int right = array.length - 1; while (left ) { int mid = left + (right - left) / 2; if ...

    这是直接插入排序的代码

    在实际应用中,我们可以通过优化插入操作,例如使用二分查找来确定插入位置,来提高直接插入排序的效率。这种改进被称为二分插入排序,它将插入操作的平均时间复杂度降低到O(log n),但最坏情况的时间复杂度仍然是O...

    8中排序算法(java实现)

    - **二分法插入排序**:改进了直接插入排序,通过二分查找确定插入位置,减少了比较次数,提高了效率。 - **希尔排序**:基于插入排序,通过将元素按照一定间隔分组进行排序,然后逐渐减小间隔,最后整个数组完成...

    Java各种排序算法

    - 时间复杂度为O(n^2)的有:直接插入排序、冒泡排序和简单选择排序,其中以直接插入为最好,特别是对于关键字近似有序的记录序列。 - 时间复杂度为O(n)的排序方法只有基数排序。当待排序记录序列按关键字顺序有序...

    java各种数组排序(插入,交换,选择,归类,基数排序).docx

    - **折半插入排序**:改进版的直接插入排序,通过二分查找的方式减少比较次数。 - **希尔排序**:通过将数组分成多个子序列分别进行直接插入排序来提高效率。 #### 交换排序 - **冒泡排序**:简单直观,但效率较低...

    Java排序Java排序.doc

    - 折半插入排序:在插入过程中采用二分查找找到插入位置,减少了比较次数,但整体时间复杂度仍为O(n^2)。 - 希尔排序:通过增量序列将待排序元素分组,然后对各组进行插入排序,逐步减少增量,提高效率。 2. 交换...

Global site tag (gtag.js) - Google Analytics