13堆-----------

P96 96_堆_堆的概述、插入删除、堆排序

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
区别:
查找树,父节点比左子结点要大,父节点比右子结点要小
在这里插入图片描述
提供比较规则。
因为堆中,每一个结点都大于等于它的两个结点。

在这里插入图片描述

插入

索引0处,我们是不用的。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
子节点和父节点比较。直到父节点比子节点要大。

删除

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package heap;

public class Heap<T extends Comparable<T>> {
    //存储堆中的元素
    private T[] items;
    //记录堆中元素的个数
    private int N;

    public Heap(int capacity) {//容量,假如说传进来的是2,只有0和1,0不使用
        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 exch(int i,int j){
        T temp = items[i];
        items[i] = items[j];
        items[j] = temp;
    }

    //往堆中插入一个元素
    public void insert(T t){
        items[++N]=t;//N一开始等于0,堆中,0索引是不使用的,所以++N,而不是N++
        swim(N);
    }

    //使用上浮算法,使索引k处的元素能在堆中处于一个正确的位置
    //为什么叫上浮算法,因为我们插入元素的时候,都是在后面。通过上浮不断调整自己的位置。
    private void swim(int k){
        //通过循环,不断的比较当前结点的值和其父结点的值,如果发现父结点的值比当前结点的值小,则交换位置
        while(k>1){//根节点为k=1,k>1,说明k最小可以取到2。
            //比较其父结点k/2和当前结点k
            //如果发现父结点的值比当前结点的值小,则交换位置
            if (less(k/2,k)){//k=2/2=1,把第一个节点和另一个进行比较。
                exch(k/2,k);
            }

            k = k/2;
        }

    }

    //删除堆中最大的元素,并返回这个最大元素
    public T delMax(){
        T max = items[1];

        //交换索引1处的元素和最大索引处的元素,让完全二叉树中最右侧的元素变为临时根结点
        exch(1,N);
        //最大索引处的元素删除掉
        items[N]=null;
        //元素个数-1
        N--;
        //通过下沉调整堆,让堆重新有序
        sink(1);
        return max;
    }

    //使用下沉算法,使索引k处的元素能在堆中处于一个正确的位置
    private void sink(int k){
        //通过循环不断的对比当前k结点和其左子结点2*k以及右子结点2k+1处中的较大值的元素大小,如果当前结点小,则需要交换位置
        while(2*k<=N){//没有子节点,不需要比。所以至少需要一个左子节点2k。这里是下沉,要保证下沉,所以至少需要一个左子节点2k。2k<=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)){//如果k大于max,!0=1,停止循环
                break;
            }

            //如果k小于max
            //交换k索引处的值和max索引处的值
            exch(k,max);

            //变换k的值,为下一次循环
            k = max;

        }


    }

    public static void main(String[] args) {
        Heap<String> heap = new Heap<String>(20);
        heap.insert("A");
        heap.insert("B");
        heap.insert("C");
        heap.insert("D");
        heap.insert("E");
        heap.insert("F");
        heap.insert("G");


        String del;
        while((del=heap.delMax())!=null){
            System.out.print(del+",");
        }

    }
}

package test;

import heap.Heap;

public class HeapTest {
    public static void main(String[] args) {
        //创建堆对象
        Heap<String> heap = new Heap<>(10);
        //往堆中存入字符串数据
        heap.insert("A");
        heap.insert("B");
        heap.insert("C");
        heap.insert("D");
        heap.insert("E");
        heap.insert("F");
        heap.insert("G");

        //通过循环从堆中删除数据
        String result = null;
        while((result = heap.delMax())!=null){
            System.out.print(result+" ");
        }
    }
}

P101 101_堆_堆排序

把数组先复制到堆中。
数组是从0开始的。
堆是从1开始的。

复制到堆中之后,一开始堆是没有顺序的。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
为什么?
再从新数组长度的一半处开始往1索引处扫描(从右往左),然后
对扫描到的每一个元素做下沉调整即可。

因为一半处,上面一半是父节点
下面一半是叶子节点。
我们从右往左就是那节点和父节点比较。
大的上浮,小的下沉。

总之,从一半处扫描,性能提高。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

src:要复制的数组(源数组)

srcPos:复制源数组的起始位置

dest:目标数组

destPos:目标数组的下标位置

length:要复制的长度

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

构建堆后,堆有序之后,是从大到小的。如何从小到大排序呢?

我们把最大的元素,也就是堆顶元素和最后一个交换。
此时最后一个就是最大的元素。(正好也是数组最后一个索引处,对应最大元素,嘻嘻!)
最后一个元素不使用,N–。
剩下的元素重新找到最大元素放在堆顶。
然后再和最后一个交换,不使用,N–。重复这样的过程。

代码

package heap;

public class HeapSort {
    //判断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 exch(Comparable[] heap, int i, int j) {
        Comparable tmp = heap[i];
        heap[i] = heap[j];
        heap[j] = tmp;
    }



    //根据原数组source,构造出堆heap
    private static void createHeap(Comparable[] source, Comparable[] heap) {
        //把source中的元素拷贝到heap中,heap中的元素就形成一个无序的堆
        System.arraycopy(source,0,heap,1,source.length);

        //对堆中的元素做下沉调整(从长度的一半处开始,往索引1处扫描)
        for (int i = (heap.length)/2;i>0;i--){
            sink(heap,i,heap.length-1);//因为现在是在构造堆,所以是在整个范围内调整,这里填写heap.length-1
        }

    }



    //对source数组中的数据从小到大排序
    //从小到大,没看懂。
    //堆不是从大到小的吗?使用sink下沉,如何保证从小到大?
    public static  void sort(Comparable[] source) {
        //构建堆
        Comparable[] heap = new Comparable[source.length+1];//heap数组0索引废弃
        createHeap(source,heap);
        //定义一个变量,记录未排序的元素中最大的索引
        int N = heap.length-1;
        //通过循环,交换1索引处的元素和排序的元素中最大的索引处的元素
        while(N!=1){
            //交换元素
            exch(heap,1,N);
            //排序交换后最大元素所在的索引,让它不要参与堆的下沉调整
            N--;
            //需要对索引1处的元素进行对的下沉调整
            sink(heap,1, N);
        }
        //把heap中的数据复制到原数组source中
        System.arraycopy(heap,1,source,0,source.length);

    }


    //在heap堆中,对target处的元素做下沉,范围是0~range
    //为什么0~range?
    //之前视频说,可以让某一部分及其之前的,可以参与下沉,这个range就是规定了,那一部分
    private static void sink(Comparable[] heap, int target, int range){

        while(2*target<=range){
            //1.找出当前结点的较大的子结点
            int max;
            if (2*target+1<=range){//之前视频说,可以让某一部分及其之前的,可以参与下沉,这个range就是规定了,那一部分,所以是<=range
                if (less(heap,2*target,2*target+1)){
                    max = 2*target+1;
                }else{
                    max = 2*target;
                }
            }else{//如果右子节点都没有,则直接记录左子节点
                max = 2*target;
            }

            //2.
            //比较当前结点和子节点中较大结点的值
            //如果当前结点和子节点中较大结点的值,还要大,那就没必要比较了
            if (!less(heap,target,max)){
                break;
            }

            exch(heap,target,max);

            target = max;
        }
    }

}

package test;

import heap.HeapSort;

import java.util.Arrays;

public class HeapSortTest {
    public static void main(String[] args) {
        //待排序数组
        String[] arr = {"S","O","R","T","E","X","A","M","P","L","E"};
        //通过HeapSort对数组中的元素进行排序
        HeapSort.sort(arr);
        //打印排序后数组中的元素
        System.out.println(Arrays.toString(arr));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值