哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
前言
在日常开发中,数组排序是一个非常常见的操作。很多开发者可能会认为排序只是一个简单的操作,但实际上,实现一个高效、稳定、可扩展的排序算法并不容易。因此,在本文中,我想探讨一下为什么数组排序如此重要,以及如何在Java中实现各种排序算法。
摘要
数组排序是一种非常基础的算法,但却在日常开发中经常被忽视。本文将介绍Java中常用的排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序和堆排序。我们将通过代码解析、应用场景案例、优缺点分析、类代码方法介绍和测试用例来帮助读者更好地理解这些算法。
正文
简介
排序是将一组元素按照某种规律排列的过程。通常情况下,排序是将一组无序的元素按照从小到大或从大到小的顺序排列。在Java中,我们可以使用Arrays.sort()方法来对数组进行排序。但是,这种排序方法在处理大规模数据时可能会出现运行时间过长的情况。因此,在某些情况下,我们需要使用更高效的排序算法来提高程序的性能。
源代码解析
冒泡排序
冒泡排序是一种简单的排序算法。它通过反复交换相邻的元素来对数组进行排序。该算法的时间复杂度为O(n^2)。
public static void bubbleSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len-i-1; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
代码分析:
这是一个经典的冒泡排序算法,其主要思想是将相邻的数进行比较,如果顺序不对则进行交换,一次遍历后可以将一个最大或最小的数移到数组的末尾或开头,然后再进行下一次遍历。重复这个过程,直到排序完整个数组。
该算法的时间复杂度为O(n^2),并不适用于大规模数据的排序。因此,在实际应用中,如果需要对大量数据进行排序,应该使用更高效的排序算法,例如归并排序、快速排序等。
插入排序
插入排序是一种简单的排序算法。它通过将未排序的元素插入已排序的序列中来对数组进行排序。该算法的时间复杂度为O(n^2)。
public static void insertionSort(int[] arr) {
int len = arr.length;
for (int i = 1; i < len; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j--;
}
arr[j+1] = key;
}
}
代码分析:
上述代码实现了插入排序算法。插入排序算法的基本思想是将待排序序列分为已排序区间和未排序区间,每次从未排序区间中选择一个元素,将其插入已排序区间中的正确位置,直到未排序区间为空。代码实现中,for循环遍历未排序序列,将i位置的元素作为key,将key插入到已排序区间的正确位置,即将key与已排序区间中的元素从后往前进行比较,将比key大的元素往后移动一位。当已排序区间中找到一个比key小的元素时,将key插入到该元素后面即可。时间复杂度为O(n^2)。
选择排序
选择排序是一种简单的排序算法。它通过选择未排序元素中的最小值来对数组进行排序。该算法的时间复杂度为O(n^2)。
public static void selectionSort(int[] arr) {
int len = arr.length;
for (int i = 0; i < len-1; i++) {
int minIndex = i;
for (int j = i+1; j < len; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
}
代码分析:
这段代码是实现选择排序的算法。选择排序的思想是每次从未排序的数组中选择最小的元素,放到已排序的数组末尾,直到所有元素都被排序。具体实现如下:
- 对于未排序部分的数组,找到其中最小的元素。
- 将最小的元素与未排序部分的第一个元素交换位置。
- 从剩余未排序部分的数组中找到最小的元素,将其与未排序部分的第二个元素交换位置。
- 依次类推,直到未排序部分为空。
代码中,外层循环用于控制已排序部分的末尾,内层循环用于查找未排序部分中的最小元素,并与已排序部分的末尾交换位置。时间复杂度为O(n^2)。
需要注意的是,这段代码没有处理异常情况,例如传入空数组。在实际应用中,需要进行相应的异常处理。
快速排序
快速排序是一种高效的排序算法。它通过选定一个基准值,将数组分为两个子数组,然后递归地对子数组进行排序。该算法的时间复杂度为O(nlogn)。
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi-1);
quickSort(arr, pi+1, high);
}
}
public static int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i+1;
}
代码分析:
这是一个使用快速排序算法实现的代码。
函数quickSort(int[] arr, int low, int high)用于对一个给定的数组arr中的元素进行排序。low和high分别表示数组的起始和结束位置。如果low小于high,那么函数会选取数组中的一个元素作为基准值(pivot),并将数组中小于基准值的元素放到基准值的左边,大于基准值的元素放到右边。然后,递归调用quickSort函数对左侧和右侧的子数组进行排序。
函数partition(int[] arr, int low, int high)用于选取基准值(pivot),并将小于基准值的元素放到基准值的左边,大于基准值的元素放到右边。具体地,它首先选取数组中的最后一个元素作为基准值(pivot),然后遍历数组中low到high-1位置的元素。如果某个元素小于基准值,那么就将i位置的元素(i为小于基准值的元素的最后一个位置)和这个元素进行交换,表示这个元素应该放到基准值的左边。最后,将i+1位置的元素(也就是大于基准值的元素的第一个位置)和基准值进行交换,表示基准值被放到了正确的位置。
整个算法的核心是partition函数,它用于将数组中的元素划分成小于基准值和大于基准值的两部分。在每次对一个子数组进行排序时,我们都需要选取一个新的基准值,并用partition函数来对子数组进行划分。最终,整个数组就会被排好序。
归并排序
归并排序是一种高效的排序算法。它通过将数组分为两个子数组,然后递归地对子数组进行排序,最后将两个已排序的子数组合并成一个有序的数组。该算法的时间复杂度为O(nlogn)。
public static void mergeSort(int[] arr, int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
mergeSort(arr, low, mid);
mergeSort(arr, mid+1, high);
merge(arr, low, mid, high);
}
}
public static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i = low, j = mid+1, k = 0;
while (i <= mid && j <= high) {
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= high) {
temp[k++] = arr[j++];
}
for (int m = 0; m < temp.length; m++) {
arr[low+m] = temp[m];
}
}
代码分析:
这段代码实现了归并排序算法。归并排序是一种分治算法,它将待排序的数组分成两个子数组,分别对它们进行排序,然后将两个已排序的子数组合并成一个有序的数组。该算法的时间复杂度为O(nlogn)。
该算法分为两个函数,mergeSort和merge。
mergeSort函数接收三个参数:待排序的数组,数组的起始下标(low),数组的结束下标(high)。它使用递归的方式将数组一分为二,然后对两个子数组分别调用自身进行排序,最后将两个已排序的子数组合并成一个有序的数组。如果low小于high,则进行排序,否则直接返回。
merge函数接收四个参数:待排序的数组,数组的起始下标(low),数组的中间下标(mid),数组的结束下标(high)。它的作用是将两个已排序的子数组合并成一个有序的数组。它创建一个临时数组temp,用i、j、k三个指针变量来遍历两个已排序的子数组,将它们中较小的元素放入temp数组中,最后再将temp数组中的元素复制回原数组中。
最终,当mergeSort函数执行完毕后,原数组就会被排序成一个有序数组。
堆排序
堆排序是一种高效的排序算法。它通过将数组看作一棵完全二叉树,然后将数组转换为最大堆或最小堆,最后将堆排序,得到一个有序的数组。该算法的时间复杂度为O(nlogn)。
public static void heapSort(int[] arr) {
int len = arr.length;
for (int i = len/2-1; i >= 0; i--) {
heapify(arr, len, i);
}
for (int i = len-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);
}
}
代码分析:
这是一个堆排序的Java实现。
堆排序的基本思想是:首先将待排序的数组构造成一个大顶堆,然后将堆顶元素与堆底元素交换并下调,使得剩余元素重新构成一个大顶堆,重复此步骤直到堆排序完成。
这个Java代码中,heapSort()方法遍历数组并调用heapify()方法,将数组构造成一个大顶堆。然后,它对数组进行排序,直到排序完成。
heapify()方法用来调整堆,确保以i为根节点的子树满足大顶堆的性质。它会检查左右子节点的值,找到其中最大值,如果最大值不是当前节点值,那么就交换它和最大值,然后递归调用heapify(),直到整个堆满足大顶堆的性质。
应用场景案例
在日常开发中,排序算法的应用非常广泛。下面是一些排序算法的应用场景:
- 冒泡排序:适用于小规模数据的排序。
- 插入排序:适用于对于大部分数据已经有序的情况下的排序。
- 选择排序:适用于需要排序的数据规模较小的情况。
- 快速排序:适用于需要高效地排序大规模数据的情况。
- 归并排序:适用于需要高效地排序大规模数据的情况。
- 堆排序:适用于需要高效地排序大规模数据的情况。
优缺点分析
不同的排序算法各有优缺点,下面是一些排序算法的优缺点:
-
冒泡排序:简单易懂,代码实现简单,但是时间复杂度较高,不适用于大规模数据的排序。
-
插入排序:代码简单,对于大部分数据已经有序的情况下排序效率较高,但是对于逆序排列的数据,时间复杂度较高。
-
选择排序:简单易懂,代码实现简单,适用于需要排序的数据规模较小的情况,但是时间复杂度较高,不适用于大规模数据的排序。
-
快速排序:时间复杂度较低,适用于需要高效地排序大规模数据的情况,但是可能会受到递归深度的影响,导致栈溢出。
-
归并排序:时间复杂度较低,适用于需要高效地排序大规模数据的情况,但是需要额外的存储空间,可能会导致空间浪费。
-
堆排序:时间复杂度较低,适用于需要高效地排序大规模数据的情况,但是需要额外的存储空间,不适用于非连续的数据结构。
因此,在选择排序算法时,需要根据实际情况来选择最适合的排序算法。
类代码方法介绍
- bubbleSort(int[] arr):实现冒泡排序算法,将给定的数组按从小到大的顺序排序。
- insertionSort(int[] arr):实现插入排序算法,将给定的数组按从小到大的顺序排序。
- selectionSort(int[] arr):实现选择排序算法,将给定的数组按从小到大的顺序排序。
- quickSort(int[] arr, int low, int high):实现快速排序算法(递归实现),将给定的数组按从小到大的顺序排序。
- partition(int[] arr, int low, int high):快速排序算法中的划分函数,用于将数组分为两个子数组。
- mergeSort(int[] arr, int low, int high):实现归并排序算法(递归实现),将给定的数组按从小到大的顺序排序。
- merge(int[] arr, int low, int mid, int high):归并排序算法中的合并函数,用于合并两个已排序的子数组。
- heapSort(int[] arr):实现堆排序算法,将给定的数组按从小到大的顺序排序。
- heapify(int[] arr, int n, int i):堆排序算法中的堆化函数,用于将数组转换为最大堆或最小堆。
测试用例
为了验证数组排序算法的正确性和效率,我们需要编写一些相应的测试用例。以下是对排序算法进行测试的示例代码:
测试代码演示
package com.example.javase.se.sort;
import java.util.Arrays;
/**a
* @Author ms
* @Date 2023-11-14 21:11
*/
public class SortTest {
public static void main(String[] args) {
int[] arr = {9, 3, 1, 4, 6, 8, 7, 5, 2};
int[] expected = {1, 2, 3, 4, 5, 6, 7, 8, 9};
SortingUtils.bubbleSort(arr);
System.out.println("Bubble Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Bubble Sort passed\n");
else
System.out.println("Bubble Sort failed\n");
arr = new int[]{9, 3, 1, 4, 6, 8, 7, 5, 2};
SortingUtils.insertionSort(arr);
System.out.println("Insertion Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Insertion Sort passed\n");
else
System.out.println("Insertion Sort failed\n");
arr = new int[]{9, 3, 1, 4, 6, 8, 7, 5, 2};
SortingUtils.selectionSort(arr);
System.out.println("Selection Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Selection Sort passed\n");
else
System.out.println("Selection Sort failed\n");
arr = new int[]{9, 3, 1, 4, 6, 8, 7, 5, 2};
SortingUtils.quickSort(arr, 0, arr.length - 1);
System.out.println("Quick Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Quick Sort passed\n");
else
System.out.println("Quick Sort failed\n");
arr = new int[]{9, 3, 1, 4, 6, 8, 7, 5, 2};
SortingUtils.mergeSort(arr, 0, arr.length - 1);
System.out.println("Merge Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Merge Sort passed\n");
else
System.out.println("Merge Sort failed\n");
arr = new int[]{9, 3, 1, 4, 6, 8, 7, 5, 2};
SortingUtils.heapSort(arr);
System.out.println("Heap Sort: " + Arrays.toString(arr));
if (Arrays.equals(expected, arr))
System.out.println("Heap Sort passed\n");
else
System.out.println("Heap Sort failed\n");
}
}
测试结果
根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。
以上示例代码中,使用JUnit框架编写了针对数组排序算法的单元测试用例,确保排序算法的正确性和效率。
这段代码是一个用于测试排序算法的程序。其中包含了不同种类的排序算法,如冒泡排序、插入排序、选择排序、快速排序、归并排序、堆排序。程序会将一个包含9个元素的整型数组传入这些排序算法中进行排序,并输出排序后的结果。在排序完成后,程序会将排序后的结果与一个期望的有序数组进行比较,如果两个数组相等则表示该排序算法通过测试,否则表示该排序算法未通过测试。
测试结果
根据如上测试用例,本地测试结果如下,仅供参考,你们也可以自行修改测试用例或者添加更多的测试数据或测试方法,进行熟练学习以此加深理解。
测试代码分析
根据如上测试用例,在此我给大家进行深入详细的解读一下测试代码,以便于更多的同学能够理解并加深印象。
小结
本文主要介绍了Java中常用的数组排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序和堆排序。通过对这些排序算法的源代码解析、应用场景案例、优缺点分析、类代码方法介绍和测试用例进行讲解,帮助读者更好地理解这些排序算法。在选择排序算法时,需要根据实际情况来选择最适合的排序算法,不同的排序算法各有优缺点。同时,为了验证排序算法的正确性和效率,可以编写相应的测试用例进行验证。
总结
本文对数组排序的重要性进行了探讨,并介绍了Java中常用的排序算法,包括冒泡排序、插入排序、选择排序、快速排序、归并排序和堆排序。通过代码解析、应用场景案例、优缺点分析、类代码方法介绍和测试用例,详细介绍了每种算法的实现和优缺点,帮助读者更好地理解排序算法。最后,强调了选择最适合的排序算法的重要性,并给出了相应的测试用例,以验证排序算法的正确性和效率。
因此,对于开发者而言,在日常开发中,不能忽视数组排序的重要性。在选择排序算法时,需要根据实际情况来选择最适合的排序算法,以提高程序的性能和效率。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。