Field note

binomial_queue

Binomial Queue

  • 每棵树都是最小堆
  • 高度为KK的二项树恰好有2K2^K个结点,且在深度dd处的结点数恰好就是二项系数(Kd)\tbinom{K}{d}

插入时间复杂度最坏是O(logn)O(\log{n}),但是从空开始建堆的时间复杂度是O(1)O(1);

其他操作时间复杂度是O(logn)O(\log{n})

代码分析

BinQueue  Merge( BinQueue H1, BinQueue H2 )
{	BinTree T1, T2, Carry = NULL; 	
	int i, j;
	if ( H1->CurrentSize + H2-> CurrentSize > Capacity ) ErrorMessage();
	H1->CurrentSize += H2-> CurrentSize;
	for ( i=0, j=1; j<= H1->CurrentSize; i++, j*=2 ) {
	    T1 = H1->TheTrees[i]; T2 = H2->TheTrees[i]; /*current trees */
	    switch( 4*!!Carry + 2*!!T2 + !!T1 ) { 
		case 0: /* 000 */
	 	case 1: /* 001 */  break;	
		case 2: /* 010 */  H1->TheTrees[i] = T2; H2->TheTrees[i] = NULL; break;
		case 4: /* 100 */  H1->TheTrees[i] = Carry; Carry = NULL; break;
		case 3: /* 011 */  Carry = CombineTrees( T1, T2 );
			            H1->TheTrees[i] = H2->TheTrees[i] = NULL; break;
		case 5: /* 101 */  Carry = CombineTrees( T1, Carry );
			            H1->TheTrees[i] = NULL; break;
		case 6: /* 110 */  Carry = CombineTrees( T2, Carry );
			            H2->TheTrees[i] = NULL; break;
		case 7: /* 111 */  H1->TheTrees[i] = Carry; 
			            Carry = CombineTrees( T1, T2 ); 
			            H2->TheTrees[i] = NULL; break;
	    } /* end switch */
	} /* end for-loop */
	return H1;
}

首先注意switch语句中的条件4*!!carry + 2*!!T2 + !!T1,实际上就是为了把不是0或1的数字bool化.例如如果T1T_1存在(即第一个堆对应高度为ii的二项树存在,则!!T1 !!T_1 做了两次取非操作后就变成1,否则就是0,T2T_2和进位carrycarry也是同理.

那么我们可以理解4*!!carry + 2*!!T2 + !!T1的含义了,实际上这就是一个三位二进制数,最高位表示是否有carrycarry,即之前的合并是否带来了进位(合并出新的更高的二项树),第二位代表第二个堆H2H_2是否有高度为ii的二项树,最后一位代表是否有高度为ii的二项树.于是就可以与casecase一一对应了.

  1. 000:什么都不做,只要循环结束后结束合并就可以了;
  2. 001:同上;
  3. 010:因为我们最重要返回H1H_1(即H1H_1是合并后的结果),清空H2H_2,因此此时我们只要把H2H_2的树转移到H1H_1,然后把H2H_2对应位置改为NULL,最后等待循环结束后结束合并即可;
  4. 011:从加法操作来看此时没有进位,但会产生进位,当前位需要置0,对应于堆操作就是H1H_1H2H_2当前位变为NULL,进位等于两个堆该高度的二项树合并后的结果;
  5. 100:类似于010的情况,但此时是把carrycarry接到H1H_1上,然后等待循环结束后结束合并即可;
  6. 101:从加法操作来看此时会产生进位,当前位需要置0,因此堆操作就是H1H_1当前位变为NULL,新的carrycarry等于T1T_1和当前的carrycarry合并的结果;
  7. 110:与101类似,只是H2H_2当前位变成NULL,新的carycary等于T2T_2和当前的carrycarry合并的结果;
  8. 111:此时是1+11 + 1还要加上进位11,因此求和后有新的进位,当前位也是1,因此让H1H_1当前位变成carrycarry,新的carrycarry等于T1T_1T2T_2合并的结果,最后给H2H_2当前位变为NULL即可