在普通堆排序中,原始的数组必须要先添加进一个maxHeap堆,再将堆中的元素取出来放到一个temp数组,这个堆和数组都需要消耗额外的空间,是非原地排序,复杂度都是O(nlogn)
因此可以对这两个过程进行优化:
- 不用将原始数组添加进一个额外的堆,而是直接将原始数组看成一个堆,用heapify()方法让其满足最大堆的条件,这种方法只考虑非叶子节点,省去了一半的元素,时间复杂度为O(n)
- 不用将堆中的元素取出来放在一个额外的数组,而是直接将这个堆按照从小到大的顺序进行原地排序,因为第一个元素始终是最大的,将第一个和最后一个元素互换,即最大值放到数组末尾,然后将堆的范围减1变为[0, size - 1),再将第一个元素进行siftDown,让左边n - 1个元素恢复成最大堆,继续进行以上操作,最后的数组就是升序排列的,时间复杂度为O(n)
heapify()方法性能比较
import java.util.Random;
public class Algorithm {
public static void main(String[] args) {
int n = 10000000;
Random random = new Random();
Integer[] testData = new Integer[n];
for (int i = 0; i < n; i++) {
testData[i] = random.nextInt(Integer.MAX_VALUE);
}
testHeap(testData, false);
testHeap(testData, true);
}
public static void testHeap(Integer[] testData, boolean isHeapify){
long startTime = System.nanoTime();
MaxHeap<Integer> maxHeap;
if (isHeapify){
maxHeap = new MaxHeap<>(testData);
long endTime = System.nanoTime();
System.out.println("heapify: " + (endTime - startTime) / 1000000000.0 + "秒");
}
else {
maxHeap = new MaxHeap<>();
for (int i : testData){
maxHeap.add(i);
}
long endTime = System.nanoTime();
System.out.println("simple: " + (endTime - startTime) / 1000000000.0 + "秒");
}
int[] temp = new int[testData.length];
for (int i = 0; i < testData.length; i++) {
temp[i] = maxHeap.extractMax();
}
for (int i = 0; i < arr.length - 1; i++) {
if (temp[i] < temp[i + 1]){
throw new IllegalArgumentException("二叉堆实现失败");
}
}
}
}
class MaxHeap<E extends Comparable<E>>{
private Array<E> heap;
public MaxHeap(int capacity){
heap = new Array<>(capacity);
}
public MaxHeap(){
heap = new Array<>(10);
}
/**
* 添加一个构造方法,用heapify()方法直接将一个原始数组变成最大堆
*/
public MaxHeap(E[] arr){
heap = new Array<>(arr);
heapify(arr);
}
public int size(){
return heap.getSize();
}
public boolean isEmpty(){
return size() == 0;
}
private int parent(int index){
if (index == 0) {
throw new IllegalArgumentException("根节点没有父节点");
}
return (index - 1) / 2;
}
private int leftChild(int index){
return 2 * index + 1;
}
private int rightChild(int index){
return 2 * index + 2;
}
public void add(E e){
heap.addLast(e);
siftUp(size() - 1);
}
private void siftUp(int index){
while (index > 0 && heap.get(index).compareTo(heap.get(parent(index))) > 0){
heap.swap(index, parent(index));
index = parent(index);
}
}
public E findMax(){
if (size() == 0){
throw new IllegalArgumentException("堆为空");
}
return heap.get(0);
}
public E extractMax(){
E max = findMax();
heap.swap(0, size() - 1);
heap.remove(size() - 1);
siftDown(0);
return max;
}
private void siftDown(int index){
while (leftChild(index) < size()){
int max = leftChild(index);
if (rightChild(index) < size() && heap.get(leftChild(index)).compareTo(heap.get(rightChild(index))) < 0){
max = rightChild(index);
}
if (heap.get(index).compareTo(heap.get(max)) >= 0){
break;
}
heap.swap(index, max);
index = max;
}
}
/**
* heapify()方法将数组看成是一个不满足条件的堆
* 从最后一个非叶子节点开始,依次进行SiftDown操作,直到成为最大堆
* 最后一个非叶子节点,就是最后一个节点的父节点
*/
public void heapify(E[] arr){
if (arr.length > 1){
for (int i = parent(arr.length - 1); i >= 0; i--) {
siftDown(i);
}
}
}
/**
* replace()方法替换最大节点,即根节点
* 先保存根节点,将根节点直接替换,然后再用SiftDown()方法满足条件
*/
public E replace(E e){
E max = findMax();
heap.set(e, 0);
siftDown(0);
return max;
}
}
class Array<E>{
private E[] data;
private int size;
public Array(int capacity){
data = (E[]) new Object[capacity];
size = 0;
}
/**
* 添加一个构造方法,可以直接传入一个数组
*/
public Array(E[] arr){
data = (E[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++) {
data[i] = arr[i];
}
size = arr.length;
}
public Array(){
data = (E[]) new Object[10];
size = 0;
}
public int getSize(){
return size;
}
public void swap(int index1, int index2){
E temp;
temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
public E get(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
return data[index];
}
public void set(E e, int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
data[index] = e;
}
public void add(E e, int index){
if (index < 0 || index > size){
throw new IllegalArgumentException("索引值非法");
}
if (size == data.length){
resize(2 * data.length);
}
for (int i = size - 1; i >= index; i++) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
public void addLast(E e){
add(e, size);
}
public void remove(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
data[size] = null;
if (size == data.length / 2 && data.length / 2 != 0){
resize(data.length / 2);
}
}
public void resize(int newCapacity){
E[] temp = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
temp[i] = data[i];
}
data = temp;
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
str.append("[");
for (int i = 0; i < size; i++) {
str.append(data[i]);
if (i != size - 1){
str.append(", ");
}
}
str.append("]");
return str.toString();
}
}
优化的原地堆排序性能比较
import java.util.Arrays;
import java.util.Random;
public class Algorithm {
public static void main(String[] args) {
Integer[] testScale = {1000000, 10000000};
for (Integer n : testScale){
Integer[] randomArr = ArrayGenerator.generatorRandomArray(n, n);
Integer[] sortedArr = ArrayGenerator.generatorSortedArray(n, n);
Integer[] arr1 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr3 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr5 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr7 = Arrays.copyOf(randomArr, randomArr.length);
Integer[] arr2 = Arrays.copyOf(sortedArr, sortedArr.length);
Integer[] arr4 = Arrays.copyOf(sortedArr, sortedArr.length);
Integer[] arr6 = Arrays.copyOf(sortedArr, sortedArr.length);
Integer[] arr8 = Arrays.copyOf(sortedArr, sortedArr.length);
System.out.println("测试随机数组排序性能");
System.out.println();
Verify.testTime("HeapSort", arr1);
Verify.testTime("HeapSortOptimized", arr3);
Verify.testTime("QuickSort3Ways", arr5);
Verify.testTime("MergeSort", arr7);
System.out.println();
System.out.println("测试有序数组排序性能");
System.out.println();
Verify.testTime("HeapSort", arr2);
Verify.testTime("HeapSortOptimized", arr4);
Verify.testTime("QuickSort3Ways", arr6);
Verify.testTime("MergeSort", arr8);
System.out.println();
}
}
}
class HeapSort<E>{
private HeapSort(){}
public static<E extends Comparable<E>> void sort(E[] arr){
MaxHeap<E> maxHeap = new MaxHeap<>();
for (E e: arr){
maxHeap.add(e);
}
for (int i = arr.length - 1; i >= 0; i--) {
arr[i] = maxHeap.extractMax();
}
}
/**
* 原地堆排序
* 因为不需要创建MaxHeap类对象,所以重写了heapify()方法实现一个最大堆
* 然后对arr[0, range)范围内的元素进行siftDown
* siftDown()方法默认的数组范围是n,因此需要重新修改一些地方
*/
public static<E extends Comparable<E>> void sortOptimized(E[] arr){
heapify(arr);
for (int i = arr.length - 1; i >= 0; i--) {
swap(arr, 0, i);
siftDown(arr, 0, i);
}
}
public static<E extends Comparable<E>> void heapify(E[] arr){
if (arr.length > 1){
for (int i = (arr.length - 2) / 2; i >= 0; i--) {
siftDown(arr, i, arr.length);
}
}
}
public static<E extends Comparable<E>> void siftDown(E[] arr, int index, int range){
while (2 * index + 1 < range) {
int max = 2 * index + 1;
if (max + 1 < range && arr[max].compareTo(arr[max + 1]) < 0) {
max++;
}
if (arr[index].compareTo(arr[max]) >= 0) {
break;
}
swap(arr, index, max);
index = max;
}
}
public static<E extends Comparable<E>> void swap(E[] arr, int index1, int index2){
E temp;
temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
}
class QuickSort {
private QuickSort() {
}
public static<E extends Comparable<E>> void sort3ways(E[] arr){
Random random = new Random();
E temp = null;
sort3ways(arr, 0, arr.length - 1, temp, random);
}
public static<E extends Comparable<E>> void sort3ways(E[] arr, int left, int right, E temp, Random random){
if (left >= right){
return;
}
int[] res = partition3ways(arr, left, right, temp, random);
sort3ways(arr, left, res[0], temp, random);
sort3ways(arr, res[1], right, temp, random);
}
public static<E extends Comparable<E>> int[] partition3ways(E[] arr, int left, int right, E temp, Random random){
int p = random.nextInt(right - left + 1) + left;
swap(arr, p, left, temp);
int i = left + 1;
int lt = left;
int gt = right + 1;
while (i < gt){
if (arr[i].compareTo(arr[left]) < 0){
lt++;
swap(arr, lt, i, temp);
i++;
}
else if (arr[i].compareTo(arr[left]) == 0){
i++;
}
else if (arr[i].compareTo(arr[left]) > 0){
gt--;
swap(arr, gt, i, temp);
}
}
swap(arr, lt, left, temp);
int[] res = {lt - 1, gt};
return res;
}
public static<E extends Comparable<E>> void swap(E[] arr, int i, int j, E temp){
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
class MergeSort {
private MergeSort(){}
public static<E extends Comparable<E>> void sort(E[] arr){
E[] temp = Arrays.copyOf(arr, arr.length);
sort(arr, 0, arr.length - 1, temp);
}
private static<E extends Comparable<E>> void sort(E[] arr, int left, int right, E[] temp){
if (left >= right){
return;
}
int mid = left + (right - left) / 2;
sort(arr, left, mid, temp);
sort(arr, mid + 1, right, temp);
if (arr[mid].compareTo(arr[mid + 1]) > 0) {
merge(arr, left, mid, right, temp);
}
}
public static<E extends Comparable<E>> void merge(E[] arr, int left, int mid, int right, E[] temp){
int i = left;
int j = mid + 1;
System.arraycopy(arr, left, temp, left, right - left + 1);
for (int n = left; n < right + 1; n++) {
if (i == mid + 1){
arr[n] = temp[j];
j++;
}
else if (j == right + 1) {
arr[n] = temp[i];
i++;
}
else if (temp[i].compareTo(temp[j]) <= 0) {
arr[n] = temp[i];
i++;
}
else{
arr[n] = temp[j];
j++;
}
}
}
}
class MaxHeap<E extends Comparable<E>>{
private Array<E> heap;
public MaxHeap(int capacity){
heap = new Array<>(capacity);
}
public MaxHeap(){
heap = new Array<>(10);
}
public MaxHeap(E[] arr){
heap = new Array<>(arr);
heapify(arr);
}
public int size(){
return heap.getSize();
}
public boolean isEmpty(){
return heap.getSize() == 0;
}
public int parent(int index){
if (index == 0) {
throw new IllegalArgumentException("根节点没有父节点");
}
return (index - 1) / 2;
}
private int leftChild(int index){
return 2 * index + 1;
}
private int rightChild(int index){
return 2 * index + 2;
}
public void add(E e){
heap.addLast(e);
siftUp(size() - 1);
}
private void siftUp(int index){
while (index > 0 && heap.get(index).compareTo(heap.get(parent(index))) > 0){
heap.swap(index, parent(index));
index = parent(index);
}
}
public E findMax(){
if (size() == 0){
throw new IllegalArgumentException("堆为空");
}
return heap.get(0);
}
public E extractMax(){
E max = findMax();
heap.swap(0, size() - 1);
heap.remove(size() - 1);
siftDown(0);
return max;
}
public void siftDown(int index){
while (leftChild(index) < size()){
int max = leftChild(index);
if (rightChild(index) < size() && heap.get(leftChild(index)).compareTo(heap.get(rightChild(index))) < 0){
max = rightChild(index);
}
if (heap.get(index).compareTo(heap.get(max)) >= 0){
break;
}
heap.swap(index, max);
index = max;
}
}
public void heapify(E[] arr){
if (arr.length > 1){
for (int i = parent(arr.length - 1); i >= 0; i--) {
siftDown(i);
}
}
}
public E replace(E e){
E max = findMax();
heap.set(e, 0);
siftDown(0);
return max;
}
}
class Array<E>{
private E[] data;
private int size;
public Array(int capacity){
data = (E[]) new Object[capacity];
size = 0;
}
public Array(E[] arr){
data = (E[]) new Object[arr.length];
for (int i = 0; i < arr.length; i++) {
data[i] = arr[i];
}
size = arr.length;
}
public Array(){
data = (E[]) new Object[10];
size = 0;
}
public int getSize(){
return size;
}
public void swap(int index1, int index2){
E temp;
temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
public E get(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
return data[index];
}
public void set(E e, int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
data[index] = e;
}
public void add(E e, int index){
if (index < 0 || index > size){
throw new IllegalArgumentException("索引值非法");
}
if (size == data.length){
resize(2 * data.length);
}
for (int i = index - 1; i >= index; i++) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
public void addLast(E e){
add(e, size);
}
public void remove(int index){
if (index < 0 || index >= size){
throw new IllegalArgumentException("索引值非法");
}
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
data[size] = null;
if (size == data.length / 2 && data.length / 2 != 0){
resize(data.length / 2);
}
}
public void resize(int newCapacity){
E[] temp = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++) {
temp[i] = data[i];
}
data = temp;
}
@Override
public String toString(){
StringBuilder str = new StringBuilder();
str.append("[");
for (int i = 0; i < size; i++) {
str.append(data[i]);
if (i != size - 1){
str.append(", ");
}
}
str.append("]");
return str.toString();
}
}
class ArrayGenerator {
private ArrayGenerator (){}
public static Integer[] generatorRandomArray (Integer n, Integer maxBound){
Integer[] arr = new Integer[n];
Random random = new Random();
for (int i = 0; i < n; i++) {
arr[i] = random.nextInt(maxBound);
}
return arr;
}
public static Integer[] generatorSortedArray (Integer n, Integer maxBound){
Integer[] arr = new Integer[n];
for (int i = 0; i < n; i++) {
arr[i] = i;
}
return arr;
}
}
class Verify {
private Verify (){}
public static<E extends Comparable<E>> boolean isSorted(E[] arr){
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i].compareTo(arr[i + 1]) > 0) {
return false;
}
}
return true;
}
public static<E extends Comparable<E>> void testTime(String AlgorithmName, E[] arr) {
long startTime = System.nanoTime();
if (AlgorithmName.equals("HeapSort")) {
HeapSort.sort(arr);
}
if (AlgorithmName.equals("HeapSortOptimized")) {
HeapSort.sortOptimized(arr);
}
if (AlgorithmName.equals("QuickSort3Ways")) {
QuickSort.sort3ways(arr);
}
if (AlgorithmName.equals("MergeSort")) {
MergeSort.sort(arr);
}
long endTime = System.nanoTime();
if (!Verify.isSorted(arr)){
throw new RuntimeException(AlgorithmName + "算法排序失败!");
}
System.out.println(String.format("%s算法,测试用例为%d,执行时间:%f秒", AlgorithmName, arr.length, (endTime - startTime) / 1000000000.0));
}
}