自定义堆(1):实现最大堆

通过学习自定义堆,了解堆的数据结构。

 本篇以最大堆为例。

底层依赖了自定义数组,  参考:自定义数组   中的   Array.java

所以,其时间复杂度分析:

add

O(log n)

extractMax

O(log n)

replace

O(log n)

 

对 siftUP方法的解释:

每次将要添加的元素,放在数组最后一个位置,按照最大堆的定义,使其值与父节点的值进行比较,决定是否“上浮”。

 

对siftDown方法的解释:

将位于数组最后位置的元素与最大元素交换位置,并删除最大元素,对此时处于最大元素位置的元素,安装最大堆的定义,比较其值与左右孩子中最大的那个的大小,进行“下沉”

 

 

对replace方法的解释:直接将堆顶元素替换以后sift Down。 

 

Array.java 新增了一个方法:

/**
	 * 新增
	 * 用于交换两个位置的值
	 * @param i
	 * @param j
	 */
	public void swap(int i,int j) {
		if(i < 0 || i >= size || j < 0 || j >= size) {
            throw new IllegalArgumentException("索引不合法");
		}
		
		E e=data[i];
		data[i]=data[j];
		data[j]=e;
	}

MaxHeap.java:

package Heap;

public class MaxHeap<E extends Comparable<E>> {
	private Array<E> data;//维护一个自定义数组
	/**
	 * 带参构造方法
	 * @param capacity
	 */
	public MaxHeap(int capacity) {
		data =new Array<>(capacity);
	}
	/**
	 * 无参构造方法
	 */
	public MaxHeap() {
		data=new Array<>();
	}
	/**
	 * 返回堆中的元素个数
	 * @return
	 */
	public int size() {
		return data.getSize();
	}
	/**
	 * 判断堆是否为空
	 * @return
	 */
	public boolean isEmpty() {
		return data.isEmpty();
	}
	/**
	 * 返回完全二叉树的数组表示中,一个索引所表示的元素的父亲节点的索引
	 * @param index
	 * @return
	 */
	private int parent(int index) {
		if(index==0) {
			throw new IllegalArgumentException("索引为0,没有父节点");
		}
		return (index-1)/2;
	}
	/**
	 * 返回完全二叉树的数组表示中,一个索引所表示的元素的左孩子节点的索引
	 * @param index
	 * @return
	 */
	private int leftChild(int index) {
		return index*2+1;
	}
	/**
	 * 返回完全二叉树的数组表示中,一个索引所表示的元素的右孩子节点的索引
	 * @param index
	 * @return
	 */
	private int rigthChild(int index) {
		return index*2+2;
	}
	/**
	 * 向堆中添加元素。
	 * @param e
	 */
	public void add(E e) {
		data.addLast(e);
		siftUp(data.getSize()-1);
	}
	private void siftUp(int index) {
		
		while(index>0 && data.get(parent(index)).compareTo(data.get(index))<0) {
			data.swap(index, parent(index));
			index=parent(index);
		}
	}
	/**
	 * 查看堆中的最大元素
	 * @return
	 */
	public E findMax() {
		if(data.getSize() == 0) {
            throw new IllegalArgumentException("堆为空");
		}
		return data.get(0);
	}
	/**
	 * 取出堆中最大元素
	 * @return
	 */
	public E extractMax() {
		E e=findMax();
		data.swap(0, data.getSize()-1);
		data.removeLast();
		siftDown(0);
		
		return e;
		
	}
	private void siftDown(int i) {
		
		while(leftChild(i)<data.getSize()) {
			
			int k=leftChild(i);
			if(k+1<data.getSize()&& data.get(k+1).compareTo(data.get(k))>0) {
				k++;
			}
			//此时,data[k]是leftChild和rightChild中最大值
			if(data.get(i).compareTo(data.get(k))>=0) {
				break;
			}
			data.swap(k, i);
			i=k;
			
		}
		
	}
	/**
	 * 取出堆中最大的元素,并且替换成元素e
	 * @param e
	 * @return
	 */
	public E replace(E e) {
		E ret=findMax();
		data.set(0, e);
		siftDown(0);
		return ret;
		
	}
	
	

	
	
}

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值