Java手写堆排序(Heap Sort)
1. 思维导图
下面是使用Mermaid代码绘制的思维导图,用于解释堆排序算法的实现思路原理:
2. 手写堆排序的必要性
堆排序是一种高效的排序算法,它的时间复杂度为O(nlogn),具有以下几个方面的优势:
- 相对于其他基于比较的排序算法(如快速排序、归并排序),堆排序是一种原地排序算法,不需要额外的辅助空间;
- 堆排序是稳定的排序算法,不会改变相同元素之间的顺序;
- 堆排序在最坏情况下仍具有较好的性能,因此在一些对排序稳定性要求较高的场景中,堆排序是一个不错的选择。
3. 市场调研
据市场调研,堆排序在实际应用中广泛使用,特别是在以下场景中:
- 大规模数据的排序:由于堆排序的时间复杂度较低且具备稳定性,因此在需要对大规模数据进行排序的场景中,堆排序是一种常用的选择;
- 优先级队列:堆排序的基础是堆这种数据结构,而堆被广泛用于实现优先级队列。优先级队列是一种可以动态地插入和删除元素,并且每次操作都能高效地找到最大(或最小)值的数据结构,而堆正是实现这一特性的关键;
- 排名统计:堆排序在一些需要对数据进行排名统计的场景中也有应用,例如按照成绩对学生进行排名、按照销售额对商品进行排名等。
4. 实现步骤
堆排序的实现步骤包括以下几个部分:
4.1 建立最大堆
首先,我们需要构建一个最大堆,以便后续的排序操作。建立最大堆的步骤如下:
- 从最底层的非叶子节点开始,逐层向上调整,使得每个节点都满足最大堆的性质;
- 对于当前节点,比较其与左右子节点的大小,如果存在比当前节点更大的子节点,则交换两者的位置;
- 重复上述比较和交换的步骤,直到当前节点满足最大堆的性质或成为叶子节点。
以下是建立最大堆的Java代码:
// 建立最大堆
public static void buildMaxHeap(int[] array, int length) {
for (int i = length / 2 - 1; i >= 0; i--) {
heapify(array, length, i);
}
}
4.2 交换堆顶和最后一个元素
建立最大堆之后,最大堆的堆顶元素即为数组中的最大值。我们将堆顶元素与数组中的最后一个元素进行交换。
以下是交换堆顶和最后一个元素的Java代码:
// 交换堆顶和最后一个元素
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
4.3 维护最大当交换堆顶和最后一个元素后,我们需要维护最大堆的性质,以保证新的堆顶元素是剩下元素中的最大值。
维护最大堆的步骤如下:
- 将交换后的堆顶元素(原数组的最后一个元素)下沉到合适的位置,以满足最大堆的性质;
- 每次将当前节点与其子节点进行比较,如果存在比当前节点更大的子节点,则将其与其中较大的子节点进行交换;
- 重复上述比较和交换的步骤,直到当前节点满足最大堆的性质或成为叶子节点。
以下是维护最大堆的Java代码:
// 维护最大堆性质
public static void heapify(int[] array, int length, int i) {
int largest = i; // 当前节点
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
// 如果左子节点比当前节点大
if (left < length && array[left] > array[largest]) {
largest = left;
}
// 如果右子节点比当前节点大
if (right < length && array[right] > array[largest]) {
largest = right;
}
// 如果最大值不是当前节点,交换它们的位置并继续调整
if (largest != i) {
swap(array, i, largest);
heapify(array, length, largest);
}
}
4.4 排序
在建立最大堆和维护最大堆的基础上,我们可以进行堆排序了。
堆排序的步骤如下:
- 通过调用建立最大堆的函数,构建一个最大堆;
- 从最后一个元素开始,依次将堆顶元素与当前元素进行交换,并缩小堆的范围以排除已排序的元素;
- 重复上述交换和缩小堆范围的操作,直到只剩下一个元素。
以下是堆排序的Java代码:
// 堆排序
public static void heapSort(int[] array) {
int length = array.length;
// 建立最大堆
buildMaxHeap(array, length);
// 从最后一个元素开始,依次交换堆顶和当前元素,并调整堆
for (int i = length - 1; i >= 0; i--) {
swap(array, 0, i);
heapify(array, i, 0);
}
}
5. 手写总结
堆排序是一种高效稳定的排序算法,适用于大规模数据的排序和优先级队列的实现。它的实现步骤包括建立最大堆、交换堆顶和最后一个元素、维护最大堆性质以及排序操作。通过合理地利用最大堆的性质,堆排序能够在O(nlogn)的时间复杂度下完成排序任务。
6. 拓展案例代码
按照成绩对学生进行排名
假设有一个学生列表,每个学生包含姓名和成绩两个属性。我们可以使用堆排序按照成绩对学生进行排名。
class Student {
String name;
int score;
// 构造函数和其他方法省略...
}
public static void heapSortByScore(Student[] students) {
int length = students.length;
// 建立最大堆,按照成绩进行比较
buildMaxHeapByScore(students, length);
// 从最后一个学生开始,依次交换堆顶和当前学生,并调整堆
for (```java
int i = length - 1; i >= 0; i--) {
swap(students, 0, i);
heapifyByScore(students, i, 0);
}
}
// 建立最大堆,按照成绩进行比较
public static void buildMaxHeapByScore(Student[] students, int length) {
for (int i = length / 2 - 1; i >= 0; i--) {
heapifyByScore(students, length, i);
}
}
// 维护最大堆性质,按照成绩进行比较
public static void heapifyByScore(Student[] students, int length, int i) {
int largest = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
if (left < length && students[left].score > students[largest].score) {
largest = left;
}
if (right < length && students[right].score > students[largest].score) {
largest = right;
}
if (largest != i) {
swap(students, i, largest);
heapifyByScore(students, length, largest);
}
}
// 交换两个学生的位置
public static void swap(Student[] students, int i, int j) {
Student temp = students[i];
students[i] = students[j];
students[j] = temp;
}
使用示例:
Student[] students = new Student[5];
students[0] = new Student("Alice", 80);
students[1] = new Student("Bob", 75);
students[2] = new Student("Charlie", 90);
students[3] = new Student("David", 85);
students[4] = new Student("Eve", 70);
heapSortByScore(students);
for (Student student : students) {
System.out.println(student.name + ": " + student.score);
}
输出结果:
Charlie: 90
Alice: 80
David: 85
Bob: 75
Eve: 70
按照成绩从高到低排名的结果就是Charlie、Alice、David、Bob、Eve。
这是一个基于堆排序的简单拓展案例,可以根据具体需求进行进一步的扩展和优化。希望对你有帮助!