堆的定义,堆与树的区别,堆排序(Java实现)

堆的定义(Heap)

堆是一个特殊的数据结构,可以看作是用数组实现的二叉树。

它是一个完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不 是满的,那么要求左满右不满。

数组实现的具体方法就是将二叉树的结点按照层级顺序放入数组中,根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推。

堆的每个结点都大于等于它的两个子结点。这里要注意堆中仅仅规定了每个结点大于等于它的两个子结点,但这两个子结点的顺序并没有做规定,跟二叉查找树是有区别的。 

怎么去确定结点位置呢?如果一个结点的位置为k,则它的父结点的位置为k/2,而它的两个子结点的位置则分别为2k和2k+1。这样在不使用指针的情况下,我们也可以通过计算数组的索引在树中上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或2k+1。 

 

堆与树之间的区别

结点的排序:在二叉查找树中,左子节点必须比父节点小,右子节点必须必比父节点大。但是在堆中并非如此,在最大堆中两个子节点都必须比父节点小,而在最小堆中,它们都必须比父节点大,就像是从大到小或者从小排序的一个数组。

内存占用:普通的树所占用的内存会更多,因为它需要指针,指向左右子结点,而堆不用,只需要储存一个数组就可以了,因为它规定了子父结点之间的关系。

平衡以及作用:普通的树更强调平衡性,只有在平衡的时候,树的快速查找才能体现出来,而堆并不需要,它更多的是利用堆排序来进行增删,它们的职能是不一样的。

 

堆的API设计(最大堆)

 

 

public class Heap<T extends Comparable<T>> {
    private T[] items;
    private int N;

    public Heap(int capacity) {
        //capacity+1是因为索引从1开始储存数据
        this.items = (T[]) new Comparable[capacity + 1];
        this.N = 0;
    }

        // 判断索引i处的值是否小于索引j处的值
    private boolean less(int i,int j){
        return items[i].compareTo(items[j])<0;
    }
    // 交换i处和j处的值
    private void exchange(int i,int j){
        T temp = items[i];
        items[i]=items[j];
        items[j]=temp;
    }
    // 往堆中插入元素
    public void insert(T t){
        items[++N]=t;
        //添加之后进行上浮排序
        swim(N);
    }
    // 删除堆中最大的元素,并返回这个最大元素
    public T delMax(){
        //就是将第一个元素和最后一个元素交换,再让最后索引为null,最后下沉第一个元素
        T max = items[1];
        exchange(1,N);
        items[N] = null;
        N--;
        // 通过下沉算法调整堆的排序
        sink(1);
        return max;
    }
    // 上浮算法,使得添加的元素在合适的位置
    public void swim(int k){
        // 通过循环不断比较与父结点的大小,若比父结点大,就换位置上浮
        while (k > 1) {
            if (less(k/2,k)){
                exchange(k/2,k);
            }
            k=k/2;
        }
    }
    // 下沉算法
    private void sink(int k){
        // 通过循环不断比较当前结点k与其左子结点2k和右子结点2k+1中的较大值
        while (2*k<=N){
            int max;
            if (2 * k + 1 <= N) {
                if (less(2*k,2*k+1)){
                    max=2*k+1;
                }else {
                    max=2*k;
                }
            }else {
                max=2*k;
            }
            if (!less(k,max)){
                break;
            }
            exchange(k,max);
            k=max;
        }
    }
}

 

堆排序

排序原理:堆排序分为两个过程,一个是堆有序的构建,二是堆的排序。

  1. 首先在堆索引的一半处开始倒叙遍历,对得到的每一个元素做下沉操作,得到一个有序的完全二叉树(此时只是树形结构上的有序,而不是堆的有序)。
  2. 再通过将首结点与末结点交换,在排除此时末结点的范围内对首结点进行下沉,往复筛选全部元素的方法,实现堆排序。

 

 

堆排序的API设计

public class HeapSort {
    //对source数组中的数据从小到大排序
    public static void sort(Comparable[] source) {
        //创建一个比原数组大1的数组
        Comparable[] heap = new Comparable[source.length + 1];
        //构造堆
        createHeap(source,heap);
        //堆排序
        //定义一个变量,记录heap中未排序的所有元素中最大的索引
        int N = heap.length-1;
        while(N!=1){
            //交换heap中索引1处的元素和N处的元素
            exchange(heap,1,N);
            N--;
            //对索引1处的元素在0~N范围内做下沉操作
            sink(heap,1,N);
        }
        //heap中的数据已经有序,拷贝到source中
        System.arraycopy(heap,1,source,0,source.length);
    }

    // 根据原数组source,构造出堆heap
    private static void createHeap(Comparable[] source,Comparable[] heap){
        System.arraycopy(source,0,heap,1,source.length);
        //2.从heap索引的一半处开始倒叙遍历,对得到的每一个元素做下沉操作
        for (int i=(heap.length)/2;i>0;i--){
            sink(heap,i,heap.length-1);
        }
    }
    //判断heap堆中索引i处的元素是否小于索引j处的元素
    private static boolean less(Comparable[] heap,int i,int j){
        return heap[i].compareTo(heap[j])<0;
    }
    //交换heap堆中i索引和j索引处的值
    private static void exchange(Comparable[] heap,int i, int j){
        Comparable tem= heap[i];
        heap[i]=heap[j];
        heap[j]=tem;
    }
    // 在堆heap中对目标元素进行下沉,范围为range
    private static void sink(Comparable[] heap,int target,int range){
        //有子结点
        while (2 * target <= range) {
            int max=0;
            //有两个子结点
            if (2 * target + 1 <= range) {
                if (less(heap,2*target,2*target+1)){
                    max=2*target+1;
                }else {
                    max=2*target;
                }

            }else {
                max=2*target;
            }
            if (less(heap,target,max)){
                exchange(heap,target,max);
            }
            // 为了继续下一个循环
            target=max;
        }
    }
}

 

 

 

 

 

 

 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值