有没有人知道Java中的最大堆实现允许您添加具有某些键的元素,然后它会以某种方式将它们保存在堆中,以便它可以在恒定时间内获取max-key元素。并且它还允许您在O(log n)时间内更改某些元素的键。我认为这意味着堆会跟踪堆中每个元素的位置。
答案 0 :(得分:1)
以下是对堆上所有操作的简单实现,其中包含注释中指示的时间复杂度以及一些解释。您可以删除main方法中的注释字符 看看其他业务的运作情况。随时随地查询。
/*ALL DISCUSSION IS IN CONTEXT OF MAX HEAP.EXTRACTING THE MAX OR MIN TAKES O(LOG N) TIME. FIRST WE REMOVE THE VERY FIRST ELEMENT WHICH IS THE MAXIMUM (OR THE MINIMUM.
* (IN CASE OF MIN HEAP)THEN WE PUT THE LAST ELEMENT IN FRONT POSITION. THIS TAKES CONSTANT TIME.THEN WE INITIATE HEAPIFY AT POSITION 1, WHICH WILL
* TAKE TIME O(LOGN) BECAUSE HEIGHT AT POSITION 1 IS H AND HEAPIFY INITIATED AT HEIGHT H TAKES O(H) TIME.SO THE TIME COMPLEXITY
* OF THIS ALGO IS O(LOG N) WHERE HEIGHT OF HEAP=H=LOG N.
*/
import java.util.*;
class HEAP
{
private LinkedList<Integer> heap;
public static void main(String []args)
{
Scanner s=new Scanner(System.in);
System.out.println("enter the number elements in the heap");
int n=s.nextInt();
HEAP o = new HEAP();
o.heap=new LinkedList<Integer>();
for(int i=0;i<n;++i)
o.heap.add(s.nextInt());
o.build_heap();
o.heapsort();
System.out.println("the sorted elements are");
for(int e:o.heap)
System.out.println(e);
/*
//int max=o.extract_max();
System.out.println();
o.insert(300);
System.out.println("Max Heap is");
for(int e:o.heap)
System.out.print(e+" ");
o.increase_key(3,900);
System.out.println();
System.out.println("Max Heap is");
for(int e:o.heap)
System.out.print(e+" ");*/
}
public void build_heap()
{
for(int i=heap.size()/2;i>=1;--i)
heapify(i,heap.size());
}
public int extract_max()
{
int max=heap.remove(0);
int last=heap.remove(heap.size()-1);
heap.add(0,last);
heapify(1,heap.size());
return max;
}
public void heapify(int i,int size)
{
int l=2*i,r=l+1;
int largest;
if(l<=size&&heap.get(l-1)>heap.get(i-1))
largest=l;
else largest=i;
if(r<=size&&heap.get(r-1)>heap.get(largest-1))
largest=r;
if(largest!=i)
{
int temp=heap.get(i-1);
heap.set(i-1,heap.get(largest-1));
heap.set(largest-1,temp);
heapify(largest,size);
}
}
/*ITS PRETTY STRAIGHT FORWARD ALGORITHM FIRST U INCREASE THE KEY AND THEN YOU BEGIN THE COMPARISON WITH THE PARENT BECAUSE U HAVE
* INCREASED SO THE CHILD SUBTREES WOULD BE ALRIGHT ALL U HAVE TO DO IS BEGIN COMPARISON WITH THE PARENT AND GO ON TILL ROOT IF
* U HAVE TO. TIME TAKEN IS O(LOG N). WORST CASE OCCURS WHEN FROM LEAF YOU TRAVEL ALL THE WAY UP TO ROOT.
*/
public void increase_key(int i,int k)
{
if(k<= heap.get(i-1))
System.out.println("The increase_key did not go all the way through");
int temp;
heap.set(i-1,k);
/*Now the left and right subtrees are fine but its parent may be upset so get to it and do this till root if you have to*/
while(i>1&&heap.get(i-1)> heap.get(i/2-1))
{
temp=heap.get(i-1);
heap.set(i-1,heap.get(i/2-1));
heap.set(i/2-1,temp);
i=i/2;
}
}
/*IT IS ALSO A PRETTY STRAIGHT FORWARD ALGORITHM. YOU DECREASE THE KEY NOW CAUSE ITS MAX HEAP THE PARENT IS ALL FINE BUT THE
* CHILD MAY BE UPSET SO BEST THING TO DO IS TO CALL THE HEAPIFY ALGORITHM FROM THE INDICATED POSITION.WORST CASE TIME TAKEN IS
* OR SIMPLY THE TIME TAKEN IS O(LOG N). WORST CASE OCCURS WHEN FROM ROOT YOU GO ALL THE WAY TO LEAF.
*/
public void decrease_key(int i,int k)
{
if(k>=heap.get(i-1))
System.out.println("The increase_key did not go all the way through");
heap.set(i-1,k);
/*Now after decreasing the key the parent is fine but the child may be upset so why not call heapify*/
heapify(i,heap.size());
}
/*INSERT IS ALSO PRETTY MUCH STRAIGHT FORWORD BECAUSE . SIMPLY INSERT AT LAST POSITION. THEN START THE COMPARISON FROM ITS PARENT
* AND THIS BUSINESS MAY CONTINUE ALL THE WAY UP TO THE ROOT. HENCE THE TIME TAKEN IS O(LOG N) AND THE WORST CASE OCCURS WHEN
* THE BUSINESS DOES REALLY GO UP TO THE ROOT. REMEMBER WHEN WE MENTION THAT TIME TAKEN IS O(LOG N), IT ALSO MEANS THAT THIS IS
* THE WORST TIME BECAUSE IT MEANS THAT IN NO CASE THE TIME TAKEN CAN BE GREATER THAN CLOG N , C>0. BUT THIS DOES NOT NECESSARILY
* MEAN IT IS ALWAYS THE TIME.
*/
public void insert(int k)
{
/*element inserted at last position and from its parent work begins of comparing and will go on till root if it has to*/
heap.add(k);
int temp;
int i=heap.size();
while(i>1&&heap.get(i/2-1)<heap.get(i-1))
{
temp=heap.get(i-1);
heap.set(i-1,heap.get(i/2-1));
heap.set(i/2-1,temp);
i=i/2;
}
}
/*HEAPSORT IS VERY CLEVER ALGORITHM THAT TAKES NLOG(N) TIME IN ALL CASES AND IS IN PLACE. MERGESORT IS AN ALGORITHM HAS TIME
* PERFORMANCE EQUIVALENT TO HEAPSORT BUT ITS SPACE COMPLEXITY IS ALWAYS LINEAR. QUICKSORT IS INPLACE BUT IN WORST CASE IT TAKES
* TIME QUADRATIC. HEAPSORT IS A WIN WIN SITUATION, IT SEEMS TO BE BECAUSE IT WINS OVER BOTH QUICKSORT AND MERGESORT.HEAPSORT
* DOES NOT MAKE USE OF CACHE MEMORY BECAUSE THERE IS NO LOCALITY OF REFERENCE AND THE REFERECES ARE SPREAD OUT IN MEMORY.
* TECHNIQUE IS VERY SIMPLE. THE LARGEST ELEMENT IS FIRST ONE SO WE SWAP IT WITH THE LAST ELEMENT AND DECREASE THE HEAP SIZE BY
* ONE AND THEN CALL HEAPIFY(1) SO THAT IT DOES NOT EFFECT THE LAST ELEMENT AS WE HAVE DECREMENTED THE SIZE BY ONE.ALSO THE LAST
* ELEMENT IS AT ITS CORRECT POSITION, THAT IS WHY WE DO NOT WANT TO DISTURB IT AND THEREFORE WE DECREASE THE SIZE BY ONE. WE WILL
* REPEAT THIS STEP N-1 TIMES BECAUSE THE SMALLEST ELEMENT WILL AUTOMATICALLY BE IN ITS APPROPRIATE PLACE.
*
* HEAPSORT HOWEVER IS NOT PREFERRED.THE REASON IS:
*
* HEAPSORT IS NOT A STABLE SORTING ALGORITHM. THE REASON IS BECAUSE IT DOES NOT PRESERVE THE RELATIVE ORDER OF THE ELEMENTS WITH
* SAME KEY. SAY THE HEAP IN STARTING OF THE ALGO IS A C ..... D D ......S. SO WHEN THE SORTING WILL BE DONE, THE D ON THE LEFT
* WILL BE REMOVED FIRST AND THEN THE D ON RIGHT WILL BE REMOVED, THIS IS KIND OF SWAPPING OF THE POSITIONS AND THIS IS THE REASON
* WHY HEAPSORT IS NOT STABLE.
* SECONDLY HEAPSORT IS NOT CACHE FRIENDLY DUE TO LACK OF LOCALITY OF REFERENCE. THE REFERENCES IN THE ARRAY ARE MADE IN SUCH WAY
* THAT THE CACHE MISS ARE MORE THAN IN THE CASE OF QUICKSORT.
*/
public void heapsort()
{
LinkedList<Integer> sorted=new LinkedList<Integer>();
int k=heap.size();
for(int i=1;i<heap.size();++i)
{
int temp=heap.get(0);
heap.set(0,heap.get(k-1));
heap.set(k-1,temp);
heapify(1,--k);
System.out.println();
for(int e:heap)
System.out.print(e+" ");
}
}
}
答案 1 :(得分:0)
如果您计划进行大量增加键操作,那么您可能需要查看Fibonacci堆或Brodal队列,因为它们为增加键操作提供了更好的渐近复杂性。 Google搜索具有Java关键字的特定名称可能会找到您所需的名称。
这是我发现的一个:
http://www.keithschwarz.com/interesting/code/?dir=fibonacci-heap