I've only glanced at the code, but you seem to have a couple issues to deal with.
The cause of your segfault is probably your off-by-one error on line 66.
You should not be dynamically allocating the SkipList, just the nodes.
I'm not sure why you are maintaining separate NIL nodes. This makes life more complicated (and leakier) than it needs to be. If there are no more nodes, just make the next pointer a
nullptr.
You are maintaining a very large node by having an array of 100 next pointers for every node. You are also not tracking the number of levels per node. Make sure to reduce and be careful about it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
struct SkipNode
{
std::size_t level;
T* key;
SkipNode* forward[];
};
SkipNode* CreateSkipNode( std::size_t level )
{
SkipNode* node = ::operator new( sizeof(SkipNode) + sizeof(SkipNode*) * level );
node->level = n;
node->key = nullptr;
while (n--) node->forward[ n ] = nullptr;
return node;
}
SkipNode* CreateSkipNode( const T& key, std::size_t level )
{
SkipNode* node = CreateSkipNode( level );
node->key = new T( key );
return node;
}
void DeleteSkipNode( SkipNode* node )
{
if (node)
{
if (node->key) delete node->key;
if (node->forward[ 0 ]) DeleteSkipNode( node->forward[ 0 ] );
::operator delete( node );
}
}
|
Your head node is just a dummy, it does not have data attached to it, but it should have all the levels. If necessary, you can reallocate to create new levels, or you can just allocate max-levels at the start. This is an implementation detail you should decide and stick to.
Your use of the word “key” is a little distracting, since this limits your skiplist to, essentially, a set. If you are looking to create an associative array, you will want your data to include both key and value. A std::map does this by using a std::pair <key, value> . You could do the same. That is, make sure it is clear in your head that your “key” is only a key in terms of whatever your lookup function is defined to be.
Your main should look like this:
1 2 3 4 5 6
|
int main()
{
SkipList<int> list;
list.insert( 2 ).insert( 3 );
list.print();
}
|
The print function is nice, but I would make sure to add an overloaded stream insertion operator:
1 2 3 4 5 6 7 8 9 10 11 12
|
template <typename T>
class SkipList
{
...
std::ostream& print( std::ostream& outs ) ...
};
template <typename T>
std::ostream& operator << ( std::ostream& outs, const SkipList <T> & list )
{
return list.print( outs );
}
|
Your main function then becomes:
1 2 3 4 5 6
|
int main()
{
SkipList<int> list;
list.insert( 2 ).insert( 3 );
cout << list << "\n";
}
|
Hope this helps.