面试 11:玩转 Java 归并排序

简介: 面试 11:Java 玩转归并排序前面讲了冒泡、选择、插入三种简单排序,时间复杂度都是 O(n²),今天,我们终于迎来了更高级的排序:归并排序。虽然在这之前还有希尔排序和堆排序,但由于时间关系,我们这里就直接跳过,确实感兴趣的请直接 Google。

面试 11:Java 玩转归并排序

前面讲了冒泡、选择、插入三种简单排序,时间复杂度都是 O(n²),今天,我们终于迎来了更高级的排序:归并排序。

虽然在这之前还有希尔排序和堆排序,但由于时间关系,我们这里就直接跳过,确实感兴趣的请直接 Google。

归并排序

我们总是可以将一个数组一分为二,然后二分为四,直到每一组只有两个元素,这可以理解为个递归的过程,然后将两个元素进行排序,之后再将两个元素为一组进行排序。直到所有的元素都排序完成。同样我们来看下边这个动图。

图片来源于网络

归并排序算法是采用分治法的一个非常典型的应用,且各层分治递归可以同时进行。

归并算法的思想

归并算法其实可以分为递归法和迭代法(自底向上归并),两种实现对于最小集合的归并操作思想是一样的。区别在于如何划分数组,我们先介绍下算法最基本的操作:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针到达序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

我们来看看 Java 递归代码是怎么实现的:

public class Test09 {

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    private static void printArr(int[] arr) {
        for (int anArr : arr) {
            System.out.print(anArr + " ");
        }
    }

    private static void mergeSort(int[] arr) {
        if (arr == null)
            return;
        mergeSort(arr, 0, arr.length - 1);
    }

    private static void mergeSort(int[] arr, int start, int end) {
        if (start >= end)
            return;
        // 找出中间索引
        int mid = start + (end - start >> 1);
        // 对左边数组进行递归
        mergeSort(arr, start, mid);
        // 对右边数组进行递归
        mergeSort(arr, mid + 1, end);
        // 合并
        merge(arr, start, mid, end);
    }

    private static void merge(int[] arr, int start, int mid, int end) {
        // 先建立一个临时数组,用于存放排序后的数据
        int[] tmpArr = new int[arr.length];

        int start1 = start, end1 = mid, start2 = mid + 1, end2 = end;
        // 创建一个下标
        int pos = start1;
        // 缓存左边数组的第一个元素的索引
        int tmp = start1;
        while (start1 <= end1 && start2 <= end2) {
            // 从两个数组中取出最小的放入临时数组
            if (arr[start1] <= arr[start2])
                tmpArr[pos++] = arr[start1++];
            else
                tmpArr[pos++] = arr[start2++];
        }
        // 剩余部分依次放入临时数组,实际上下面两个 while 只会执行其中一个
        while (start1 <= end1) {
            tmpArr[pos++] = arr[start1++];
        }
        while (start2 <= end2) {
            tmpArr[pos++] = arr[start2++];
        }
        // 将临时数组中的内容拷贝回原来的数组中
        while (tmp <= end) {
            arr[tmp] = tmpArr[tmp++];
        }

    }

    public static void main(String[] args) {
        int[] arr = {6, 4, 2, 1, 8, 3, 7, 9, 5};
        mergeSort(arr);
        printArr(arr);
    }
}

归并排序算法总的时间复杂度是 O(nlogn),而且这是归并排序算法中最好、最坏、平均的时间性能。

而由于在归并排序过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时压入栈的数据占用的空间:n + logn,所以空间复杂度为 O(n)。

总结

归并排序虽然比较稳定,在时间上也是非常有效的,但是这种算法很消耗空间,一般来说只有在外部排序才会采用这个方法,但在内部排序不会用这种方法,而是用快速排序。明天,我们将带来排序算法中的王牌:快速排序。

目录
相关文章
|
22天前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
21天前
|
消息中间件 缓存 算法
Java多线程面试题总结(上)
进程和线程是操作系统管理程序执行的基本单位,二者有明显区别: 1. **定义与基本单位**:进程是资源分配的基本单位,拥有独立的内存空间;线程是调度和执行的基本单位,共享所属进程的资源。 2. **独立性与资源共享**:进程间相互独立,通信需显式机制;线程共享进程资源,通信更直接快捷。 3. **管理与调度**:进程管理复杂,线程管理更灵活。 4. **并发与并行**:进程并发执行,提高资源利用率;线程不仅并发还能并行执行,提升执行效率。 5. **健壮性**:进程更健壮,一个进程崩溃不影响其他进程;线程崩溃可能导致整个进程崩溃。
26 2
|
22天前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
22天前
|
存储 Java
【Java集合类面试二十九】、说一说HashSet的底层结构
HashSet的底层结构是基于HashMap实现的,使用一个初始容量为16和负载因子为0.75的HashMap,其中HashSet元素作为HashMap的key,而value是一个静态的PRESENT对象。
|
21天前
|
存储 Java
Java面向对象面试题总结(上)
在Java中,重写(Override)与重载(Overload)是两个重要的概念,关联到方法的定义与调用。重写是指子类对继承自父类的方法进行新的实现,以便提供子类特有的行为,其关键在于方法签名一致但方法体不同。重载则允许在同一个类中定义多个同名方法,只要参数列表不同即可,以此提供方法调用的灵活性。重写关注多态性,而重载强调编译时多态。
18 1
|
22天前
|
Java
【Java集合类面试三十】、BlockingQueue中有哪些方法,为什么这样设计?
BlockingQueue设计了四组不同行为方式的方法用于插入、移除和检查元素,以适应不同的业务场景,包括抛异常、返回特定值、阻塞等待和超时等待,以实现高效的线程间通信。
|
22天前
|
NoSQL Java 数据库
2022年整理最详细的java面试题、掌握这一套八股文、面试基础不成问题[吐血整理、纯手撸]
这篇文章是一份详尽的Java面试题总结,涵盖了从面向对象基础到分布式系统设计的多个知识点,适合用来准备Java技术面试。
2022年整理最详细的java面试题、掌握这一套八股文、面试基础不成问题[吐血整理、纯手撸]
|
22天前
|
缓存 Java
【IO面试题 一】、介绍一下Java中的IO流
Java中的IO流是对数据输入输出操作的抽象,分为输入流和输出流,字节流和字符流,节点流和处理流,提供了多种类支持不同数据源和操作,如文件流、数组流、管道流、字符串流、缓冲流、转换流、对象流、打印流、推回输入流和数据流等。
【IO面试题 一】、介绍一下Java中的IO流
|
11天前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
49 0
|
19天前
|
Java 编译器 开发工具
JDK vs JRE:面试大揭秘,一文让你彻底解锁Java开发和运行的秘密!
【8月更文挑战第24天】JDK(Java Development Kit)与JRE(Java Runtime Environment)是Java环境中两个核心概念。JDK作为开发工具包,不仅包含JRE,还提供编译器等开发工具,支持Java程序的开发与编译;而JRE仅包含运行Java程序所需的组件如JVM和核心类库。一个简单的&quot;Hello, World!&quot;示例展示了两者用途:需借助JDK编译程序,再利用JRE或JDK中的运行环境执行。因此,开发者应基于实际需求选择安装JDK或JRE。
37 0