详谈插入排序(Java语言版)

排序:

 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。

  内部排序:数据元素全部放在内存中的排序。

  外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

稳定性:对于相同的元素,在排序前后其相对位置保持不变。即在原序列中,r[i]=r[j],且r[i]在r[j]之前,在

              排序完成后,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

七大排序根据功能型分类:插入排序: 直接插入排序(插入排序)、希尔排序 

                                           选择排序: 直接选择排序(选择排序)、堆排序

                                           冒泡排序

                                           快速排序

                                           归并排序

今天,我们在这里只讨论插入排序中的两种:直接插入排序,希尔排序

一、直接插入排序

插入排序相当于“抓牌”,每次将抓起的一张牌往有序的部分里插入

1.从第一个元素开始,该元素可以认为已经被排序

2.取出下一个元素,在已经排序的元素序列中从后向前扫描

3.如果该元素(已排序)大于新元素,将该元素移到下一位置

4.重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

5.将新元素插入到该位置

6.重复步骤2~5

 

 

 

详谈插入排序(Java语言版)_第1张图片

 

 

1、最外层循环:一次让一个数有序,所以需要length              有序 [ 0,i)

                                                                                                  无序[ i ,length)

       (1) 在有序区间,为array[ i ] 找一个合适的位置(保证稳定性)

                       直接遍历

代码如下:

01、找和搬分开

public static void insertSort_01(int[] array){
        for (int i = 0;i < array.length;i++){
            /**
             * 有序:[0,i)
             * 无序:[i,array.length)
             */
            //在有序区间内遍历查找,从后往前
            int j;
            for (j = i -1; j >= 0 && array[i] < array[j]; j--){

            }
            //j+1 就是要插入的下表
            //插入数据,从后往前搬
            int key = array[i];
            for (int k = i; k > j +1; k--){
                array[k] = array[k -1];
            }
            array[j +1] = key;
        }
    }

02、边找边搬*

//查和搬同时进行
    public static void insertSort_02(int[] array){
        for (int i = 0; i < array.length; i++){
            int key = array[i];
            int j = i -1;
            for (; j >= 0 && key < array[j]; j--){
                array[j +1] = array[j];
            }
            array[j +1] = key;
        }
    }

03、二分查找

        如果比较操作的代价比交换操作大的话,可以采用“二分查找法”来减少比较操作的数目。该算法可以认为是插入排序的一个变种,称为二分查找插入排序。

//二分查找
    public static void insertSort_03(int[] array){
        for (int i = 0; i < array.length; i++){
            int key = array[i];
            //有序[0,i)
            int left = 0;
            int right = i;

            while (left < right){
                int mid = left + (right - left)/2;
                if (key == array[mid]){
                    //为了追求稳定性
                    left = mid +1;
                }else if(key < array[mid]){
                    //下一次查找范围:[left,mid)
                    right = mid;
                }else{
                    left = mid +1;
                }
            }
            int pos = left;
            for (int k = i; k > pos; k--){
                array[k] = array[k -1];
            }
            array[pos] = key;
        }
    }

插入排序总结:时间复杂度:最坏:O(n^2)   已经逆序
                                               平均:O(n^2)
                                               最好:O(n^2)   已经有序
                         空间复杂度:O(1)
                         稳定性:稳定

 

二、希尔排序

        在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。

       希尔排序是对直接插入排序的优化

        当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比

 

 

详谈插入排序(Java语言版)_第2张图片

 

代码如下:

private static void insertWithGap(int[] array,int gap){
        for(int i = 0;i = 0 && key < array[j]; j= j -gap){
                array[j +gap] = array[j];
            }
            array[j +gap] = key;
        }
    }

        public static void shellSort(int[] array){
        int gap = array.length;
        while(true){
            //gap = gap/2;
            gap = (gap/3) +1;

            insertWithGap(array,gap);

            if(gap == 1){
                break;
            }
        }
    }

 

希尔排序总结:时间复杂度:最坏:O(n^2)   
                                               平均:O(n^1.2~1.3)   (不讨论怎么算出来的,知道结论就行)
                                               最好:O(n)   
                         空间复杂度:O(1)
                         稳定性:不稳定

        由于多次插入排序,我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序是不稳定的。 

        本质上讲,希尔排序算法是直接插入排序算法的一种改进,减少了其复制的次数,速度要快很多。当gap值很大时数据项每一趟排序需要移动的个数很少,但数据项的距离很长。当gap值减小时每一趟需要移动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多

 

 

 

 

 

你可能感兴趣的:(Java,数据结构)