直接插入排序(Java)

简介: 直接插入排序(Java)

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

直接插入排序的基本思想

  • 每次从未排序的子序列中取出一个待排序的记录按其关键字大小插入前面已排好序的子序列,直到全部记录插入完成
  • 假设在排序过程中,待排序表 L[1…n] 在某次排序过程中的某一时刻状态如下:
  • 要将元素 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)),在从后向前的比较过程中,需要反复把已排序的元素逐步向后挪位,为新元素提供插入空间。

直接插入排序的排序过程

直接插入排序的代码实现

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<n; 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基础算法系列(四)(直接插入排序以及二分插入讲解)
java基础算法系列(四)(直接插入排序以及二分插入讲解)
|
算法 Java
Java实现直接插入排序
插入排序 简单来说,就是将要排序的元素,分为两部分,一部分为有序表,一部分为无序表,每次从无序表中取出一个元素插入到有序表中。
120 0
Java实现直接插入排序
Java课后练习 对应冒泡排序、直接选择排序、直接插入排序进行选择调用,手动输入一组数字(空格隔开)转为数组 最后排序前后结果
Java课后练习 对应冒泡排序、直接选择排序、直接插入排序进行选择调用,手动输入一组数字(空格隔开)转为数组 最后排序前后结果
Java课后练习 对应冒泡排序、直接选择排序、直接插入排序进行选择调用,手动输入一组数字(空格隔开)转为数组 最后排序前后结果
|
存储 搜索推荐 算法
数据结构 | 排序算法总结——(一)直接插入排序(附Java实现代码)
数据结构 | 排序算法总结——(一)直接插入排序(附Java实现代码)
|
搜索推荐 算法 Java
插入排序(直接插入排序)java代码实现(注释详细 简单易懂)
插入排序(直接插入排序)java代码实现(注释详细 简单易懂)
147 0
|
人工智能 Java
Java使用二分插入排序竟然和直接插入排序速度比较
 Java使用二分插入排序竟然和直接插入排序速度比较 之前测试过Python使用二分插入排序竟然比直接插入排序快99倍! 现在测试下 Java,Linux测试结果如下: javac test.java java testInsertSort total milliseconds:15769InsertSortWithBinarySerach total milliseconds:15657 更正下,上面的数据在一台虚拟机上测试的,所以样本不够,理论上,这两个算法的速度应该是有倍数差别的。
1300 0
|
3天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
38 14
|
6天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
35 13
|
7天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。