直接插入排序(Java)

文章汇总归纳整理于:算法竞赛学习之路[Java版]

直接插入排序的基本思想

  • 每次从未排序的子序列中取出一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成
  • 假设在排序过程中,待排序表 L[1…n] 在某次排序过程中的某一时刻状态如下:
    • image.png
  • 要将元素 L[i] 插入已有序的子序列 L[1…i-1],需要执行以下操作:
    • 1)查找出 L[i] 在 L[1…i-1] 中的插入位置k。
    • 2)将 L[k…i-1] 中的所有元素依次后移一个位置。
    • 3)将 L[i] 复制到 L[k]。
  • 为了实现对 L[1…n] 的排序,可以将 L[2]~L[n] 依次插入前面已排好序的子序列,初始 L[1] 可以视为是一个已排好序的子序列。
  • 上述操作执行 n-1 次就能得到一个有序的表。
  • 插入排序在实现上通常采用就地排序(空间复杂度为O(1)),在从后向前的比较过程中,需要反复把已排序的元素逐步向后挪位,为新元素提供插入空间。

直接插入排序的排序过程

  • image.png
  • image.png
  • 直接插入排序(Java)_第1张图片
  • image.png
  • image.png
  • 直接插入排序(Java)_第2张图片
  • 直接插入排序(Java)_第3张图片
  • 直接插入排序(Java)_第4张图片

直接插入排序的代码实现

以 P2676 [USACO07DEC]Bookshelf B 为例,实现直接插入排序

// package 直接插入排序;

import java.io.*;

public class Main {
    // 快读快写
    static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
    
    public static void main(String[] args) {
        int n = readInt(); // 奶牛的个数
        // 由于 1≤B≤S<2,000,000,007 使用 long
        long b = readLong(); // 书架的高度
        long[] hs = new long[n]; // 每个奶牛的高度
        // 读入每个奶牛的高度
        for (int i = 0; i < n; i++) {
            hs[i] = readLong();
        }
        // 插入排序(从小到大排序)
        for (int i = 1; i < n; i++) {
            long hi = hs[i]; // 当待插入有序子序列的数据
            int idx = i - 1;
            // 查找插入位置
            while (idx >= 0 && hs[idx] > hi) {
                hs[idx+1] = hs[idx];
                idx--;
            }
            // 将数据插入(退出循环前会 -- 一次,所以idx+1)
            hs[idx+1] = hi;
        }
        // 查看排序后的数据
        // for ( int i=0; i
        //     out.println(hs[i]);
        // }
        // 塔中奶牛的数目尽量少,且所有奶牛的身高和必须不小于书架的高度
        // 所以从后向前遍历,直到奶牛的身高和大于等于书架高
        int cnt = 0; // 最少要多少头奶牛叠成塔,才能够到书架顶部
        int h_sum = 0; // 当前奶牛的身高和
        for ( int i=n-1; i>=0; i-- ) {
            h_sum += hs[i]; // 塔中奶牛的身高和增加
            cnt++; // 塔中奶牛的数目++
            // 如果奶牛的身高和大于等于书架高,退出循环
            if ( h_sum >= b ) {
                break;
            }
        }
        out.print(cnt);
        out.flush();
    }
    
    // 读入长整型整数
    static long readLong() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (long) in.nval;
    }
    
    // 读入整数
    static int readInt() {
        try {
            in.nextToken();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (int) in.nval;
    }
}

直接插入排序算法的性能分析

  • 空间效率:仅使用了常数个辅助单元,因而空间复杂度为O(1)。
  • 时间效率:
    • 在排序过程中,向有序子表中逐个地插入元素的操作进行了 n-1 趟,每趟操作都分为比较关键字和移动元素,而比较次数和移动次数取决于待排序表的初始状态。
    • 在最好情况下,表中元素已经有序,此时每插入一个元素,都只需比较一次而不用移动元素,因而时间复杂度为O(n)。
    • 在最坏情况下,表中元素顺序刚好与排序结果中的元素顺序相反(逆序),总的比较次数达到最大,总的移动次数也达到最大,总的时间复杂度为O(n2)。
    • 平均情况下,考虑待排序表中元素是随机的,此时可以取上述最好与最坏情况的平均值作为平均情况下的时间复杂度,总的比较次数与总的移动次数均约为n2/4。
    • 因此,直接插入排序算法的时间复杂度为O(n2)。
  • 稳定性:由于每次插入元素时总是从后向前先比较再移动,所以不会出现相同元素相对位置发生变化的情况,即直接插入排序是一个稳定的排序方法。

你可能感兴趣的:(算法竞赛学习之路[Java版],java,排序算法,算法,数据结构,直接插入排序)