基本的堆排序
HeapSortBasic.java
package com.algorithm.sort;
import com.dataStructure.heap.MaxHeap;
public class HeapSortBasic {
private HeapSortBasic() {
}
// 对整个arr数组使用 HeapSortBasic 排序
// HeapSortBasic, 将所有的元素依次添加到堆中, 在将所有元素从堆中依次取出来, 即完成了排序
// 无论是创建堆的过程, 还是从堆中依次取出元素的过程, 时间复杂度均为O(nlogn)
// 整个堆排序的整体时间复杂度为O(nlogn)
public static void sort(Integer[] arr) {
int n = arr.length;
MaxHeap maxHeap = new MaxHeap(n);
// 将 arr 中的元素依次取出,插入最大堆中
for (Integer num: arr){
maxHeap.insert(num);
}
// 取出最大堆中最大的元素,赋值给 arr 中的最后一个元素
for (int i = n-1; i>=0; i--){
arr[i] = maxHeap.extractMax();
}
}
public static void main(String [] args){
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
System.out.println("排序前的数组为:");
SortTestHelper.printArray(arr);
Long start = System.currentTimeMillis();
HeapSortBasic.sort(arr);
Long end = System.currentTimeMillis();
System.out.println("-------------------------------------------");
System.out.println("排序后的数组为:");
SortTestHelper.printArray(arr);
System.out.println("-------------------------------------------");
System.out.println("排序后的数组是否有序:");
if (SortTestHelper.isSorted(arr)) {
System.out.println("数组有序~");
} else {
System.out.println("数组无序!");
}
System.out.println("-------------------------------------------");
System.out.println("排序算法的运行时间为" + " : " + (end - start) + "ms");
}
}
使用 Heapify 方式的堆排序
HeapSortHeapify.java
package com.algorithm.sort;
import com.dataStructure.heap.MaxHeap;
public class HeapSortHeapify {
private HeapSortHeapify() {
}
// 对整个arr数组使用 HeapSortHeapify 排序
// HeapSortHeapify, 借助我们的heapify过程创建堆
// 此时, 创建堆的过程时间复杂度为O(n), 将所有元素依次从堆中取出来, 实践复杂度为O(nlogn)
// 堆排序的总体时间复杂度依然是O(nlogn), 但是比 HeapSortBasic 性能更优, 因为创建堆的性能更优
public static void sort(Integer[] arr) {
int n = arr.length;
MaxHeap maxHeap = new MaxHeap(arr);
for (int i = n - 1; i>=0;i--){
arr[i] = maxHeap.extractMax();
}
}
public static void main(String[] args){
int N = 1000000;
Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);
System.out.println("排序前的数组为:");
SortTestHelper.printArray(arr);
Long start = System.currentTimeMillis();
HeapSortHeapify.sort(arr);
Long end = System.currentTimeMillis();
System.out.println("-------------------------------------------");
System.out.println("排序后的数组为:");
SortTestHelper.printArray(arr);
System.out.println("-------------------------------------------");
System.out.println("排序后的数组是否有序:");
if (SortTestHelper.isSorted(arr)) {
System.out.println("数组有序~");
} else {
System.out.println("数组无序!");
}
System.out.println("-------------------------------------------");
System.out.println("排序算法的运行时间为" + " : " + (end - start) + "ms");
}
}
MaxHeap.java
package com.dataStructure.heap;
import java.lang.*;
public class MaxHeap {
protected Integer[] data;
protected int count;
protected int capacity;
// 构造函数,构造一个空的堆,可以容纳 capacity 个元素
public MaxHeap(int capacity) {
data = new Integer[capacity + 1];
count = 0;
this.capacity = capacity;
}
// 构造函数,用 Heapify 的方式使用数组构建最大堆
// 根据堆的性质, 只要保证部分有序即可, 即根节点大于左右节点的值.
// 将数组抽象为一个完全二叉树, 所以只要从【最后一个非叶子节点】向前遍历每一个节点即可.
// 如果当前节点比左右子树节点都大, 则已经是一个最大堆, 否则将当前节点与左右节点较大的一个交换。
public MaxHeap(Integer[] arr){
int n = arr.length;
data = new Integer[n+1];
capacity = n;
for (int i =0; i<n;i++){
data[i+1] = arr[i];
}
count = n;
// 从最后一个非叶子节点向前遍历到根节点,从每一个节点进行 shiftDown 操作
for (int i = count/2; i >= 1; i--){
shiftDown(i);
}
}
// 返回堆中元素的个数
public int size() {
return count;
}
// 返回一个布尔值,表示堆是否为空
public boolean isEmpty() {
return count == 0;
}
// 向最大堆中插入一个新的元素 item
public void insert(Integer item) {
if (count + 1 > capacity) {
System.out.println("元素数量已经达到堆容量的最大值!");
return;
}
// 先将 item 放在堆的末尾
data[count + 1] = item;
count++;
// 上浮 item
shiftUp(count);
}
// 将堆中 k 位置的元素 上浮 到合适的位置
private void shiftUp(int k) {
// 如果 当前节点(k指向的位置) 不是根节点,且 父节点 < 当前节点
// 进行循环
while (k > 1 && data[k / 2].compareTo(data[k]) < 0) {
swap(k, k / 2);
k /= 2;
}
}
// 将堆中 i 和 j 位置的元素进行交换
private void swap(int i, int j) {
Integer temp = data[i];
data[i] = data[j];
data[j] = temp;
}
// 从堆中取出堆顶元素,即堆中所存储的最大元素
public Integer extractMax() {
if (count <= 0 ){
System.out.println("堆已空!");
return null;
}
Integer ret = data[1];
swap(1, count);
count--;
shiftDown(1);
return ret;
}
// 将堆顶元素 下沉 到合适的位置
private void shiftDown(int k){
while (2*k <= count){
int j = 2*k;
// 如果 有 右子节点 且 右子节点 > 左子节点
if (j+1 <= count && data[j+1].compareTo(data[j]) > 0){
// j 移动到 右子节点 处
j++;
}
// 如果 根节点 > 子节点中较大者
if (data[k].compareTo(data[j]) >= 0){
// 跳出循环
break;
}
// 交换 根节点 和 子节点中较大者
swap(k, j);
// 移动 k
k = j;
}
}
public static void main(String[] args) {
MaxHeap maxHeap = new MaxHeap(100);
int N = 50; // 堆中元素个数
int M = 100; // 堆中元素取值范围[0, M)
for (int i = 0; i < N; i++)
maxHeap.insert(new Integer((int) (Math.random() * M)));
System.out.println(maxHeap.size());
// for (Integer item : maxHeap.data) {
// System.out.print(item + " ");
// }
while (maxHeap.count != 0){
System.out.print(maxHeap.extractMax() + " ");
}
}
}