堆排序(Java实现)

《算法导论》中堆排序主要将其分为堆的性质维护堆的性质建堆堆排序算法

堆的性质:给定一个结点的下标i,很容易计算得到它的父结点、左孩子和右孩子的下标(伪代码):

PARENT(i)
    return i/2

LEFT(i)
    return 2i

RIGHT(i)
    return 2i+1

这里针对下标从1开始的数组,然而实际上我们涉及的数组都是从0开始。为了改进上面的伪代码,可以使用移位来解决,其伪代码:

PARENT(i)
    return (i-1)>>1

LEFT(i)
    return ((i+1)<<1)-1

RIGHT(i)
    return (i+1)<<1

 维护堆的性质(MAX-HEAPIFY):

MAX-HEAPIFY是用于维护最大堆性质的重要过程。他输入为一个数据A和一个下标i,在调用MAX-HEAPIFY的时候,假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,但这时A[i]有可能小于其孩子,这样就违背了最大堆的性质。MAX-HEAPIFY通过让A[i]的值在最大堆中“逐级下降”,从而使得以下标i为根结点的子树重新遵循最大堆的性质。其伪代码如下所示:

MAX-HEAPIFY(A, i)
    l = LEFT(i);
    r = RIGHT(i);
    if l <= A.heap-size and A[l] > A[i]
        largest = largest
    else largest = i;
    if r <= A.heap-size and A[r] > A[largest]
        largest = r
    if largest != i
        exchangeA[i]withA[largest]
        MAX-HEAPIFY(A, largest)

用Java语言实现维护堆的性质:MAXHeapify.java

package heapsort;

public class MaxHeapify {

    public void heapAdjust(int[] A, int i, int len){

        int l = Left(i);
        int r = Right(i);
        int largest = i;//假设父节点值最大

        if (l < len && A[l] > A[i]) {//左孩子值大于父节点值
            largest = l;
        }

        if (r < len && A[r] > A[largest]) {//右孩子值大于父节点值
            largest = r;
        }

        if (largest != i) {
            //exchange A[i]withA[largest]
            int tmp = A[i] ;
            A[i] = A[largest];
            A[largest] = tmp;
            heapAdjust(A, largest, len);
        }

    }

    private int Right(int i) {//右孩子坐标
        return ((i+1)<<1);
//        return 2*i+1;
    }

    private int Left(int i) {//左孩子坐标
        return ((i+1)<<1)-1;
//        return 2*i;
    }

}

测试维护堆的性质代码即:MaxHeapifyTest.java

package heapsort;

public class MaxHeapifyTest {

    public static void main(String[] args) {
        int[] A ={16, 4, 10, 14, 7, 9, 3, 2, 8, 1};
//        int[] A={5, 2, 4, 6, 1, 3, 2, 6};
        MaxHeapify maxHeapify = new MaxHeapify();
        maxHeapify.heapAdjust(A, 1, A.length);
        for (int i = 0; i < A.length; i++) {
            System.out.print(A[i]+" ");
        }
    }

}

测试结果:

16 14 10 8 7 9 3 2 4 1 

建堆(BUILD-MAX-HEAPIFY):

用自底向上的方法利用过程MAX-HEAPIFY把一个大小为n=A.length的数组A[1..n]转换为最大堆。通过一个证明:当用数组表示存储n个元素的堆时叶节点下标分别为floor(n/2)+1,floor(n/2)+2,...,n。可知道,子数组A[floor(n/2)+1..n]中的元素都是树的叶结点。每个叶结点都可以看成只包含一个元素的堆。过程BUILD-MAX-HEAPIFY对树中的其他结点都调用一次MAX-HEAPIFY。其伪代码如下:

BUILD-MAX-HEAP(A)
 A.heap-size = A.length
 for i = floor(A.length/2) downto 1
    MAX-HEAPIFY(A, i)

用Java语言实现BUILD-MAX-HEAPIFY功能即:BuildHeap.java

package heapsort;

public class BuildHeap {

    public void buildMaxHeap(int[] A) {
        for (int i = A.length/2-1; i >= 0; i--) {
            MaxHeapify maxHeapify = new MaxHeapify();
            maxHeapify.heapAdjust(A, i, A.length);
        }

    }

}

测试建堆的功能即BuildHeapTest.java:

package heapsort;

public class BuildHeapTest {

    public static void main(String[] args) {
        int[] A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
        BuildHeap buildHeap = new BuildHeap();
        buildHeap.buildMaxHeap(A);
        for (int i = 0; i < A.length; i++) {
            System.out.print(A[i]+" ");
        }
    }

}

测试结果:

16 14 10 8 7 9 3 2 4 1 

堆排序算法

初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组A[1..n]建成最大堆,其中n=A.length。因为数组中的最大元素总在根结点A[1]中,通过把它与A[n]进行互换,我们可以让该元素放到正确的位置。这时候,如果我们从堆中去掉结点n(这一操作可以通过减少A.heap-size的值来实现),剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会违背最大队的性质。为了维护最大堆的性质,我们要做的是调用MAX-HEAPIFY(A, 1),从而在A[1..n-1]上构造一个新的最大堆。堆排序算法会不断重复这一过程,直到堆的大小从n-1降到2.

HEAPSORT(A)
    BUILD-MAX-HEAP(A)
    for i = A.length downto 2
        exchange A[1] with A[i]
        A.heapsize = A.heapsize-1
        MAX-HEAPIFY(A, 1)

注意:本段伪代码数组下标从1开始,要想实现该段伪代码时候,要将下标改为0开始。

用Java代码实现堆排序即HeapSort.java:

package heapsort;

public class HeapSort {

    public HeapSort(int[] A) {

        BuildHeap buildHeap = new BuildHeap();
        buildHeap.buildMaxHeap(A);

        for (int i = A.length-1; i > 0; i--) {
            //exchange A[1]withA[i]
            int tmp = A[0];
            A[0] = A[i];
            A[i] = tmp;
            //维护堆的性质
            MaxHeapify maxHeapify = new MaxHeapify();
            maxHeapify.heapAdjust(A, 0, i);//A[0]为根结点
        }
    }
}

测试堆排序算法即HeapSortTest.java:

package heapsort;

public class HeapSortTest {

    public static void main(String[] args) {
        int[] A = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};
        HeapSort heapSort = new HeapSort(A);
        for (int j = 0; j < A.length; j++) {
            System.out.print(A[j]+" ");
        }
    }

}

测试结果:

1 2 3 4 7 8 9 10 14 16 
时间: 2024-10-05 04:56:05

堆排序(Java实现)的相关文章

堆排序JAVA实现

package kpp.sort; /** * 堆的定义如下: n个元素的序列{k0,k1,...,ki,…,k(n-1)}当且仅当满足下关系时,称之为堆. " ki<=k2i,ki<=k2i+1;或ki>=k2i,ki>=k2i+1.(i=1,2,…,[n/2])" 若将和此次序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树, 则完全二叉树中每一个节点的值的都大于或等于任意一个字节的值(如果有的话),称之为大顶堆. 则完全二叉树中每一个

第6章 堆排序 java实现 最简版

只考虑int类型的排序,以后考虑泛型实现. 1 public class Heap { 2 3 public int heap_size;//在build_max_heap中初始化,由heap_sort自动调用 4 5 public int parent(int i){ 6 return (i - 1) / 2; 7 } 8 public int left(int i){ 9 return 2 * i + 1; 10 } 11 public int right(int i){ 12 return

堆排序(JAVA)

package org.rev.algorithm; /**  * 堆排序,时间复杂度为O(nlogn),是利用堆的性质进行的一种选择排序.  *   * 大顶堆是一个完全二叉树,所有的父节点都大于或等于它的左右子节点,即a[i]>=a[2i+1]&&a[i]>=a[2i+2].   *(小顶堆是父节点<=子节点)  *   * 对于完全二叉树,任意节点a[i]的父节点的索引值是(i-1) / 2 向下取整.  *   * 1.对于序列a[0]-a[n-1],构建大顶堆,

排序算法(三)堆排序及有界堆排序Java实现及分析

1.堆排序基数排序适用于大小有界的东西,除了他之外,还有一种你可能遇到的其它专用排序算法:有界堆排序.如果你在处理非常大的数据集,你想要得到前 10 个或者前k个元素,其中k远小于n,它是很有用的. 例如,假设你正在监视一 个Web 服务,它每天处理十亿次事务.在每一天结束时,你要汇报最大的k个事务(或最慢的,或者其它最 xx 的).一个选项是存储所有事务,在一天结束时对它们进行排序,然后选择最大的k个.需要的时间与nlogn成正比,这非常慢,因为我们可能无法将十亿次交易记录在单个程序的内存中.

leetcode215.数组中的第k个最大元素(快排/堆排序 java)***

class Solution { public int findKthLargest(int[] nums, int k) { int len=nums.length; int left=0,right=len-1; int target=len-k; //第k大的元素的数组下标就是len-k,可通过找规律得出 while(true){ int index=partition(nums,left,right); //index用来判断nums[left]排在数组中的第几位大小,并将其移到对应的位

堆排Java代码实现

堆排复习: 结论:堆排算法时间复杂度为O(nlgn),额外空间复杂度为O(1); 在开始堆排序过程之前先要熟悉两个结构 1,完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1-h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树.(摘自百度百科) 大白话:说白了就是建立二叉树的过程中,二叉树每一层都是满节点的,最后一层的节点不许中间有空缺: 2,大根堆:大根堆要求根节点的关键字既大于或等于左子树的关键字值,又大于或等于右子树的关键字值.(摘自百

堆排序算法学习小记

1.完全二叉树的概念 若设二叉树的深度为h,除第 h 层外,其它各层 (1-h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树. 完全二叉树是由满二叉树而引出来的.对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树. (1)所有的叶结点都出现在第k层或k-l层(层次最大的两层) (2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l. 一棵二叉树至多只有最下面

八种排序算法(内部排序)

八种排序算法很长时间没有使用了,今天做一个总结,方便以后自己用的时候参考. 这八种排序算法都是内部算法,这八种排序算法分别是: 1. 插入排序 1)直接插入排序 2)希尔排序 2.选择排序 1)简单选择排序 2)堆排序 3.交换排序 1)冒泡排序 2)快速排序 4.归并排序 5.基数排序 一.直接插入排序 将一个记录插入到已经排好序的有序表中,从而得到一个新的.记录数增1的有序表.在实际操作中,先将序列的第一个记录看成是一个有序的子序列,然后从第二个.第三个.……记录逐个进行插入,直至整个序列有

leetcode 做过的题目总结

双指针: leetcode167. 两数之和 II - 输入有序数组(双指针) leetcode 633.平方数之和(双指针 java) leetcode 345.反转字符串中的元音字母(双指针 java)(有地方不懂) leetcode 680.验证回文字符串 II(双指针 java) leetcode 88.合并两个有序数组(双指针 java) leetcode 141.环形链表(双指针 java) 排序: leetcode215.数组中的第k个最大元素(快排/堆排序 java)*** 只写