Функция удаления всех узлов заданного значения в связанном списке

Функция (deleteall) берет связанный список и значение и удаляет все узлы, которые содержат это значение, а затем возвращает список после изменения.

 Pointer deleteall(Pointer l, int v)
{
  
  Pointer temp;

  while(l != NULL)
  {
    if(l->val == v) {
      temp = l;
      l = temp ->next;
      free(temp);
      
    }
    if(l == NULL) return l;
    else l = l->next;
  }
  return l;
}

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


person rem208    schedule 23.07.2020    source источник
comment
return l;, но вы изменили l. Запомните l в начале и верните его.   -  person Paul Ogilvie    schedule 23.07.2020
comment
когда вы free узел, вам также нужно заполнить пробел, то есть связать предыдущий узел со следующим   -  person sebastian    schedule 23.07.2020
comment
логика очень странная. Если этот узел имеет правильное значение, то пропустить следующий независимо? Наверное, не то, что вы хотели сделать.   -  person Tasos Papastylianou    schedule 23.07.2020
comment
Такого рода проблемы идеально подходят для отладчиков. Попробуйте использовать gdb, это позволит вам выполнять эту функцию построчно, чтобы вы могли видеть, где поведение неправильно.   -  person TSG    schedule 23.07.2020
comment
Пожалуйста, попробуйте это stackoverflow.com/questions/59097696/ Это должно помочь вам увидеть все проблемы, упомянутые в комментариях. Я испытываю искушение предложить это как дубликат...   -  person Yunnosch    schedule 23.07.2020
comment
@TSG Если вы знаете отладчик, который может визуализировать проблемы с указателями здесь лучше, чем ручка и бумага, то я хочу знать, какой именно. Это не гдб.   -  person Yunnosch    schedule 23.07.2020
comment
Покажите полную программу, включая то, как это называется, иначе бесполезно строить догадки.   -  person underscore_d    schedule 23.07.2020


Ответы (3)


  1. Вам не нужно возвращать указатель. Вам просто нужно передать заголовок списка в качестве аргумента. Затем создайте временный указатель, который инициализируется туда, куда указывает голова, и с этой точки вы можете работать.
  2. Когда вы найдете узел с val, который необходимо удалить, УБЕДИТЕСЬ, что вы соединяете узел, предшествующий и следующий из узла, который должен быть освобожден. Таким образом, связанный список остается связанным

Настоящим я помещаю пример решения, предполагая, что список а) заголовок не совпадает с тем значением, которое необходимо освободить, и б) последний узел указывает на NULL. Мы храним 2 временных указателя, указывающих на a)Node и b)Node-›next. Мы всегда проверяем Node-›next, и если он один-к-освобождению, подключаем список без него, а потом освобождаем.

void deleteall(Pointer l, int v)
{
  
  Pointer temp, temp2;
  temp2 = l;
  temp = temp2->next;

  while(temp != NULL)
  {
    if(temp->val == v) {
      temp2->next = temp->next;
      free(temp);
      temp = temp2->next

    } else
    {
      temp2 = temp;
      temp = temp->next;
    if(temp == NULL)
      break;

  return;} 

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

person Confidenc3    schedule 23.07.2020
comment
Ваша функция может вызвать неопределенное поведение. - person Vlad from Moscow; 23.07.2020
comment
@VladfromMoscow, пожалуйста, поправь меня в деталях, пожалуйста :) - person Confidenc3; 23.07.2020
comment
"Вам не нужно возвращать указатель". В зависимости от требуемого интерфейса функции может быть присвоен указатель "head" и от нее требуется вернуть указатель "head" (который может отличаться, если первый элемент удаляется). - person CiaPan; 23.07.2020
comment
Это решение также можно украсить последовательными отступами. - person CiaPan; 23.07.2020

Ваша функция всегда возвращает нулевой указатель.

Когда указатель на головной узел не передается по ссылке на функцию, как в определении вашей функции, функция должна вернуть указатель на головной узел.

Это может выглядеть следующим образом

Pointer deleteall(Pointer l, int v)
{
    while ( l && l->val == v )
    {
        Pointer temp = l;
        l = l->next;
        free( temp );
    }

    if ( l )
    {
        for ( Pointer current = l; current->next != NULL; )
        {
            if ( current->next->val == v )
            {
                Pointer temp = current->next;
                current->next = current->next->next;
                free( temp );
            }
            else
            {
                current = current->next;
            }
        }
    }

    return l;
}

Если в main указатель на головной узел также называется как l, то функция должна вызываться как

l = deleteall( l, v );

Более простое определение функции может выглядеть, когда указатель на головной узел передается по ссылке. Например

void deleteall(Pointer *l, int v)
{
    while ( *l != NULL )
    {
        if ( ( *l )->val == v )
        {
            Pointer temp = *l;
            *l = ( *l )->next;
            free( temp );
        }
        else
        {
            l = &( *l )->next;
        }
    }
}

И в основном функция может быть вызвана как

deleteall( &l, v );
person Vlad from Moscow    schedule 23.07.2020

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

Pointer deleteall(Pointer l, int v)
{
    Pointer* prev = &l;

    while (*prev != NULL)        // any items after the previous one?
    {
        Pointer curr = *prev;    // yes - so this is our current item
        if (curr->val == v)
        {
            *prev = curr->next;  // link next item to prev
            free(curr);
        }
        else
            prev = & curr->next; // step forward on the list
    }
    return l;                    // a new head of the list
}
person CiaPan    schedule 23.07.2020