Пропустить список C ++ сегментации ошибка

Я пытаюсь реализовать список пропусков, используя эту статью Пропустить список.

Код:

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<limits>

using namespace std;

template<class T>
class SkipList{
private:
class SkipNode{
public:
T* key; //Pointer to the key
SkipNode** forward; //Forward nodes array
int level; //Node level
//SkipNode constructor
SkipNode(T* key, int maxlvl, int lvl){
forward = new SkipNode*[maxlvl];
this->key=key;
level=lvl;
}
//Method that print key and level node
print(){
cout << "(" << *key << "," << level << ") ";
}
};

SkipNode *header,*NIL; //Root and End pointers
float probability; //Level rate
int level; //Current list level
int MaxLevel; //Maximum list levels number

//Function that returns a random level between 0 and MaxLevel-1
int randomLevel(){
int lvl = 0;
while( (float(rand())/RAND_MAX < probability) && (lvl < MaxLevel-1) )
lvl++;
return lvl;
}

public:
//SkipList constructor
SkipList(float probability, int maxlvl){
this->probability = probability;
MaxLevel = maxlvl;
srand(time(0));
header=new SkipNode(NULL,MaxLevel,0); //Header initialization
T* maxValue = new T;
*maxValue = numeric_limits<T>::max(); //Assign max value that T can reach
NIL = new SkipNode(maxValue,0,0); //NIL initialization
level=0; //First level
for(int i=0; i<MaxLevel; i++){ //Every header forward node points to NIL
header->forward[i]=NIL;
}
}

//SkipList destructor
~SkipList(){
delete header;
delete NIL;
}

//Method that search for a key in the list
SkipNode* search(T* key){
SkipNode* cursor = header;
//Scan the list
for(int i=level; i>=0; i--)
while(*(cursor->forward[i]->key) < (*key))
cursor=cursor->forward[i];
cursor=cursor->forward[0];
if(*(cursor->key) == *key)
return cursor;
return NULL;
}

//Method that insert a key in the list
SkipList* insert(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Node already inserted
return this;
}
int lvl = randomLevel(); //New node random level
if(lvl > level){ //Adding missing levels
for(int i=level+1; i<=lvl; i++)
update[i]=header;
level=lvl;
}
SkipNode* x = new SkipNode(key,MaxLevel,lvl); //New node creation
for(int i=0; i<=lvl; i++){ //Fixing pointers
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}
return this;
}

//Method that delete a key in the list
SkipList* erase(T* key){
SkipNode* cursor = header;
SkipNode* update[MaxLevel]; //Support array used for fixing pointers
//Scan the list
for(int i=level; i>=0; i--){
while(*(cursor->forward[i]->key) < *(key))
cursor=cursor->forward[i];
update[i]=cursor;
}
cursor=cursor->forward[0];
if(*(cursor->key) == *(key)){ //Deletetion of the founded key
for(int i=0; i<=level && update[i]->forward[i] == cursor; i++){
update[i]->forward[i] = cursor->forward[i];
}
delete cursor;
while(level>0 && header->forward[level]==NIL){
level=level-1;
}
}
return this;
}

//Method that print every key with his level
SkipList* print(){
SkipNode* cursor = header->forward[0];
int i=1;
while (cursor != NIL) {
cursor->print();
cursor = cursor->forward[0];
if(i%15==0) cout << endl; i++;
}
cout << endl;
return this;
}
};

main(){
SkipList<int>* list = new SkipList<int>(0.80, 8);
int v[100];
for(int i=0; i<100; i++){
v[i]=rand()%100;
list->insert(&v[i]);
}
list->print();
cout << endl << "Deleting ";
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
list->erase(&v[h]);
}
cout << endl;
list->print();
cout << endl;
for(int i=0; i<10; i++){
int h = rand()%100;
cout << v[h] << " ";
if(list->search(&v[h]))
cout << " is in the list" << endl;
else
cout << " isn't in the list" << endl;

}
delete list;
}

Это дает мне Ошибка сегментации в строке 59 (цикл for на вставке), но я не могу понять почему. Можете ли вы помочь мне, пожалуйста? Я приму любое другое улучшение, которое вы предложите. Мой крайний срок — два дня, поэтому я прошу помощи.

РЕДАКТИРОВАТЬ:
Я исправил код с предложениями bebidek (спасибо). Теперь первый уровень равен 0. Кажется, что он работает, но иногда некоторые узлы вставляются неправильно, и поиск дает неверный результат.

ПОСЛЕДНИЕ РЕДАКТИРОВАТЬ:
Работает, спасибо всем

ОДИН БОЛЬШЕ РЕДАКТИРОВАТЬ:
Добавлены комментарии к коду, если у вас есть предложения, пожалуйста

-2

Решение

Вероятно, самая большая проблема в вашем коде NIL=new SkipNode(numeric_limits<T*>::max());

Прежде всего, я подозреваю, что вы хотите key указатель, указывающий на адрес памяти, который содержит максимально возможный int значение.
Но это не то, что на самом деле здесь происходит. Вместо key указатель указывает на максимально возможный адрес памяти, который, скорее всего, недоступен для вашего процесса.
Так же forward Свойство, вероятно, содержит массив указателей мусора.

Затем, когда первый цикл в insert Метод выполняется, это приводит к 2 проблемам:

  1. while(*(cursor->forward[i]->key) < *(key)) будет сравнивать значение ключа с неверным указателем
  2. cursor=cursor->forward[i]; переназначит курсор на неверный указатель

Сначала я бы предложил изменить дизайн, чтобы SkipNode оставил значение T вместо указателя:

class SkipNode{
public:
T key;
SkipNode* forward[100];

Это сделает ненужным много кода, связанного с указателем, и упростит код, чтобы снизить вероятность нарушения прав доступа.

Также может быть чище использовать реальный NULL (или событие лучше nullptr) значение вместо манекена NIL значение для обозначения конца списка.

0

Другие решения

Итак, первая проблема, когда вы создаете узел NIL:

NIL=new SkipNode(numeric_limits<T*>::max());

В качестве аргумента вы должны использовать указатель на существующую переменную, например:

T* some_name = new T;
*some_name = numeric_limits<T>::max();
NIL = new SkipNode(some_name);

Обратите внимание, я использовал T вместо T* в numeric_limits, Конечно, вы должны помнить об удалении этой переменной в деструкторе.

Вторая проблема заключается в том, что level переменная в вашем коде иногда включительно (я имею в виду номер уровня level существует) как в строке 61, а иногда и эксклюзивно (номер уровня level не существует), как в строке 71. Вы должны быть последовательными.

Третья проблема в строке 52. Вы, вероятно, имеете в виду cursor=cursor->forward[1];, но после цикла я = 0, и forward[0] не имеет никакого смысла в вашем коде.

РЕДАКТИРОВАТЬ:
Четвертая и пятая проблема в функции стирания.

cursor->~SkipNode();

Он не удалит ваш узел, а только запустит пустой деструктор. использование delete cursor; вместо.

И в цикле вы, вероятно, хотели написать update[i]->forward[i] == cursor вместо !=,

ОДИН БОЛЬШЕ РЕДАКТИРОВАТЬ:
Вы не реализовали ни одного деструктора SkipList, а также забыли о delete list; в конце main (). Эти два даст вам утечку памяти.

ДРУГОЕ РЕДАКТИРОВАНИЕ:

srand(time(0));

Эта строка должна быть выполнена один раз в начале main и все. Если вы выполняете его перед каждым случайным поколением, вы будете получать один и тот же результат каждый раз (так как time (0) считает только секунды, и ваша программа может запустить функцию randomLevel() более одного раза в секунду).

Вы также забыли о переписывании precision переменная в конструкторе SkipList,

СЛЕДУЮЩАЯ РЕДАКТИРОВКА:
В вашем insert Функция у вас нет уровня рандомизации. Я имею в виду, у вас нет возможности вставлять узел уровня ниже уровня всего списка пропусков. Это не ошибка, которая приведет к краху вашей программы или даст неправильные результаты, но временная сложность запросов в вашей структуре составляет O (n) вместо O (log n).

Вы должны использовать lvl вместо level в этом цикле в функции вставки:

for(int i=1; i<level; i++){
x->forward[i] = update[i]->forward[i];
update[i]->forward[i] = x;
}

А также минимальный результат вашей случайной функции randomLevel должно быть 1 вместо 0, так как вы не хотите, чтобы уровень ведьмы = 0.

0

По вопросам рекламы [email protected]