Java 数据结构与算法系列精讲之排序算法

Java 数据结构与算法系列精讲之排序算法攻略

1. 序言

排序算法是计算机程序设计中常见的一类算法,主要用于将一组数据按照一定的顺序重新排列。在实际工作和面试中,排序算法是计算机程序员必须掌握的基本算法之一。本文将重点讲解 Java 数据结构与算法系列中的排序算法,其中包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序和堆排序。

2. 冒泡排序

冒泡排序是一种较为简单的排序方式,其基本思想是从头到尾比较相邻两元素的大小,并交换位置。在一次完整的冒泡过程中,将会把数字序列中最大的数“冒泡”到最后面。最好情况下时间复杂度为 O(n),最坏情况和平均情况时间复杂度均为 O(n²)。

示例:

public static void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

3. 选择排序

选择排序和冒泡排序一样,都是简单的排序算法。它的基本思想是从未排序的序列中选择一个最小的元素存放到已排序序列的末尾,然后再从未排序序列中选择最小元素。最好情况、最坏情况和平均情况下时间复杂度均为 O(n²)。

示例:

public static void selectionSort(int[] arr) {
    int minIndex, temp;
    for (int i = 0; i < arr.length - 1; i++) {
        minIndex = i;
        for (int j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

4. 插入排序

插入排序方面,插入排序的基本思想是将一个记录插入到有序的序列中,从而得到一个新的序列。在插入排序过程中,记录从前往后依次插入,未排序的序列每次从前面取出一个元素,并插入到有序序列的相应位置中。最好情况时间复杂度为 O(n),最坏情况和平均情况时间复杂度均为 O(n²)。

示例:

public static void insertionSort(int[] arr) {
    for (int i = 1; i < arr.length; i++) {
        int temp = arr[i];
        int j;
        for (j = i; j > 0 && temp < arr[j - 1]; j--) {
            arr[j] = arr[j - 1];
        }
        arr[j] = temp;
    }
}

5. 希尔排序

希尔排序是插入排序的一种优化版本。它通过缩小增量(增量序列的选择也有影响)来改变待排序序列中各元素的相对位置,从而达到排序的目的。希尔排序是非稳定排序,它的时间复杂度依据所选取的增量序列不同而不同,通常情况下它的时间复杂度都是 O(n log n) 级别的。

示例:

public static void shellSort(int[] arr) {
    int n = arr.length;
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j;
            for (j = i; j >= gap && temp < arr[j - gap]; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}

6. 归并排序

归并排序采用先拆分再合并的递归方法,将序列分成若干个长度为1的子序列,然后再将它们两两合并,直接合并完成为止。归并排序是稳定排序,在排序过程中需要开辟一个与原数组大小相等的数组来辅助排序。时间复杂度是 O(n log n)。

示例:

public static void mergeSort(int[] arr, int l, int r) {
    if (l < r) {
        int m = (l + r) / 2;
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}

public static void merge(int[] arr, int l, int m, int r) {
    int[] temp = new int[arr.length];
    int i = l, j = m + 1, k = l;
    while (i <= m && j <= r) {
        if (arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= m) {
        temp[k++] = arr[i++];
    }
    while (j <= r) {
        temp[k++] = arr[j++];
    }
    for (int x = l; x <= r; x++) {
        arr[x] = temp[x];
    }
}

7. 快速排序

快速排序是一种快速的排序方法,采用分而治之的思想,利用递归实现。它的基本思想就是选择一个基准元素,然后将序列分成两部分,分别对这两部分再进行快速排序,直到整个序列有序为止。快速排序是不稳定的排序方法,其时间复杂度平均情况下为 O(n log n)。

示例:

public static void quickSort(int[] arr, int low, int high) {
    if (low < high) {
        int pivot = partition(arr, low, high);
        quickSort(arr, low, pivot - 1);
        quickSort(arr, pivot + 1, high);
    }
}

public static int partition(int[] arr, int low, int high) {
    int pivot = arr[low];
    while (low < high) {
        while (low < high && arr[high] >= pivot) {
            high--;
        }
        arr[low] = arr[high];
        while (low < high && arr[low] <= pivot) {
            low++;
        }
        arr[high] = arr[low];
    }
    arr[low] = pivot;
    return low;
}

8. 堆排序

堆排序是一种树形选择排序方法,其基本思想是将待排序的序列构建成一个大根堆或小根堆,依次取出最大或最小元素放到根节点中,再对剩余的元素重新构造堆,直到整个序列有序为止。堆排序是一种不稳定的排序方法,其平均时间复杂度为 O(n log n),空间复杂度为 O(1)。

示例:

public static void heapSort(int[] arr) {
    int n = arr.length;
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    for (int i = n - 1; i >= 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        heapify(arr, i, 0);
    }
}

public static void heapify(int[] arr, int n, int i) {
    int largest = i;
    int l = 2 * i + 1;
    int r = 2 * i + 2;
    if (l < n && arr[l] > arr[largest]) {
        largest = l;
    }
    if (r < n && arr[r] > arr[largest]) {
        largest = r;
    }
    if (largest != i) {
        int temp = arr[i];
        arr[i] = arr[largest];
        arr[largest] = temp;
        heapify(arr, n, largest);
    }
}

9. 总结

通过本文对 Java 数据结构与算法系列中的排序算法的讲解,我们可以了解到不同的排序算法的原理、时间复杂度和空间复杂度等方面的知识。其中包括了冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序和堆排序等七种基本排序算法,希望这篇文章能为你理解和掌握排序算法提供帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java 数据结构与算法系列精讲之排序算法 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • dotenv源码解读从.env文件中读取环境变量

    简介 dotenv是一个读取.env文件的工具库,能够将.env文件中的环境变量读取到process.env中,使得在程序中可以方便地访问环境变量。本篇文章将从源码角度简要介绍dotenv的实现机制。 源码解读 dotenv库的主要代码存放在dotenv-webpack和dotenv两个仓库中,可从github上进行下载,下面是dotenv的主要源码解读: …

    other 2023年6月27日
    00
  • ios史上最全的图片压缩方法集合

    ios史上最全的图片压缩方法集合 在现代社会里,图片已经成为人们生活中必不可少的一部分。然而,通过移动设备上传图片可能导致一些问题,比如图片质量过高、容量过大、加载时间慢等等。所以,对于 iOS 设备的用户来说,解决这些问题是非常关键的。下面将介绍一些在 iOS 设备上进行图片压缩的最有效的方法。 方法一:使用 iOS 自带压缩功能 iOS 11 之后,系统…

    其他 2023年3月29日
    00
  • 卧龙苍天陨落剧情动画没声音怎么办 过场CG没声音解决方法

    针对“卧龙苍天陨落剧情动画没声音怎么办 过场CG没声音解决方法”这个问题,我们提供以下完整攻略: 1. 检查系统及播放器设置 首先需要检查一下你的系统及播放器设置,是否有音频输出器件被禁用,或可能的设置问题。我们可以按以下步骤进行排查: 检查系统中的音频输出器件是否正常工作,是否被禁用或静音。比如,可以进入声音设置界面,检查默认输出设备是否正确,是否勾选了静…

    other 2023年6月27日
    00
  • jenkins部署分支报finished:unstable的问题解决

    当然,我可以为您提供有关“Jenkins部署分支报finished:unstable的问题解决”的完整攻略,以下是详细说明: 问题描述 在使用Jenkins分支部署时,可能会遇到“finished:unstable”状态的问题。这种情况通常表示构建过程中出现了一些问题,但构建仍然完成了。这可能会导致部署失败或出现其他问题。 问题解决 以下是解决Jenkins…

    other 2023年5月7日
    00
  • Win11电脑蓝屏显示你的电脑遇到问题需要重新启动的解决办法

    Win11电脑蓝屏显示“你的电脑遇到问题需要重新启动”的解决办法 当我们在使用Win11电脑时,突然出现了蓝屏问题,提示“你的电脑遇到问题需要重新启动”,这时我们该如何应对呢?下面提供一些解决办法供参考。 1. 更新或卸载问题驱动程序 蓝屏问题通常与驱动程序相关。因此,我们可以通过更新或卸载问题驱动程序解决问题。 更新驱动程序: 按下Win键 + X组合键,…

    other 2023年6月27日
    00
  • Linux上最常用的用户名和密码 有的快改

    攻略:Linux上常用的用户名和密码 用户名 在Linux系统中,最常用的用户名是“root”,这是因为“root”是Linux系统的管理员账户。拥有“root”账户的用户可以对整个系统进行管理,包括安装、升级和删除软件,修改系统配置文件等操作。因此,使用“root”账户需要小心谨慎,避免误操作导致系统崩溃或数据丢失。 除了“root”账户,Linux系统中…

    other 2023年6月27日
    00
  • Win10右键菜单怎么添加删除复制路径选项?

    添加、删除和修改Win10右键菜单的步骤如下: 添加右键菜单选项 打开注册表编辑器(Registry Editor),使用快捷键“Win + R”,输入 “regedit” 然后按Enter键进入。 转到以下路径 HKEY_CLASSES_ROOT\*\shell 右键“shell”文件夹,选择“新建” -> “键值(key)”。 为新键值取一个名字,…

    other 2023年6月27日
    00
  • JDK9为何要将String的底层实现由char[]改成了byte[]

    JDK 9将String的底层实现由char[]改成了byte[]的原因 在JDK 9中,Java的String类的底层实现从使用char[]数组改为了使用byte[]数组。这个改变是为了提高内存使用效率和性能,并且在处理非拉丁字符时能够更好地支持Unicode编码。 1. 内存使用效率 使用byte[]数组作为String的底层实现可以减少内存使用量。在J…

    other 2023年8月2日
    00
合作推广
合作推广
分享本页
返回顶部