Я работаю над назначением двоичного дерева поиска для класса, и для этой функции мне нужно следовать псевдокоду моего профессора. К сожалению, я не уверен в одной конкретной детали, и она отказывается уточнять.
Ссылка на псевдокод здесь: https://imgur.com/a/rhjhEIa
SUBROUTINE insert(current, parent, node)
IF current is null
IF parent is null
root = node
ELSE
ID node.value < parent.value
parent.left = node
ELSE
parent.right = node
END IF
RETURN true
END IF
ELSE IF node.value = current.=value
RETURN false
ELSE IF ode.value < current.value
insert(current.left, current, node)
ELSE
insert(current.right, current, node)
END IF
END SUBROUTINE
Вместо node
я попробовал, по-видимому, большинство разрешенных переменных, включая текущую, родительскую (и даже значение, что не сработало. Шокер).
bool recursiveInsert(Node* current, Node* parent, double value)
{
if (current == NULL)
{
if (parent == NULL)
{
}
else
{
if (current->value < parent->value)
{
parent->left = current;
}
else
{
parent->right = current;
}
return true;
}
}
else if (parent->value == current->value)
{
return false;
}
else if (parent->value < current->value)
{
insert(current->left, current->value, current);
}
else
{
insert(current->right, current->value, current);
}
}
Я ожидаю, что вывод добавит значение в двоичное дерево поиска и вернет true, но в настоящее время программа просто выдает ошибку всякий раз, когда я добираюсь до частей, требующих «узел».
Ваша функция всегда возвращает значение?
Почему вы чувствуете, что должны заменить node
чем-то другим? В качестве альтернативы, на какой именно вопрос вы хотите получить ответ? (И это тот же вопрос, который вы задали своему профессору?)
Похоже, псевдокод вашего учителя отсутствует return
перед рекурсивными вызовами функции.
@rici Если вы посмотрите на кодекс пуэдо учителей 1975 года, это будет иметь смысл.
Вы должны включить псевдокод в виде текста: это легко сделать. Кажется, у вас нет аргумента, соответствующего node
в псевдокоде, что затрудняет соответствие вашей функции псевдокоду вашего учителя.
@bailey: мне кажется, псевдокод подходит. Но в посте по-прежнему отсутствует вопрос.
@rici Псевдо в порядке. Гэвин вообще не следил за ним. Он перепутал параметры, но я не могу набрать полный ответ на своем телефоне!!!!
Кажется, у вас тоже нет переменной root
. Предположительно, это глобальная переменная — она не определена в псевдокоде. И аргумент node
, похоже, является структурным типом. Вы на самом деле не указали, какой вопрос вы задали своему учителю; как мы должны угадать, что вы спросили? И вы не показали, как вы ожидаете, что функция будет вызываться. Это может иметь значение.
@bailey: я думаю, мы все знаем, что не так. Но правила здесь таковы, что вы должны на самом деле задать вопрос. «Исправьте мой код для меня» — это не вопрос. Научиться задавать точные вопросы — важный навык.
Как вы упомянули, это функция для вставки узла в двоичное дерево поиска. Параметры следующие
parent
— родитель исследуемого узла. Это будет вызвано с корень дерева.current
— слева или справа от исследуемого родительского узла. При первом вызове функции следует использовать root->left
, если значение текущего узла меньше корня, или root->right
, если значение больше root
. Если root
равно нулю, то current
также должно быть NULL
if (root == NULL)
{
ret = recursiveInsert(NULL, NULL, node);
}
else if (root->value < node->value)
{
ret = recursiveInsert(root->left, root, node);
}
else if (root-> value > node->value)
{
ret = recursiveInsert(root->right, root, node);
}
else
{
//same value, handle error
}
node
— это новый узел, который нужно добавить в дерево. Выделение памяти для этого узла должно быть выполнено до вызова recursiveinsert
, и значение должно быть указано.
Теперь давайте посмотрим на код, который вы написали.
Первая ошибка — использовать третий параметр как double
. Это должен быть параметр типа node
, который уже должен быть выделен ранее.
Из условия проверьте, что
ELSE IF node.value = current.=value
RETURN false
кажется, что node->value
имеет целочисленный тип.
Принимая все это во внимание, обновленный код приведен ниже.
Node* root = NULL; //global variable
...
bool recursiveInsert(Node* current, Node* parent, Node* node)
{
if (current == NULL)
{
if (parent == NULL)
{
root = node;
}
else
{
if (current->value < parent->value)
{
parent->left = node;
}
else
{
parent->right = node;
}
return true;
}
}
else if (node->value == current->value)
{
return false;
}
else if (parent->value < current->value)
{
recursiveInsert(current->left, current, node);
}
else
{
recursiveInsert(current->right, current, node);
}
}
node
в контексте псевдокода — ранее выделенный узел, содержащий вставляемые в дерево данные. Первоначальный вызывающий объект выделяет его (что бессмысленно и никогда не делается в коде RW). В действительности крайне маловероятно, что на самом деле будет реализован этот шаблон, если только вы не рассматриваете библиотеку, которая потенциально движется узлов из одного дерева в другое, и вы хотите избежать затрат на установку/демонтаж самих узлов.
Что касается алгоритма, он довольно прост, хотя и не очень красив:
Если и current
, и parent
равны нулю, это означает, что это должен быть первый узел в дереве, отслеживаемый некоторым глобальным указателем root
. Следовательно, root
назначается непосредственно входящему узлу.
В противном случае, если current
равно нулю, но parent
равно нет, если означает, что parent
является некоторым потенциальным листом в дереве (это означает, что у него есть левый, правый или оба содержащихся указателя, которые равны нулю), и вы приземлились на нулевой указатель. Вставка требует сравнения с родительским значением, чтобы узнать, повесили ли вы узел слева или справа. Обратите внимание, что это неэффективно, так как вы уже сделали это сравнение (в первую очередь, как вы сюда попали).
В противном случае, если current
не является нулевым, мы просто проверяем, равны ли значения или меньше (предполагается большее, если ни одно из них не верно), и входим в поддерево слева или справа, если это оправдано. В этом случае current.left
или current.right
становятся рекурсивными current
, а current
становятся parent
того же самого рекурсивного вызова.
Вот и все. Так работает этот алгоритм. И, честно говоря, это маргинал.
Чтобы реализовать этот алгоритм с вашим списком аргументов (который принимает значение, а не узел в качестве последнего аргумента), вам нужно только убедиться, что выделение узла происходит только тогда, когда пришло время его фактически повесить, а затем Только (есть два таких случая.
bool recursiveInsert(Node* current, Node* parent, double value)
{
bool result = false;
if (current == NULL)
{
if (parent == NULL)
{
root = malloc(sizeof *root);
root->value = value;
root->left = root->right = NULL;
}
else
{
Node *p = malloc(sizeof *p);
p->value = value;
p->left = p->right = NULL;
if (value < parent->value)
{
parent->left = p;
}
else
{
parent->right = p;
}
result = true;
}
}
else if (value < parent->value)
result = recursiveInsert(current->left, current, value);
else if (parent->value < value)
result = recursiveInsert(current->right, current, value);
return result;
}
При вставке значения в дерево вызов будет выглядеть примерно так:
recursiveInsert(root, NULL, value);
Это некрасиво, но это работает. То, что он полагается на глобальное root
присутствие, вероятно, является худшей частью этого алгоритма. Множественное сравнение, вероятно, второе в списке гадостей.
В идеале корень дерева передается в качестве аргумента. Далее, мы можем сделать алгоритм рекурсивным, как сейчас, но больше не полагаться на какие-то глобальные root
. Наконец, мы можем сократить количество аргументов до двух: адрес указателя (изначально адрес корневого указателя) и вставляемое значение. использование указателя на указатель, поскольку метод доступа к дереву делает этот алгоритм элегантным, независимо от того, используется ли рекурсия или нет. Оба представлены ниже:
bool treeInsert(Node **pp, double value)
{
bool result = false;
if (!*pp)
{
*pp = malloc(sizeof **pp);
(*pp)->value = value;
(*pp)->left = (*pp)->right = NULL;
result = true;
}
else if (value < (*pp)->value)
{
result = recursiveInsert(&(*pp)->left, value);
}
else if ((*pp)->value < value)
{
result = recursiveInsert(&(*pp)->right, value);
}
return result;
}
bool treeInsert(Node **pp, double value)
{
bool result = false;
while (*pp)
{
if (value < (*pp)->value)
pp = &(*pp)->left;
else if ((*pp)->value < value)
pp = &(*pp)->right;
else break;
}
if (!*pp)
{
*pp = malloc(sizeof **pp);
(*pp)->value = value;
(*pp)->left = (*pp)->right = NULL;
result = true;
}
return result;
}
в любом случае вы вызываете, передавая адрес корневого указателя (таким образом, указатель на указатель), где пустая попытка обозначается нулевым корнем:
treeInsert(&root, value);
Любая функция выполнит поставленную задачу. Я оставляю защиту от ошибок как задачу для вас (проверьте свои mallocs).
Если
current==NULL
что это делает -->current->value < parent->value
?