六、Java数据结构与算法---堆(讲解与代码)

堆的实现代码

下面展示一些 堆代码基本功能的实现

package Heap;

/**
 * @author Fantic
 * @create 2021-08-17 10:59
 */
public class Heap<T extends Comparable<T>> {

    //用来存储元素的数组
    private T[] items;
    //记录堆中元素的个数
    private int Num;

    //创建容量为capacity的Heap对象

    public Heap(int capacity) {
        this.items = (T[]) new Comparable[capacity + 1];
        Num = 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){
        //由于有对元素转换为数组,便于调用,将索引0处废止,起始存储索引位置向前一位
        items[++Num] = t;
        //插入元素后使用,由于元素大小问题,采用上浮算法将元素移到适当的位置
        swim(Num);
    }

    //使用上浮算法,将索引k处的元素放在堆中处于一个正确的位置
    private void swim(int k){
        //通过循环,不断的比较当前结点的值和其父点结点的值,如果发现其父结点的值比当前结点值小,则互换位置
        while (k > 1) {
            if (less(k / 2,k)) {
                exch(k / 2,k);
            }

            k = k / 2;
        }

    }


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

        //交换索引1处的元素和最大索引处的元素,让其暂时成为根结点
        exch(1,Num);
        //将交换后的最大元素的索引处的元素删除
        items[Num] = null;
        //元素个数减1
        Num--;
        //通过下沉算法,让堆重新排序
        sink(1);
        return max;

    }
    //使用下沉算法,将索引k处的元素能在堆中处于一个正确的位置
    private void sink(int k){

        //通过循环不断的对比当前k结点和其左子结点2*k以及右子节点2*k+1处中的较大值的元素大小
        while (2*k <= Num) {
            //获取当前结点的子节点中的较大值
            //记录较大结点所在索引值
            int max;
            if (2 * k + 1 <= Num){
                if (less(2 * k,2 * k + 1)){
                    max = 2 * k + 1;
                }else {
                    max = 2 * k;
                }
            }else {
                max = 2 * k;
            }

            //比较当前结点和最大结点的值,如果k的值比子结点最大值大,直接退出循环,没有下沉的必要
            if (!less(k,max)) {

                break;
            }
            //交换k结点和最大子结点的值
            exch(k,max);
            //变换k的值,与接下来的子结点进行二次比较
            k = max;
        }


    }

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


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



}

1.1 堆的定义

堆是计算机科学中一类特殊的数据结构的统称,堆通常可以被看做是一棵完全二叉树的数组对象。

堆的特性: 1.它是完全二叉树,除了树的最后一层结点不需要是满的,其它的每一层从左到右都是满的,如果最后一层结点不是满的,那么要求左满右不满。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkLrnKKP-1629183266409)(file://C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210817141917658.png?lastModify=1629181759)]

2.它通常用数组来实现。具体方法就是将二叉树的结点按照层级顺序放入数组中,根结点在位置1,它的子结点在位置2和3,而子结点的子结点则分别在位置4,5,6和7,以此类推。 (用数组实现堆,对应索引0位置不放入元素)
在这里插入图片描述
如果一个结点的位置为k,则它的父结点的位置为[k/2],而它的两个子结点的位置则分别为2k和2k+1。这样,在不使用指针的情况下,我们也可以通过计算数组的索引在树中上下移动:从a[k]向上一层,就令k等于k/2,向下一层就令k等于2k或2k+1。

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

1.2 堆的API设计

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A7nHzWCL-1629183266412)(file://C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210817142339781.png?lastModify=1629181759)]

1.3 堆的实现

1.3.1 insert插入方法的实现

堆是用数组完成数据元素的存储的,由于数组的底层是一串连续的内存地址,所以我们要往堆中插入数据,我们只能往数组中从索引0处开始,依次往后存放数据,但是堆中对元素的顺序是有要求的,每一个结点的数据要大于等于它的两个子结点的数据,所以每次插入一个元素,都会使得堆中的数据顺序变乱,这个时候我们就需要通过一些方法让刚才插入的这个数据放入到合适的位置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以,如果往堆中新插入元素,我们只需要不断的比较新结点a[k]和它的父结点a[k/2]的大小,然后根据结果完成数据元素的交换,就可以完成堆的有序调整。

1.3.2 delMax删除最大元素方法的实现

由堆的特性我们可以知道,索引1处的元素,也就是根结点就是最大的元素,当我们把根结点的元素删除后,需要有一个新的根结点出现,这时我们可以暂时把堆中最后一个元素放到索引1处,充当根结点,但是它有可能不满足堆的有序性需求,这个时候我们就需要通过一些方法,让这个新的根结点放入到合适的位置。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
所以,当删除掉最大元素后,只需要将最后一个元素放到索引1处,并不断的拿着当前结点a[k]与它的子结点a[2k]和a[2k+1]中的较大者交换位置,即可完成堆的有序调整。

1.4 堆排序

给定一个数组:
String[] arr = {“S”,“O”,“R”,“T”,“E”,“X”,“A”,“M”,“P”,“L”,“E”}
请对数组中的字符按从小到大排序。

实现步骤:
1.构造堆;
2.得到堆顶元素,这个值就是最大值;
3.交换堆顶元素和数组中的最后一个元素,此时所有元素中的最大元素已经放到合适的位置;
4.对堆进行调整,重新让除了最后一个元素的剩余元素中的最大值放到堆顶;
5.重复2~4这个步骤,直到堆中剩一个元素为止。

API设计:
在这里插入图片描述

1.4.1 堆构造过程

堆的构造,最直观的想法就是另外再创建一个和新数组数组,然后从左往右遍历原数组,每得到一个元素后,添加到新数组中,并通过上浮,对堆进行调整,最后新的数组就是一个堆。

上述的方式虽然很直观,也很简单,但是我们可以用更聪明一点的办法完成它。创建一个新数组,把原数组0 ~ length-1的数据拷贝到新数组的1~ length处,再从新数组长度的一半处开始往1索引处扫描(从右往左),然后对扫描到的每一个元素做下沉调整即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4.2 堆排序过程

对构造好的堆,我们只需要做类似于堆的删除操作,就可以完成排序。
1.将堆顶元素和堆中最后一个元素交换位置;
2.通过对堆顶元素下沉调整堆,把最大的元素放到堆顶(此时最后一个元素不参与堆的调整,因为最大的数据已经到了数组的最右边)
3.重复1~2步骤,直到堆中剩最后一个元素。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面展示一些 堆排序代码功能实现

package Heap;

import java.util.Arrays;

/**
 * @author Fantic
 * @create 2021-08-17 11:00
 */
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 temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }

    //根据原数组source,构造出堆heap
    private static void creatHead(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);

        }
    }


    //对source数组中的数据从小到大排序
    public static void sort(Comparable[] source){
        //构建堆
        Comparable[] heap = new Comparable[source.length + 1];
        creatHead(source,heap);

        //定义一个变量,交换索引1和最大索引处的元素
        int num = heap.length - 1;
        while(num != 1){
            //交换元素
            exch(heap,1,num);
            //交换元素,删除最大索引处元素
            num--;
            //对调序后的元素进行下沉
            sink(heap,1,num);
        }

        //把heap中的数据复制到原数组中
        System.arraycopy(heap,1,source,0,source.length);


    }




    //在heap堆中,对target处的元素做下沉,范围是0 ~ range
    private static void sink(Comparable[] heap,int target,int range){
        while (target * 2 <= range){
            //1.找到当前结点的较大的子节点
            int max;
            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;
            }

            //2.比较当前结点的值和较大节点的值,如果目标值并不比子结点的值小,没必要完成下沉
            if (!less(heap,target,max)){
                break;
            }

            exch(heap,target,max);

            target = max;
        }
    }

    public static void main(String[] args) {
        //待排序的数组
        String[] array = {"S","O","R","T","E","X","A","M","P","L","E"};

        //通过heapSort对数组中的元素进行排序
        HeapSort.sort(array);
        System.out.println(Arrays.toString(array));
    }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面展示了一些Java数据结构与算法代码实现: 冒泡排序代码实现O(n^2): ```java public class Bubble { /* 对数组a中的元素进行排序 */ public static void sort(Comparable[] a){ for(int i=a.length-1;i>0;i--){ for(int j=0;j<i;j++){ // 比较索引j和索引j+1处的值 if (greater(a[j],a[j+1])){ exch(a,j,j+1); } } } } /* 比较v元素是否大于w元素 */ private static boolean greater(Comparable v,Comparable w){ return v.compareTo(w)>0; } /* 数组元素i和j交换位置 */ private static void exch(Comparable[] a,int i,int j){ Comparable temp; temp = a[i]; a[i]=a[j]; a[j]=temp; } } ``` 归并排序代码实现测试: ```java public class MergeTest { public static void main(String[] args) { Integer[] array = {4, 6, 8, 7, 9, 2, 10, 1,12}; MergeSort.sort(array); System.out.println(Arrays.toString(array)); } } ``` 快速排序代码实现测试: ```java public class QuickTest { public static void main(String[] args) { Integer[] array = {4, 6, 8, 7, 9, 2, 10, 1,12,10}; QuickSort.sort(array); System.out.println(Arrays.toString(array)); } } ```<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [二、Java数据结构与算法---排序(讲解代码)](https://blog.csdn.net/Royalic/article/details/119427212)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

原来如此呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值