java 关于最大索引堆的简单实现

本文介绍了一个使用Java编写的最大索引堆数据结构,用于高效存储和操作大容量数据。核心内容包括堆的构建、插入、修改、删除及排序算法。适合处理大规模数据的快速查找和维护最大值场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码

package structure;

import java.util.Arrays;

/**
 * 最大索引堆的简单实现
 */
public class IndexMaxHeap<T extends Comparable<T>> {
	
	/**
	 * 笔记:
	 * 1.父节点>=左右子节点
	 * 2.第一个非叶子节点的索引 = count/2
	 * 3.假设当前元素的索引位置为 i,可以得到规律:
		 parent(i) = i/2(取整)
		 left child(i) = 2*i
		 right child(i) = 2*i +1
	 */
	
	/**
	 * 堆中的数据	下标0不用	躯体
	 */
	protected T[] datas;
	
	/**
	 * 堆中的索引:存储堆中由大到小排序的数据的datas下标	下标0不用	灵魂
	 *  索引堆:	优化了交换元素的消耗;加入的数据位置固定,方便寻找。
			适用说明:
				如果堆中存储的元素较大,那么进行交换就要消耗大量的时间,
				这个时候可以用索引堆的数据结构进行替代,堆中存储的是数组的索引,我们相应操作的是索引。
	 */
	protected int[] indexes;
	
	/**
	 * 目前不用
	 * 通过执行  indexes[i] = j;reverseIndexes[j] = i;
	     使满足      indexes[reverseIndexes[i]] = i;reverseIndexes[indexes[i]] = i;
	 */
	protected int [] reverseIndexes;
	
	/**
	 * 堆的最大容量
	 */
	protected int capacity;
	
	/**
	 * 堆中的现有元素个数
	 */
	protected int count;
	
	/**
	 * 构造一个指定容量的空堆
	 * @param capacity
	 */
	@SuppressWarnings("unchecked")
	public IndexMaxHeap(int capacity) {
		datas = (T[]) new Comparable[capacity + 1];//数组的第一个位置不用
		indexes = new int[capacity + 1];
		count = 0;
		this.capacity = capacity;
	}

	/**
	 * 通过一个给定数组中的数据创建一个堆
	 * 时间复杂度为O(n)
	 * @param arr
	 */
    @SuppressWarnings("unchecked")
	public IndexMaxHeap(T [] arr){

        int n = arr.length;

        datas = (T[])new Comparable[n+1];
        indexes = new int [n+1];
        capacity = n;

        for( int i = 0 ; i < n ; i ++ ) {//存储数据
        	datas[i+1] = arr[i];
        	indexes[i+1] = i+1;
        }
        count = n;//先存数据,再记录元素数量
        sortAll();
    }

	/**
	 * 获取当前堆中的元素个数
	 */
	public int size() {
		return count;
	}

	/**
	 * 堆空判断
	 */
	public boolean isEmpty() {
		return count == 0;
	}
	
	/**
	 * 增加一个指定元素并进行排序
	 * @param item
	 */
    public void add(T item){
    	checkIsFull();
    	count ++;
        datas[count] = item;
        indexes[count] = count;//放置在堆尾
        shiftUp(count);//对新元素进行向上排序
    }
    
    /**
	 * 修改指定位置的元素
	 * @param i
	 * @param newItem
	 */
	public T replace(int i, T newItem) {
		checkIndex(i);
		i += 1;
		T replacedValue = datas[indexes[i]];
		datas[indexes[i]] = newItem;
		shiftUp(i);//先向上同父节点比较排序,完成左/右一部分的排序
		shiftDown(i);//然后向下向下同MAX(左/右子节点)进行比较排序,完成左/右剩下的未排序的一部分的排序
		return replacedValue;
	}

	/**
	 * 取出堆顶元素, 即索引堆中所存储的最大数据
	 */
	public T extractMax() {
		checkIsEmpty();
		T ret = datas[indexes[1]];
		swapIndexes(1, count);//交换堆尾元素
		count--;
		shiftDown(1);//对堆尾元素进行向下排序
		return ret;
	}

//	// 从最大索引堆中取出堆顶元素的索引
//	public int extractMaxIndex() {
//		checkIsEmpty();
//		int ret = indexes[1] - 1;
//		swapIndexes(1, count);
//		count--;
//		shiftDown(1);
//		return ret;
//	}

	/**
	 * 获取堆顶元素
	 * @return
	 */
	public T getMax() {
		return getItem(0);
	}

//	/**
//	 * 获取堆顶元素的索引
//	 * @return
//	 */
//	public int getMaxIndex() {
//		checkIsEmpty();
//		return indexes[1] - 1;
//	}

	/**
	 * 获取排序后的指定位置的元素
	 * @param i
	 * @return
	 */
	public T getItem(int i) {
		checkIndex(i);
		return datas[indexes[i + 1]];
	}
	
	/**
	 * 获取堆中数据按排序生成的数组
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public T [] toArray() {
		T [] arr = (T[])new Comparable[count];
		for (int i = 1; i <= count; i++) {
			arr[i-1] = datas[indexes[i]];
		}
		return arr;
	}
	
	@Override
	public String toString() {
		return Arrays.toString(toArray());
	}
	
	/**
     * 对堆中所有元素进行排序
     */
	private void sortAll() {
		//自底向上依次保证每一层的子节点有序
		for( int i = count/2 ; i >= 1 ; i -- )//从第一个不是叶子节点的元素开始	依次向下排序
            shiftDown(i);
	}

	/**
	 * 对指定节点位置的数据进行向下排序,使其满足--节点数据不小于左右子节点
	 * @param k		
	 */
	private void shiftDown(int k) {
		while (2 * k <= count) {//如果存在子节点
			int j;//	MAX(左子节点数据,右子节点数据)下标
			j = 2 * k;//暂时定为	左子节点下标
			if (j + 1 <= count && datas[indexes[j + 1]].compareTo(datas[indexes[j]]) > 0)//如果右节点存在且数据大于左节点
				j++;//改为右子节点下标
			if (datas[indexes[k]].compareTo(datas[indexes[j]]) >= 0)//如果该节点大于其左右子节点,则此次排序完成
				break;
			swapIndexes(k, j);//交换数据索引
			k = j;
		}
	}

	/**
	 * 对指定节点位置的数据进行向上排序,使其满足--该节点数据不大于其父节点数据
	 * @param k
	 */
	private void shiftUp(int k) {
		checkIndex(k-1);
		//如果该节点数据	存在父节点数据  且 大于其父节点数据,则交换至父节点位置	重复循环直至不满足该条件	
		while (k > 1 && datas[indexes[k / 2]].compareTo(datas[indexes[k]]) < 0) {
			swapIndexes(k, k / 2);
			k /= 2;
		}
	}
	
	/**
	 * 交换索引数组中的两个指定索引数据的位置
	 * @param i
	 * @param j
	 */
	private void swapIndexes(int i, int j) {
		int t = indexes[i];
		indexes[i] = indexes[j];
		indexes[j] = t;
	}
	
	/**
	 * 如果指定下标超出范围:[0,count),则报异常,打断之后流程的执行
	 * @param index	[0,count)
	 */
	private void checkIndex(int index) {
		if( index<0 || index>=count )
			throw new IllegalArgumentException(String.format("The index:%s not in range:[0,%s)", index,count ));
	}
	
	/**
	 * 如果堆空,则报异常,打断之后流程的执行
	 */
	private void checkIsEmpty() {
		if(count<=0)
			throw new UnsupportedOperationException(String.format("The current heap is empty."));
	}
	
	/**
	 * 如果堆满,则报异常,打断之后流程的执行
	 */
	private void checkIsFull() {
		if( count >= capacity )
			throw new UnsupportedOperationException("The current heap is full.");
	}

}

说明:代码参考自:菜鸟教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值