Удаление определенного узла из связанного списка


Мой пойти на создание функции для удаления конкретного узла из связанного списка:

void deleteSpecific(DataType N, Node *&H, Node *&T)
{
  if (H == nullptr) // 1. Check Whether List Is Empty (head == NULL)
  { // If It's Empty Then, Display The Following And Terminate The Function.
    std::cout << std::endl << std::endl;
    std::cout << "\t" << "-> LL| ERR: The List Is Empty. Deletion Not Possible.";
    std::cout << std::endl << std::endl;
    return;
  }
  Node *TMP1 = H, *TMP2; // If It's Not Empty Then, Define Two Node Pointers Temp1 And Temp2 And Put Head In Temp1 Only.
  while (TMP1->data != N) // Keep Moving Temp1 Until it reaches to exact node to delete
  {
    if (TMP1->next == nullptr) // If Node is not found in list then display the message and exit the loop and function.
    {
      std::cout << std::endl << std::endl;
      std::cout << "\t" << "-> LL| ERR: Given Node Not Found. Deletion Not Possible.";
      std::cout << std::endl << std::endl;
      return;
    }
    TMP2 = TMP1;
    TMP1 = TMP1->next;
  }
  if (H->next == nullptr)
  {
    H = nullptr;
    delete TMP1;
  }
  else if (TMP1 == H)
  {
    H = H->next;
    delete TMP1;
  }
  else if (TMP1->next == nullptr)
  {
    TMP2->next = nullptr;
    T = TMP2;
    delete TMP1;
  }
  else
  {
    TMP2->next = TMP1->next;
    delete TMP1;
  }
}  

Логика правильного кода? Я пробовал с несколькими входами и она работает, однако не существует каких-либо особых случаев, которые вы можете заметить, что не буду работать с этим?



1026
1
задан 2 марта 2018 в 08:03 Источник Поделиться
Комментарии
1 ответ

Комментарий Код

Снятие элемента:

Ваши четыре варианта, кажется, чтобы охватить все ситуации.

  if (H->next == nullptr)
{
H = nullptr;
delete TMP1;
}
else if (TMP1 == H)
{
H = H->next;
delete TMP1;
}
else if (TMP1->next == nullptr)
{
TMP2->next = nullptr;
T = TMP2;
delete TMP1;
}
else
{
TMP2->next = TMP1->next;
delete TMP1;
}

Но в реальности есть только две ситуации. Удалении головного узла. При удалении другой узел. Две другие особые случаи при удалении хвоста, и мы должны рассмотреть отдельно.

  Node* TMP1 = H;
Node* TMP2 = nullptr; // set a value for TMP2 so we can check it/

// Find node to delete

// Check if the item being removed is the tail
// and update the tail appropriately.
if (T == TMP1) {
T = (T == H) ? nullptr : TMP2;
}

// Now update the list.
if (TMP2 == nullptr) { // If TMP2 is null this is the head
H = TMP1->next; // Node we are deleting. So reset H
}
else {
TMP2->next = TMP1->next; // Otherwise TMP1 is being deleted and
// TMP2 is the previous node.
}

// Now the node is unlinked delete it.
delete TMP1;

Можно упростить еще больше, используя дополнительную вспомогательную функцию.

Объекты

Почему это отдельно стоящая функция?

void deleteSpecific(DataType N, Node *&H, Node *&T)

Я ожидал бы это, чтобы быть частью класса. Где head и tail являются частью объекта. Так что вам не нужно, чтобы передать их вокруг.

Разделение

Ваш код должен быть управления ресурсами и бизнес-логики. Код смешивает два разных типа код вместе. Здесь руководство ресурса удалить узел из списка. Но у вас также есть некоторые бизнес-логики внутри вашего кода, который разговаривает с пользователем.

std::cout << std::endl << std::endl;
std::cout << "\t" << "-> LL| ERR: The List Is Empty. Deletion Not Possible.";
std::cout << std::endl << std::endl;

Действительно ли это ошибки в первую очередь? Но почему это часть кода, управления ресурсами?

Код управления ресурсами следует завернуть в еще один слой кода, что делает бизнес-логику и информирует пользователя. Таким образом, вы можете повторно использовать deleteSpecific() функцию из другого кусок кода, который не волнует, если данные в списке или нет.

Комментарии

Именования

Имена переменных должны быть нагляден.

 H:   head
T: tail

Это не так нагляден, как вы думаете. Просто это заклинание. Есть несколько других конвенций, вы должны держать в уме.


  1. Идентификаторы, все крышки, как правило, зарезервированы для макросов.

  2. Идентификаторы с заглавной буквы обычно пользовательские "типы"

  3. Идентификаторы с начальной буквы, как правило, объекты.

Это хорошие конвенций следовать и поможет вам определить вещи, как ваш код становится более сложным.

Указатель & Ссылка

В "C" в "*" обычно размещенный объект. Но "C++" гораздо сильнее и "*" а "&" считаются частью типа информации, так что вы обычно разместить эти типа.

void deleteSpecific(DataType N, Node *&H, Node *&T)

Более обычно пишется как:

void deleteSpecific(DataType N, Node*& H, Node*& T)

А вы говорите "Ааааа, а как насчет"

Node *TMP1 = H, *TMP2;

Да он не работает в этой ситуации. Но есть и более важные рекомендации, правила, что делает это положение устарело. Правило, чтобы объявить одну переменную в строке. Таким образом, это должно действительно быть записана так:

Node*  TMP1 = H;
Node* TMP2 = nullptr;

Пожалуйста, перестаньте писать на C++, как это было С. Это совершенно разные языки с их собственные стили и идиомы.

Предпочитаю "\n" за std::endl

Разница между двумя заключается в том, что endl очищает буфер. Обычно это не то, что вы хотите, и делает код весьма неэффективен (промывка пользователей почти всегда неправильно). Если вы считаете, что вам это нужно затем добавить явный флаш.

3
ответ дан 2 марта 2018 в 03:03 Источник Поделиться