Quantcast
Channel: Анал со зрелой
Viewing all articles
Browse latest Browse all 47

Binary Heaps in C

$
0
0

After conducting a short search for a decent implementation of a binary heap in C I ended up having to write one. Some requirements were: no dependencies, no dynamically allocated memory, no need to modify the source to extend to new types.

The first result found from a google search yielded this OK implementation. Unfortunately it makes use of allocation routines and has a couple annoying header dependencies. It also defines the macro CMP, a fairly commonly used macro. The idea is a good idea — if we define a modifiable macro the heap can be converted to a min or max heap as needed. However in practice this is not that useful; if we use min heap first, and later require max heap, we then still need to copy + paste out a new set of heap source files. Why not just make the two different sources up-front once and for all? It also typedefs the internal type, so use of the header would require alteration of the header. This triggers recompiles, and is a pain to maintain as new types of data are needed, perhaps copy and pasting new implementations around, modifying the build system as necessary to accommodate the new files, etc.

This implementation suffers almost identical downsides, and has no way to store auxiliary data at all. Heaps of only single integers are not very useful!

One nice strategy is it uses a void* and stride size so the heap could hold arbitrary pieces of POD memory. A small downside to this strategy is the requirement to constantly multiply stride with indices, which sort of clutters the source. Not really a big deal, just a nit-pick. I actually could not find a single implementation that takes this strategy.

In the end I decided to use an indirection trick: sort an array of struct { int key, int val }, where the val member is used as an index into an array of auxiliary data. The key integer is used for sorting, and the val integer is used to lookup data associated with a key. This implementation style can be re-used for any kind of data without C++ templates, and without requiring the user to mess with the source, ever.

typedef struct
{
	int key;
	int val;
} Item;

void Cascade( Item* items, int N, int i )
{
	int min = i;
	Item min_val = items[ i ];
	int i2 = 2 * i;

	while ( i2 < N )
	{
		int left = i2;
		int right = i2 + 1;

		if ( items[ left ].key < min_val.key ) min = left;
		if ( right < N && items[ right ].key < items[ min ].key ) min = right;
		if ( min == i ) break;

		items[ i ] = items[ min ];
		items[ min ] = min_val;
		i = min;
		i2 = 2 * i;
	}
}

void MakeHeap( Item* items, int count )
{
	for ( int i = count / 2; i > 0; --i )
		Cascade( items, count, i );
}

Item PopHeap( Item* items, int count )
{
	Item root = items[ 1 ];
	items[ 1 ] = items[ count - 1 ];
	Cascade( items, count - 1, 1 );
	return root;
}

void PushHeap( Item* items, int count, Item a )
{
	int i = count;
	items[ count ] = a;

	while ( i )
	{
		int j = i / 2;
		Item child = items[ i ];
		Item parent = items[ j ];

		if ( child.key < parent.key )
		{
			items[ i ] = parent;
			items[ j ] = child;
		}

		i = j;
	}
}

#include <assert.h>
#include <stdlib.h>

void AssertHeap( Item* data, int N, int i )
{
	int left = i * 2;
	int right = left + 1;

	if ( left < N )
	{
		Item iVal = data[ i ];
		Item lVal = data[ left ];
		assert( iVal.key <= lVal.key );

		if ( right < N )
		{
			Item rVal = data[ right ];
			assert( iVal.key <= rVal.key );

			AssertHeap( data, N, left );
			AssertHeap( data, N, right );
		}
	}
}

void PrintHeap( Item* data, int count )
{
	int i = 1;
	int j = 2;
	int N = count;
	do
	{
		if ( j > N ) j = N;
		for ( int index = 0; index < N / 2 - i; ++index ) printf( " " );
		for ( int index = i; index < j; ++index ) printf( "%d ", data[ index ] );
		printf( "\n" );

		i *= 2;
		j = i * 2;
	}
	while ( i < N );
	printf( "\n" );
}

void TestHeap( )
{
	int N = 64;
	Item data[ 64 ];

	for ( int i = 1; i < N / 2; ++i )
	{
		Item item;
		item.key = rand( ) % 10;
		item.val = 0;
		PushHeap( data, i, item );
	}
	AssertHeap( data, N / 2, 1 );
	for ( int i = N / 2; i < N; ++i )
	{
		Item item;
		item.key = rand( ) % 10;
		item.val = 0;
		data[ i ] = item;
	}
	MakeHeap( data, N );
	PrintHeap( data, N );
	AssertHeap( data, N, 1 );

	Item prev;
	prev.key = 2147483647;
	prev.val = 0;
	for ( int i = 0; i < N; ++i )
	{
		Item a = PopHeap( data, N - i );
		assert( a.key <= prev.key );
		a = prev;
		PrintHeap( data, N - (i + 1) );
		AssertHeap( data, N - i, 1 );
	}
}

int main( )
{
	TestHeap( );
	return 0;
}

And here’s the output:

0
      1 1
    2 4 4 1
8 7 9 5 5 4 7 8

      1
     2 1
   7 4 4 1
8 8 9 5 5 4 7

      1
     2 1
   7 4 4 7
8 8 9 5 5 4

     1
    2 4
  7 4 4 7
8 8 9 5 5

     2
    4 4
  7 5 4 7
8 8 9 5

    4
   5 4
 7 5 4 7
8 8 9

    4
   5 4
 7 5 9 7
8 8

   4
  5 7
7 5 9 8
8

   5
  5 7
7 8 9 8

  5
 7 7
8 8 9

  7
 8 7
9 8

 7
8 8
9

 8
9 8

8
9

9

TwitterRedditFacebookShare


Viewing all articles
Browse latest Browse all 47

Trending Articles