Java实现归并排序算法
简介
归并排序(Merge Sort)是一种高效的、基于分治思想的排序算法。它的基本原理是将一个大的无序数组分成两个或多个较小的子数组,对这些子数组分别进行排序,然后再将排序好的子数组合并成一个有序的数组。在Java中实现归并排序算法,可以充分利用其面向对象和丰富的类库特性,为开发者提供一种简洁而有效的数据排序方式。
目录
- 基础概念
- 使用方法
- 常见实践
- 最佳实践
- 代码示例
- 小结
- 参考资料
基础概念
分治思想
分治思想是归并排序的核心。“分”就是将一个大问题分解成若干个规模较小、相互独立且与原问题形式相同的子问题。“治”则是对这些子问题分别进行求解,然后将子问题的解合并成原问题的解。
合并操作
合并操作是归并排序中的关键步骤。它将两个已经排序的子数组合并成一个更大的有序数组。在合并过程中,通过比较两个子数组的元素,将较小的元素依次放入结果数组中,直到其中一个子数组的元素全部被处理完,然后将另一个子数组剩余的元素直接追加到结果数组的末尾。
使用方法
递归实现
- 分解阶段:
- 找到数组的中间位置,将数组分成两个子数组。
- 对两个子数组分别递归调用归并排序算法,直到子数组的大小为1(此时子数组已经有序)。
- 合并阶段:
- 将两个已经排序的子数组合并成一个有序的数组。
迭代实现
迭代实现归并排序通常使用自底向上的方法。首先将数组分成大小为1的子数组(每个子数组本身就是有序的),然后逐步将相邻的子数组合并成更大的有序子数组,直到整个数组有序。
常见实践
对整数数组排序
在实际应用中,经常需要对整数数组进行排序。以下是一个简单的示例:
import java.util.Arrays;
public class MergeSortExample {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
mergeSort(left);
mergeSort(right);
merge(arr, left, right);
}
private static void merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6, 7};
System.out.println("Original array: " + Arrays.toString(arr));
mergeSort(arr);
System.out.println("Sorted array: " + Arrays.toString(arr));
}
}
对自定义对象数组排序
如果要对自定义对象数组进行排序,需要实现Comparable接口或使用Comparator接口。例如,对一个包含学生对象的数组按成绩排序:
import java.util.Arrays;
import java.util.Comparator;
class Student {
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public int getScore() {
return score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}
public class CustomObjectMergeSort {
public static void mergeSort(Student[] arr, Comparator<Student> comparator) {
if (arr == null || arr.length <= 1) {
return;
}
int mid = arr.length / 2;
Student[] left = Arrays.copyOfRange(arr, 0, mid);
Student[] right = Arrays.copyOfRange(arr, mid, arr.length);
mergeSort(left, comparator);
mergeSort(right, comparator);
merge(arr, left, right, comparator);
}
private static void merge(Student[] arr, Student[] left, Student[] right, Comparator<Student> comparator) {
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
if (comparator.compare(left[i], right[j]) <= 0) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
public static void main(String[] args) {
Student[] students = {
new Student("Alice", 85),
new Student("Bob", 70),
new Student("Charlie", 90)
};
System.out.println("Original array: " + Arrays.toString(students));
mergeSort(students, Comparator.comparingInt(Student::getScore));
System.out.println("Sorted array: " + Arrays.toString(students));
}
}
最佳实践
优化空间复杂度
递归实现的归并排序在分解数组时会创建额外的子数组,导致空间复杂度为O(n)。可以通过使用原地归并算法来优化空间复杂度,使其达到O(1)。不过原地归并算法实现起来相对复杂。
并行化处理
对于大规模数据,可以利用Java的多线程或并行流来并行化归并排序的分解和合并过程,提高排序效率。例如,可以使用Fork/Join框架来实现并行归并排序。
数据预处理
在进行归并排序之前,可以对数据进行一些预处理,例如去除重复元素或对数据进行大致的分组,以减少排序的工作量。
代码示例
完整的递归归并排序代码
import java.util.Arrays;
public class MergeSort {
public static void mergeSort(int[] arr) {
if (arr == null || arr.length <= 1) {
return;
}
int mid = arr.length / 2;
int[] left = Arrays.copyOfRange(arr, 0, mid);
int[] right = Arrays.copyOfRange(arr, mid, arr.length);
mergeSort(left);
mergeSort(right);
merge(arr, left, right);
}
private static void merge(int[] arr, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
public static void main(String[] args) {
int[] testArray = {10, 7, 8, 9, 1, 5};
System.out.println("Before sorting: " + Arrays.toString(testArray));
mergeSort(testArray);
System.out.println("After sorting: " + Arrays.toString(testArray));
}
}
迭代归并排序代码
import java.util.Arrays;
public class IterativeMergeSort {
public static void mergeSort(int[] arr) {
int n = arr.length;
for (int subArraySize = 1; subArraySize < n; subArraySize *= 2) {
for (int startIndex = 0; startIndex < n; startIndex += 2 * subArraySize) {
int midIndex = Math.min(startIndex + subArraySize, n);
int endIndex = Math.min(startIndex + 2 * subArraySize, n);
int[] left = Arrays.copyOfRange(arr, startIndex, midIndex);
int[] right = Arrays.copyOfRange(arr, midIndex, endIndex);
int i = 0, j = 0, k = startIndex;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
arr[k++] = left[i++];
} else {
arr[k++] = right[j++];
}
}
while (i < left.length) {
arr[k++] = left[i++];
}
while (j < right.length) {
arr[k++] = right[j++];
}
}
}
}
public static void main(String[] args) {
int[] testArray = {10, 7, 8, 9, 1, 5};
System.out.println("Before sorting: " + Arrays.toString(testArray));
mergeSort(testArray);
System.out.println("After sorting: " + Arrays.toString(testArray));
}
}
小结
归并排序是一种稳定、高效的排序算法,在Java中实现归并排序可以通过递归或迭代的方式。了解其基础概念、使用方法、常见实践和最佳实践,能够帮助开发者根据不同的应用场景选择合适的实现方式,提高排序效率和优化空间复杂度。通过不断练习和优化代码,开发者可以更好地掌握这一经典算法。
参考资料
- 《算法导论》(Thomas H. Cormen等著)
- Oracle Java Documentation
- 各种在线算法学习平台,如LeetCode、GeeksforGeeks等