直接插入排序&希尔排序【Java实现】

一、直接插入排序

思想

在一个已排好序的序列基础上,每次将下一个待排序记录有序插入到已排好序的序列中,直到所有待排序记录全部插入为止。


步骤

1. 刚开始排序时,可将数组第一个元素认为是已经有序(只有一个数时肯定有序)

2. 接着依次从数组第二个元素开始,到数组最后一个元素,每次顺次取出一个元素插入已排序序列中

3. 当待排序元素全部插入到已排序序列中时完成排序


注意

直接插入排序是利用插入的思想进行排序,是在本数组里边完成的,不需要借助其他数组空间。所谓的插入元素指的是从数组第二个元素开始到数组最后一个元素(未排序序列),依次取出一个元素插入到数组已排序序列中。通俗来说,此时数组分为两部分:已排序序列+未排序序列。

 待排序序列:        2    5    9    1    3    7              

                 1)      {2}    5    9    1    3    7                     其中红色部分表示已排好序的部分    

                 2)      {2     5}   9    1    3    7                     蓝色数字表示此次需要插入的元素

                 3)      {2     5    9}   1    3    7                      黑色部分表示待排序部分     

                 4)      {1     2    5    9}   3    7

                 5)      {1     2    3    5    9}   7

                 6)      {1     2    3    5    7    9}


 public void insert() {
        int i, j;
        for (i = 1; i < len; i++) {//从数组第二个元素开始遍历数组(数组首元素已经有序)
            int key = arr[i];//由于可能会将arr[i]之前的数顺序后移,此过程定会覆盖原来的arr[i],所以一定要在挪动元素之前把arr[i]保存起来
            for (j = i - 1; j >= 0; j--) {
                if (key > arr[j]) {//往前遍历的时候第一次遇到比key小的元素时就可以停下了
                    break;
                }
                arr[j + 1] = arr[j];//否则将key之前的元素顺序后移
            }
            arr[j + 1] = key;//将key插到相应位置
        }
    }

复杂度分析: 

1.时间复杂度

最好情况:所给数组已经有序,此时只需要依次取出数组未排序序列的元素并直接插入,省去了移动元素的时间。所以此时的时间复杂度为O(n);

最坏情况:所给数组逆序,此时外层循环依次取出数组未排序序列的元素,内层循环将该元素之前的元素全部依次往后移动一个位置,即插入第 i 个元素时要移动 i -1 次。所以此时的时间复杂度为O(n^2);

所以,直接插入排序的时间复杂度为O(n^2)。

2.空间复杂度

由于完成此次排序只需要一个辅助空间,即存放key的空间,所以空间复杂度为O(1)。


稳定性判断

从内层循环的条件语句if (key > arr[j])中我们可以知道,只有当要插入的元素比它之前的元素大时,才会移动之前的元素。当遇到和自己相等的元素时并不会进行移动操作,即要插入的元素不会出现在跟它相同的元素的前面 --> 所以,直接插入排序是一个稳定的排序算法。

待排序序列:        2    5    7    1    3    7

                1)      {2}    5    7    1    3    7                     其中红色部分表示已排好序的部分    

                2)      {2     5}   7    1    3    7                     蓝色数字表示此次需要插入的元素

                3)      {2     5    7}   1    3    7                     黑色部分表示待排序部分

                4)      {1     2    5    7}   3    7

                5)      {1     2    3    5    7}   7  

                6)      {1     2    3    5    7    7}


适用场景

 由于直接插入排序最好情况下的时间复杂度为O(n),所以特别适用于待排序记录数目较少且基本有序的情况。


完整代码

//直接插入排序
public class InsertSort {
    private int[] arr;
    private int len;

    public InsertSort(int[] arr) {
        this.arr = arr;
        this.len = arr.length;
    }

    public void insert() {
        int i, j;
        for (i = 1; i < len; i++) {//从数组第二个元素开始遍历数组(数组首元素已经有序)
            int key = arr[i];//由于可能会将arr[i]之前的数顺序后移,此过程定会覆盖原来的arr[i],所以一定要在挪动元素之前把arr[i]保存起来
            for (j = i - 1; j >= 0; j--) {
                if (key > arr[j]) {//往前遍历的时候第一次遇到比key小的元素时就可以停下了
                    break;
                }
                arr[j + 1] = arr[j];//否则将key之前的元素顺序后移
            }
            arr[j + 1] = key;//将key插到相应位置
        }
    }

    public void print() {
        for (int i = 0; i < len; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
}

二、希尔排序

思想

希尔排序又称缩小增量排序,也是一种基于插入思想的排序方法。即每次排序不是按顺序排,而是以gap=gap/3+1的间隔排序(),其中gap初始值为size,在由间隔分割成的每个子序列进行插入排序。


步骤

1. 以gap=gap/3+1为间隔,在整个待排序序列中将所有间隔为gap的记录分成一组,进行组内直接插入排序(gap初始值为数组大小)

2. 重复步骤1,直到gap为1,此时只有一个子序列,对该序列进行直接插入排序,完成排序。


               待排序序列:        2    5    7    1    3    7

取gap=3,分为间隔为3的子序列:       2    5    7    1    3    7 

各子序列内进行插入排序,结果为:    1    3    7    2    5    7  

取gap=2,分为间隔为2的子序列:       1    3    7    2    5    7           其中相同颜色的记录表示同一个子序列

各子序列内进行插入排序,结果为:     1    2    5    3    7    7

取gap=1,分为间隔为1的子序列:       1    2    5    3    7    7 

各子序列内进行插入排序,结果为:     1    2    3    5    7    7       

public void shell() {
        int gap = len;
        while (true) {
            gap = gap / 3 + 1;//间隔,增量
            _shell(gap);
            if (gap == 1) {
                return;
            }
        }
    }

    private void _shell(int gap) {
        int i, j;
        for (i = gap; i >= len; i++) {
            int key = arr[i];
            for (j = i - gap; j >= 0; j -= gap) {
                if (key > arr[j]) {
                    arr[j + gap] = arr[j];
                } else {
                    break;
                }
            }
            arr[j + gap] = key;
        }
    }

复杂度分析

1.时间复杂度

最好情况:所给数组已经有序,此时时间复杂度为O(n);

最坏情况:所给数组逆序,此时时间复杂度为O(n^2);

所以,希尔排序的时间复杂度为O(n^1.3)。

2.空间复杂度

由于排序过程中只需要一个辅助空间key,所以空间复杂度为O(1)。


稳定性判断

希尔排序不稳定。如待排序序列为{2,4,1,2}。排序前2在 2 前面,排序后2在 2 后面。

待排序序列:    2      4      1       2

取gap=2,分为间隔为2的子序列:     2      4      1       2

各子序列内进行插入排序,结果为   1      2      2       

取gap=1,分为间隔为1的子序列:     1      2      2       4

各子序列内进行插入排序,结果为:   1      2      2       4 


适用场景

尽管当gap为1时相当于直接插入排序,但是由于此时序列已经基本有序,恰好是直接插入排序的最好情况,所以希尔排序是一个较好的排序方法,适用于对中等规模(n<=1000)的序列进行排序。


完整代码

public class Shell {
    private int[] arr;
    private int len;

    public Shell(int[] arr) {
        this.arr = arr;
        this.len = arr.length;
    }

    public void shell() {
        int gap = len;
        while (true) {
            gap = gap / 3 + 1;//间隔,增量
            _shell(gap);
            if (gap == 1) {
                return;
            }
        }
    }

    private void _shell(int gap) {
        int i, j;
        for (i = gap; i >= len; i++) {
            int key = arr[i];
            for (j = i - gap; j >= 0; j -= gap) {
                if (key > arr[j]) {
                    arr[j + gap] = arr[j];
                } else {
                    break;
                }
            }
            arr[j + gap] = key;
        }
    }

    public void print() {
        for (int i = 0; i < len; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println("");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值