【数据结构】Java实现最大堆

news/2024/5/19 3:38:50 标签: 数据结构, , 堆排序

主文件:

package heap;

import java.lang.reflect.Array;
import java.util.Comparator;
import java.util.Arrays;


/**
 * @program: bintree
 * @description: 基于数组实现的二叉
 * @author: fwb
 * @create: 2019-06-25 19:29
 **/
public class Heap<E> {
    //实现任意类的大小比较
    private Comparator<E> comparator;
    private int size;
    private E[] elementData;
    //类似于treeMap的设计模式
    //默认初始容量
    private static final int DEFAULT_CAPACCITY = 10;

    public Heap(){
        this(DEFAULT_CAPACCITY,null);
    }

    public Heap(int initialCapacity){
        this(initialCapacity,null);
    }

    public Heap(int initialCapacity,Comparator<E> comparator){
        this.elementData = (E[]) new Object[initialCapacity];
        this.comparator = comparator;
    }

    public int getSize(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    /**
    * @Description: 比较两个元素的大小
    * @Param: [o1, o2]
    * @return: int
    */
    private int compare(E o1,E o2){
        if (comparator == null){
            //此时E必须为Compareable接口的子类
            return ((Comparable<E>)o1).compareTo(o2);
        }
        return comparator.compare(o1,o2);
    }

    private int leftChildIndex(int index){
        return index * 2 + 1;
    }

    private int rightChildIndex(int index){
        return index * 2 + 2;
    }

    private int fatherChildIndex(int index){
        if(index == 0){
            throw new IllegalArgumentException("根节点无父亲");
        }
        return (index - 1)/2;
    }

    //添加元素,默认在数组末尾添加元素,此时添加元素之后可能会破坏的定义
    /// 元素上浮(判断当前元素与父节点元素的大小)
    public void add(E e){
        //首先判断数组是否满了
        if (elementData.length == size){
            grow();
        }
        //向数组末尾添加元素
        elementData[size++] = e;
        //siftUp,因为size++跑到下一位去了
        siftUp(size -1);
    }
    private void grow(){
        int oldCap = elementData.length;
        int newCap = oldCap + (oldCap < 64 ? oldCap : oldCap >>1);
        if (newCap > Integer.MAX_VALUE - 8){
            throw new IndexOutOfBoundsException("数组过大");
        }
        elementData = Arrays.copyOf(elementData,newCap);
    }

    private void siftUp(int index){
        //index不是根节点,并且大于他的父节点
        while(index > 0 && compare(elementData[index],elementData[fatherChildIndex(index)]) > 0){
            swap(index,fatherChildIndex(index));
            index = fatherChildIndex(index);
        }
    }

    private void swap(int indexA,int indexB){
        E temp = elementData[indexA];
        elementData[indexA] = elementData[indexB];
        elementData[indexB] = temp;
    }

    //找到中最大元素,(大顶最大元素就是第一个elementData[o])
    public E findMax(){
        if(isEmpty()){
            throw new IndexOutOfBoundsException("head is empty");
        }
        return elementData[0];
    }

    //取出最大值,并将他的最大值返回
    //将顶元素与的最后一个元素交换位置,然后将交换后的最后一个元素删除
    //siftDown,将交换后的元素下沉
    //将其与自己的左右孩子比较,与其中最大的交换位置,
    //直到成为叶子节点(左孩子的节点下标值大于节点个数),或者比左右孩子都大
    public E extractMax(){
        E result = findMax();
        //交换最后一个元素与顶元素位置
        swap(0,size-1);
        //删除最后一个元素
        elementData[--size] = null;
        //siftDown
        siftDown(0);
        return result;
    }
    /**
    * @Description: 将二叉树当前节点下沉到正确位置
    * @Param: [index]
    * @return: void
    */
    private void siftDown(int index){
        while(leftChildIndex(index) < size){
            //当前节点左孩子下标
            int j  = leftChildIndex(index);
            //判断是左孩子大还是右孩子大
            if (j + 1 < size){//之所以是小于size而不是小于等于是因为前面size--
                //此时有右孩子
                if (compare(elementData[j],elementData[j + 1]) < 0){
                    //此时j为右孩子指引下标
                    j++;
                }
                //此时element[j]一定是左右孩子的最大值
            }
            if (compare(elementData[index],elementData[j]) > 0){
                break;
            }else{
                swap(index,j);
                index = j;
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        for(E e : elementData)
            if (e != null)
            res.append(e+"、");
        return res.toString();
    }
}

测试文件(以及排序)

package heap;

import java.util.Random;

/**
 * @program: bintree
 * @description:
 * @author: fwb
 * @create: 2019-06-26 17:03
 **/
public class HeapTest {
    public static void main(String[] args) {
//        int[] nums = new int[]{62,41,30,28,16,22,13,19,17,15};
//        Heap<Integer> heap = new Heap<>();
//        for(int i : nums){
//            heap.add(i);
//        }
//        heap.add(52);
//        heap.extractMax();
//        System.out.println(heap);

        /*
        排序,
         */
        int n = 100000;
        Random random =  new Random();
        Heap<Integer> heap = new Heap<>(n);
        for (int i = 0;i < n;i++){
            heap.add(random.nextInt(Integer.MAX_VALUE));//在整数的最大范围内取1000000个随机数
        }
        int[] nums = new int[n];
//        while(!heap.isEmpty()){
//            nums[i++] = heap.extractMax();
//            for (int j = 1;j < n;j++){
//                if (nums[j -1] < nums[j]){
//                    throw new IllegalArgumentException("Error");
//                }
//            }
//        }
        for (int j = heap.getSize() - 1;j >= 0;j--){
            nums[j] = heap.extractMax();
        }
        for (int j : nums){
            System.out.println(j + "、");
        }
        System.out.println("Test Completed!");
    }
}

http://www.niftyadmin.cn/n/1521323.html

相关文章

Jsp与HTML的区别

先了解一下什么叫动态页面和静态页面&#xff1f; 1、静态页面&#xff0c;即静态网页&#xff0c;是实际存在的&#xff0c;无需经过服务器的编译&#xff0c;直接加载到客户浏览器上显示出来。静态页面需要占一定的服务器空间&#xff0c;且不能自主管理发布更新的页面&#…

IDEA cannot access怎么办

重启IDEA&#xff08;亲测可用&#xff09; &#xff1a;&#xff09;

BS框架和CS框架之间的区别

C/S架构&#xff1a;即Client/Server架构&#xff0c;即客户端/服务器架构。是大家熟知的软件系统体系结构&#xff0c;通过将任务合理分配到Client端和Server端&#xff0c;降低了系统的通讯开销&#xff0c;需要安装客户端才可进行管理操作。客户端和服务器端的程序不同&…

BS与CS架构区别(简单易懂版)

BS架构&#xff1a;前端(Browser)服务器端(Server)&#xff08;知乎&#xff0c;微博&#xff0c;淘宝&#xff09; CS架构&#xff1a;Client/Sever 客户/服务器模式。&#xff08;迅雷&#xff0c;QQ&#xff0c;网络游戏&#xff09;

java协变性_java 不变、协变、逆变

java 不变、协变、逆变前言先说结论&#xff0c;java 的 List 是不变的&#xff0c;java 的 array 是协变的。java 的 list 可以通过添加通配符的方式来达到协变或者逆变的效果。什么是不变、协变、逆变其实&#xff0c;这三个概念就是看着术语比较吓人人&#xff0c;理解起来非…

bit,位,比特,比特位,byte联系与区别

bit、位、比特、比特位都是一个东西&#xff0c;是计算机中最小的单位 byte是字节也就是B 1字节&#xff08;byte&#xff09;8位&#xff08;bit&#xff09; 位只有两种形式0和1&#xff0c;只能表示2种状态&#xff0c;而字节是有8个位组成的。可以表示256个状态。 1byt…

经典.NET面试题

1、谈谈final, finally, finalize的区别 答&#xff1a;final—修饰符&#xff08;关键字&#xff09;如果一个类被声明为final&#xff0c;意味着它不能再派生出新的子类&#xff0c;不能作为父类被继承。因此 一个类不能既被声明为 abstract的&#xff0c;又被声明为final的。…

server.mappath php,window_Server对象之MapPath方法,MapPath 方法将指定的相对或虚 - phpStudy...

Server对象之MapPath方法MapPath 方法将指定的相对或虚拟路径映射到服务器上相应的物理目录上。语法Server.MapPath( Path )参数Path指定要映射物理目录的相对或虚拟路径。若 Path以一个正斜杠 (/) 或反斜杠 (\) 开始&#xff0c;则 MapPath方法返回路径时将 Path视为完整的虚拟…