Leftist Heaps

  • Target: Speed up merging in O(N)O(N)

  • Heap: Structure Property + Order Property

  • Leftist Heap

    • Order Property – the same
    • Structure Property – binary tree, but unbalanced

[Definition] The null path length, Npl(X), of any node X is the length of the shortest path from X to a node without two children.

  • Npl(NULL)=1Npl(NULL) = –1

  • Npl(X)=min{Npl(C)+1 for all C as children of X}Npl(X) = \min\{ Npl(C) + 1 \text{ for all C as children of X} \}

[Definition] The leftist heap property is that for every node X in the heap, the null path length of the left child is at least as large as that of the right child.

image-20210508184648676
  • The tree is biased to get deep toward the left.

[Theorem] A leftist tree with rr nodes on the right path must have at least 2r12^r – 1 nodes.

Note: The leftist tree of NN nodes has a right path containing at most log2(N+1)\lfloor\log_2(N+1)\rfloor nodes.

  • We can perform all the work on the right path, which is guaranteed to be short
  • Trouble makers: Insert and Merge may destroy leftist heap property

Note: Insertion is merely a special case of merging.

Declaration

1
2
3
4
5
6
7
struct TreeNode 
{
ElementType Element;
PriorityQueue Left;
PriorityQueue Right;
int Npl;
};

Merge (recursive version)

image-20210322104338208
  • Step 1: Merge( H1->Right, H2 )

    image-20210322104407180
  • Step 2: Attach( H2, H1->Right )

    image-20210322104510239
  • Step 3: Swap(H1->Right, H1->Left ) if necessary

1
2
3
4
5
6
7
PriorityQueue Merge( PriorityQueue H1, PriorityQueue H2 )
{
if ( H1 == NULL ) return H2;
if ( H2 == NULL ) return H1;
if ( H1->Element < H2->Element ) return Merge1( H1, H2 );
else return Merge1( H2, H1 );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
static PriorityQueue Merge1( PriorityQueue H1, PriorityQueue H2 )
{
if ( H1->Left == NULL ) /* single node */
H1->Left = H2; /* H1->Right is already NULL and H1->Npl is already 0 */
else
{
H1->Right = Merge( H1->Right, H2 ); /* Step 1 & 2 */
if ( H1->Left->Npl < H1->Right->Npl )
SwapChildren(H1); /* Step 3 */
H1->Npl = H1->Right->Npl + 1; /*If Npl is not updated, */
} /* end else */
return H1;
}

Tp=O(logN)T_p=O(\log N)

Merge(iterative version)

image-20210322110634069
  • Step 1: Sort the right paths without changing their left children

    image-20210322110702001
  • Step 2: Swap children if necessary

    image-20210322110733901

Insert

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PriorityQueue Insert1(ElementType X, PriorityQueue H)
{
PriorityQueue SingleNode;
SingleNode = malloc(sizeof(struct TreeNode));
if( SingleNode == NULL)
Fatal Error("Out of space!!!") ;
else
{
SingleNode->Element = X;
SingleNode->Npl = 0;
SingleNode->Left = SingleNode->Right = NULL;
H = Merge(SingleNode, H);
}
return H;
}

DeleteMin

  • Step 1: Delete the root
  • Step 2: Merge the two subtrees
1
2
3
4
5
6
7
8
9
10
11
12
13
PriorityQueue DeleteMin1(PriorityQueue H)
{
PriorityQueue LeftHeap, RightHeap;
if(Is Empty(H))
{
Error("Priority queue is empty" ");
return H;
}
LeftHeap = H->Left;
RightHeap = H->Right;
free (H);
return Merge(LeftHeap, RightHeap);
}

Tp=O(logN)T_p=O(\log N)

Skew Heaps

  • a simple version of the leftist heaps
  • 斜堆的右路径在任何时刻都可以任意长,所有操作的最坏情形运行时间均为O(N)O(N)
  • Target: Any MM consecutive operations take at most O(MlogN)O(M \log N) time.

Merge

  • Always swap the left and right children except that the largest of all the nodes on the right paths does not have its children swapped.

  • No Npl

Note: Not really a special case, but a natural stop in the recursions.

  • iterative version

    image-20210323205612702

    image-20210323205631230

Note:

  • Skew heaps have the advantage that no extra space is required to maintain path lengths and no tests are required to determine when to swap children.
  • It is an open problem to determine precisely the expected right path length of both leftist and skew heaps.
image-20210508195454584 image-20210508235921408

Amortized Analysis for Skew Heaps

  • Di=D_i = the root of the resulting tree
  • Φ(Di)=\Phi(D_i)= the number of heavy nodes

[Definition] A node pp is heavy if the number of descendants of pp’s right subtree is at least half of the number of descendants of pp, and light otherwise.

Note: The number of descendants of a node includes the node itself.

  • The only nodes whose heavy/light status can change are nodes that are initially on the right path.

Hi:li+hi(i=1,2)Tworst=l1+h1+l2+h2H_i:l_i+h_i(i=1,2) \rarr T_{worst}=l_1+h_1+l_2+h_2

  • Before merge: Φi=h1+h2+h\Phi_i=h_1+h_2+h
  • After merge: Φi+1l1+l2+h\Phi_{i+1}\leq l_1+l_2+h

Tamortized=Tworst+Φi+1Φi2(l1+l2)T_{amortized}=T_{worst}+\Phi_{i+1}-\Phi_i\leq2(l_1+l_2)

l=O(logN)Tamortized=O(logN)l=O(\log N) \rarr T_{amortized}=O(\log N)