bool find(const Tval& value) { // Just a helper metric, to see how fast we are. this->find_steps = 0; this->find_value = value; // We have to empty the last_visited map for each run of find. this->last_visited.clear(); SkipListElement<Tval>* head = this->get_first_element(); // Initial check to see whether value must be appended to front if (!head || this->first_elements[0]->value > value) return false; // head is smaller/equal to value -> visit this->last_visited[this->max_level] = head; ++(this->find_steps); // head is valid for (int level = this->max_level; level > -1; --level) { // The first entry in the list[level+1] only contained bigger elements, // restart at first element of list[level] if (head->value > value) { // This begs the question on how to reset last_visited for level+1 // when we overshot (or even better, prevent overshooting!). // The bug becomes visible quickly once we start to look at greater // sequences (unordered lists for level > 0). // It means that last_visited will sometimes be bigger than value, which // is a logical error. The effect is that we effectivly truncat the // level+1 list upon insertion, thus making find() very costly (as in O(n)). head = this->first_elements[level]; } this->last_visited[level] = head; while (head->value <= value) { this->last_visited[level] = head; SkipListElement<Tval>* next = head->get_next_at(level); if (!next) break; head = next; ++(this->find_steps); } } return (this->last_visited[0]->value == value || this->first_elements[0]->value == value); }
void print() { std::cout << std::endl << "Skiplist contents (last find() run for " << this->find_value << " took " << this->find_steps << " steps): " << std::endl; for (int level = this->max_level; level > -1; --level) { std::cout << "level " << level << ": "; SkipListElement<Tval>* head = this->first_elements[level]; while(head) { std::cout << head->value << " "; head = head->get_next_at(level); } std::cout << std::endl; } }