数据结构(10):堆

1.定义:

二叉堆是一颗完全二叉树

2.特点:(最大堆)

堆中某个节点的值总是不大于其父节点的值

3.数组存储二叉堆

parent(i)=(i-1)/2
left child(i)=2*i+1
right child(i)=2*i+2

4.二叉堆实现功能

(1)add
(2)extractMax取出最大元素
(3)replace 取出最大元素后,放入一个新元素,算法复杂度为O(nlogn)
(4)Heapify 将n个元素诸葛插入到一个空堆中,算法复杂度为O(nlogn)

5.最大堆的java实现

(1)实现的基本结构:动态数组

package com.DataStructures._08Heap;

/**
 * Created by Administrator on 2018/12/10.
 */
public class ArrayDynamic<E> {
    private E[] data; //数组
    private int size; //数组中实际元素个数

    //构造函数,传入数组的容量capacity
    public ArrayDynamic(int capacity){
        data=(E[])new Object[capacity];
        size=0;
    }

    //无参数构造函数,默认数组的容量10
    public ArrayDynamic(){
        this(10);
    }

    //传入数组构造函数
    public ArrayDynamic(E[] arr)
    {
        data=(E[])new Object[arr.length];
        for (int i=0;i<arr.length;i++){
            data[i]=arr[i];
        }
        size=arr.length;

    }


    //获取数组容量
    public int getCapacity(){
        return data.length;
    }
    //获取数组中的元素个数
    public int getSize(){
        return size;
    }
    //返回数组是否为空
    public boolean isEmpty(){
        return size==0;
    }

    //向所有元素后添加一个新元素
    public void addLast(E e){
//        if (size==data.length){
//            throw new IllegalArgumentException("AddLast failed. Array is full.");
//
//        }
//        data[size] = e;
//        size ++;
        add(size,e);
    }
    //在所有元素前添加一个新元素
    public void addFirst(E e){
        add(0,e);
    }
    //在index索引位置插入一个新元素
    public void add(int index,E e){

        if(index<0||index>size){
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
        }
        if(size==data.length){
//            throw new IllegalArgumentException("AddLast failed. Array is full.");
            //当达到最大长度时候,实行扩容
            resize(2*data.length);
        }
        for (int i =size-1;i>=index;i--){
            data[i+1]=data[i];
        }
        data[index]=e;
        size++;

    }

    @Override
    public String toString(){
        StringBuilder res=new StringBuilder();
        res.append(String.format("Array:size=%d,capacity=%d\n",size,data.length));
        res.append("[");
        for (int i =0;i<size;i++){
            res.append(data[i]);
            if(i!=size-1){
                res.append(",");
            }
        }
        res.append("]");

        return res.toString();
    }

    public E get(int index){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        return data[index];
    }

    public void set(int index,E e){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        }
        data[index]=e;
    }

    //查找是否包含元素e
    public boolean contains(E e){
        for (int i =0;i<size;i++){
            if(data[i].equals(e)){
                return true;
            }
        }
        return false;
    }
    //查找包含元素所在索引,不包含则返回-1
    public int find(E e){
        for (int i =0;i<size;i++){
            if (data[i].equals(e)){
                return i;
            }
        }
        return -1;
    }

    //从数组中删除元素,返回删除元素
    public E remove(int index){
        if(index < 0 || index >= size){
            throw new IllegalArgumentException("Remove failed. Index is illegal.");
        }
        E ret=data[index];
        for (int i=index;i<size-1;i++){
            data[i]=data[i+1];
        }
        size--;
        data[size]=null;

        if(size == data.length / 4 && data.length / 2 != 0)
        {
            resize(data.length/2);
        }
        return ret;
    }

    //从数组中删除第一个元素,返回删除元素
    public E removeFirst(){
        return remove(0);
    }
    //从数组中删除最后一个元素,返回删除元素
    public E removeLast(){
        return remove(size-1);
    }

    //从数组中删除元素e
    public void removeElement(E e){
        int index=find(e);
        if (index!=-1){
            remove(index);
        }
    }
    //将数组容量变为newCapacity大小
    private void resize(int newCapacity){
        E[] newData=(E[])new Object[newCapacity];
        for (int i =0;i<size;i++){
            newData[i]=data[i];
        }
        data=newData;
    }


    /**
     * 交换元素位置
     * @param i
     * @param j
     */
    public void swap(int i,int j){
        if(i<0||i>=size || j<0 ||j >=size){
            throw new IllegalArgumentException("Index is illegal");
        }
        E t=data[i];
        data[i]=data[j];
        data[j]=t;
    }

}

(2)动态数组为基本结构实现最大堆

package com.DataStructures._08Heap;

import java.util.Random;

/**
 * Created by Administrator on 2018/12/19.
 * 最大堆的实现:即每个节点都会大于其子节点的值
 * 实现基本原理:是从数组0开始的堆
 */
public class MaxHeap <E extends Comparable<E>>{
    //1.存储元素
    private ArrayDynamic<E> data;

    //2. 构造函数
    public MaxHeap(int capacity){
        data=new ArrayDynamic<E>(capacity);
    }
    public MaxHeap(){
        data=new ArrayDynamic<E>();
    }

    //3.基本信息
    //返回堆中的元素个数
    public int size(){
        return data.getSize();
    }

    //返回一个布尔值,表示堆中是否为空
    public boolean isEmpty(){
        return data.isEmpty();
    }

    //***********************************************************
    //4.辅助函数
    //返回完全二叉树的数组表示中,一个索引表示的元素的父亲节点的索引
    private int parent(int index){
        if(index==0){
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        }
        return (index-1)/2;
    }

    //返回完全二叉树的数组中,一个索引表示的元素的左孩子节点的索引
    private int leftChild(int index){
        return index*2+1;
    }
    //返回完全二叉树的数组中,一个索引表示的元素的右孩子节点的索引
    private int rightChild(int index){
        return index*2+2;
    }
    //***********************************************************
    //5.向堆中添加元素
    public void add(E e){
        data.addLast(e);
        siftUp(data.getSize()-1);
    }

    //把该元素调整到正确位置
    private void siftUp(int k){
        while (k>0&&data.get(parent(k)).compareTo(data.get(k))<0){
            data.swap(k,parent(k));
            k=parent(k);
        }
    }
    //***********************************************************
    // 6.看堆中的最大元素
    public E findMax(){
        if (data.getSize()==0){
            throw new IllegalArgumentException("Can not findMax when heap is empty.");
        }
        return data.get(0);
    }
    //***********************************************************
    //7.从堆中取出元素
    //默认取出第一个,后续改造思路:把最后一个元素放到顶部,
    public E extractMax(){
        E ret=findMax();
        data.swap(0,data.getSize()-1); //第一个和最后一个元素互换
        data.removeLast(); //删除最后一个
        siftDown(0);
        return ret;
    }
    //下沉操作
    private void siftDown(int k){
        while (leftChild(k)<data.getSize()){
            int j=leftChild(k);// 在此轮循环中,data[k]和data[j]交换位置
            if(j+1<data.getSize()&&//说明有右孩子
                    data.get(j+1).compareTo(data.get(j))>0){  //这里比较左孩子j,有孩子j+1的大小,取比较大值
                j++;
            }
            // data[j] 是 leftChild 和 rightChild 中的最大值
            if(data.get(k).compareTo(data.get(j))>=0){
                break;
            }

            data.swap(k,j);
            k=j;
        }
    }

    //********************************************
    //8.替换元素
    public E replace(E e){
        E ret=findMax();
        data.set(0,e);
        siftDown(0);
        return ret;
    }

    //********************************************
    //9.Heapify的实现:一个构造函数
    public MaxHeap(E[] arr){
        data=new ArrayDynamic<E>(arr);
        for (int i =parent(arr.length-1);i>=0;i--){
            siftDown(i);
        }
    }
    //********************************************
    //测试
    public static void main(String[] args) {


        //测试一
//        int n=1000000;
//        MaxHeap<Integer> maxHeap=new MaxHeap<>();
//        Random random =new Random();
//        for (int i=0;i<n;i++){
//            maxHeap.add(random.nextInt(Integer.MAX_VALUE));
//        }
//
//        int[] arr=new int[n];
//        for (int i=0;i<n;i++){
//            arr[i]=maxHeap.extractMax();
//        }
//
//        for (int i=1;i<n;i++){
//            if(arr[i-1]<arr[i]){
//                throw new IllegalArgumentException("Error");
//            }
//        }
//        System.out.println("Test MaxHeap completed.");

        //测试二
        int n = 1000000;

        Random random = new Random();
        Integer[] testData = new Integer[n];
        for(int i = 0 ; i < n ; i ++)
            testData[i] = random.nextInt(Integer.MAX_VALUE);

        double time1 = testHeap(testData, false);
        System.out.println("Without heapify: " + time1 + " s");

        double time2 = testHeap(testData, true);
        System.out.println("With heapify: " + time2 + " s");
    }

    private static double testHeap(Integer[] testData, boolean isHeapify){

        long startTime = System.nanoTime();

        MaxHeap<Integer> maxHeap;
        if(isHeapify)
            maxHeap = new MaxHeap<>(testData);
        else{
            maxHeap = new MaxHeap<>();
            for(int num: testData)
                maxHeap.add(num);
        }

        int[] arr = new int[testData.length];
        for(int i = 0 ; i < testData.length ; i ++)
            arr[i] = maxHeap.extractMax();

        for(int i = 1 ; i < testData.length ; i ++)
            if(arr[i-1] < arr[i])
                throw new IllegalArgumentException("Error");
        System.out.println("Test MaxHeap completed.");

        long endTime = System.nanoTime();

        return (endTime - startTime) / 1000000000.0;
    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值