索引最大堆

      普通堆(本文仅以最大堆为例)的局限性主要有以下两点:
  1.如果原始待排序或者求最值的数据元素很大,交换它们的位置性能损耗。

  2.元素在堆化后的数组中位置不确定,只能过遍历找到。

       解决上述两个弊端就是用索引的方式,二叉树的节点里存储的不再是原始数据,而是原始数据在数组中的索引。在插入弹出操作中的上浮下沉比较交换中,比较大小仍然是原始数据,但交换和、上浮下沉就是对于原始数据的索引而言。显然要在最大堆的数据结构中加入一个索引数组,该索引数组的索引是二叉树层次遍历的序列序号(i),存储的是二叉堆中第i个元素在原始数据数组中的索引。数据结构如下:

public class IndexMaxPQ<Item extends Comparable<Key>> {

	public static final int DEFAULT_SIZE=17;
	
	public Key[] data; //存放原始待排序或者求最值的数据
	public int[] index;	//存储最大堆的原始数据在data中的数组索引,index[i]=x表示堆的层次遍历序列的第i个位置的值为data[x]
	int size;			//最大堆的大小
}

关键操作上浮和下沉因为对二叉堆节点而言所有没有任何变化:

        //上浮操作
	private void swim(int i)
	{
		while(i/2>=1)
		{
			if(!less(i,i/2))
				exchange(i,i/2);
			else
				break;
			i/=2;
		}
	}
	
	//下沉操作
	private void sink(int i)
	{
		while(2*i<=this.size)
		{
			int j=2*i;
			if(j<this.size && less(j,j+1)) j++;//索引为i的值与其子节点中较大的进行比较
			if(!less(j,i))
				exchange(i,j);
			else
				break;
			i*=2;
		}
	}
但是比较交换的内部代码发生了变化:
        private boolean less(int a,int b)
	{
		if(this.data[this.index[a]].compareTo(this.data[this.index[b]])<=0)
			return true;
		else
			return false;
	}
	
	private void exchange(int i,int j)
	{
		int tmp=this.index[i];
		this.index[i]=this.index[j];
		this.index[j]=tmp;
	}

插入、删除、弹出、修改四个基本操作都是在上浮和下沉、比较和交换上完成。但同时要维护原始数据数组data、索引数组index、堆的规模size。代码如下:

        public void delete(int i)//删除data数组索引为i的元素
	{
		int j=0;
		this.data[i]=null;
		for(;j<=this.size;j++)
			if(this.index[j]==i)
				break;
		this.exchange(j, this.size--);
		this.swim(j);
		this.sink(j);
	}

        public int delMax()
	{
		int ret=this.index[1];
		exchange(1,this.size);
		this.index[this.size]=0;
		sink(1);
		this.data[this.size--]=null;
		return ret;
	}

        public void change(int k,Key Key)
	{
		 if(k>=1 && k<=this.size)
		 {
			 this.data[k]=Key;
			 int i=1;
			 for(;i<this.index.length;i++)
				 if(this.index[i]==k)
					 break;
			 swim(i);
			 sink(i);
		 }
	}

        public void delete(int i)//删除data数组索引为i的元素
	{
		int j=0;
		this.data[i]=null;
		for(;j<=this.size;j++)
			if(this.index[j]==i)
				break;
		this.exchange(j, this.size--);
		this.swim(j);
		this.sink(j);
	}

以上代码是学习Princeton《Algorithms》参考算法思想设计而来。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值