堆排序(java实现)

说到堆排序不得不说一下堆这种数据结构。这里的堆指二叉堆,本质是一个数组。可看作一个近似的完全二叉树(完全二叉树是所有叶子节点深度相同,且所有内部节点度为二的二叉树)。二叉堆有两种表现形式,最大堆和最小堆。最大堆中最大元素在二叉树的根结点,最小堆正好相反。我们的目的就是利用最大堆的特点,完成排序。

先上一段调试好的代码

package algorithms.portion6.heap;

/**
 * desc : 堆排序
 * Created by tiantian on 2018/7/24
 */
public class HeapSort {
    
    public static int heapSize = 0;
    // 维护最大堆
    public static void maxHeapfy(Integer[] array, int i) {
        int l = Node.left(i);
        int r = Node.right(i);
        
        int largest;
        if (l <= heapSize && array[l] > array[i]) {
            largest = l;
        } else {
            largest = i;
        }
        
        if (r <= heapSize && array[r] > array[largest]) {
            largest = r;
        }
        
        if (largest != i) {
            exchange(array, i, largest);
            maxHeapfy(array, largest);
        }
    }
    
    // 构建最大堆
    public static void builtMaxHeap(Integer[] array) {
        int i = (array.length)/2;
        while (i >= 0) {
            maxHeapfy(array, i);
            i--;
        }
    }
    
    // 排序
    public static void sort(Integer[] array) {
        heapSize = array.length-1;
        builtMaxHeap(array);
        int len = array.length;
        for (int i = len-1; i >= 0 ; i--) {
            exchange(array, 0, i);
            heapSize = heapSize - 1;
            maxHeapfy(array,0);
        }
    }
    
    public static void exchange(Integer[] array, int a, int b) {
        Integer temp = array[a];
        array[a] = array[b];
        array[b] = temp;
    }
    
    // 返回堆元素父节点,或者左右子节点。
    public static class Node {
        public static int parent(int i) {
            return i/2;
        } 
        
        public static int left(int i) {
            return 2*i+1;
        }
        
        public static int right(int i) {
            return 2 * i + 2;
        }
    }
}

客户端测试:

public class Client {
    public static void main(String[] args) {
        Integer[] testArray = {5,10,25,18,12,3,29,12,50,22,23,0,51,1,9,90,26,3,99,7};
//        HeapSort.builtMaxHeap(testArray);
//        for (Integer i : testArray) {
//            System.out.print(i+",");
//        }
        System.out.println("排序以下数:");
        for (int i = 0; i < testArray.length; i++) {
            System.out.print(testArray[i] + ",");
        }
        System.out.println("");
        System.out.println("排序结果:");

        HeapSort.sort(testArray);
        for (int i = 0; i < testArray.length; i++) {
            System.out.print(testArray[i] + ",");
        }
    }
}

输出:

排序以下数:
5,10,25,18,12,3,29,12,50,22,23,0,51,1,9,90,26,3,99,7,
排序结果:
0,1,3,3,5,7,9,10,12,12,18,22,23,25,26,29,50,51,90,99,

这里有三个方法说明一下。第一个maxHeapfy方法接收一个数组和一个下标i,目的是使以下标为i的节点为根元素的堆变成最大堆。通过代码可以知道这一过程是不断递归调用maxHeapfy完成堆的最大化。exchange方法是交换两个数。builtMaxHeap方法是构建一个最大堆。这里heapSize变量一定要非常注意,它的作用是sort方法的循环的每次都使maxHeapfy最大化堆的范围不断缩小,避免弄乱已有序的元素。

最后总结下,堆排序是原址的或者说原地的,因为排好序的结果就是原来的数组。这里和之前的博文中讲过的归并排序比较下,归并排序的合并阶段是要借助另一数组辅助才能完成。堆排序时间复杂度为O(NlogN)。和归并一样是一种渐进最优的比较排序算法。

猜你喜欢

转载自blog.csdn.net/m0_37577221/article/details/81229247