#c #algorithm #recursion #data-structures #linked-list
#c #алгоритм #рекурсия #структуры данных #связанный список
Вопрос:
Я пытаюсь изменить связанный список с помощью рекурсии. Ранее я написал код для этого, используя простой while
цикл, и он отлично сработал. Я не могу понять, почему мой код рекурсии не работает.
#include<iostream>
using std::cout;
using std::endl;
struct node
{
int data;
node* next;
};
class LinkedLists
{
private:
node* head;
node* temp;
public:
LinkedLists() // default constructor
{
head = NULL;
temp = NULL;
}
void add_data(int d)
{
node* new_node = new node; // create a pointer to the new node
new_node->next = NULL;
new_node->data = d;
if (head == NULL)
{ head = new_node;
temp = head;
}
else
{
temp = head;
while(temp->next != NULL)
{
temp = temp->next;
}
temp->next = new_node; // final node now points to the new_node
}
}
void print_list()
{
temp = head;
while(temp!=NULL)
{
std::cout<<temp->data<<" ";
temp = temp->next;
}
}
void reverse()
{
// reverse a linked list
node* prev_node;
node* next_node;
node* temp_ptr;
prev_node = NULL;
temp_ptr = head;
next_node = temp_ptr->next;
while(next_node != NULL)
{
temp_ptr->next = prev_node;
prev_node = temp_ptr;
temp_ptr = next_node;
next_node = temp_ptr->next;
}
temp_ptr->next = prev_node;
head = temp_ptr;
}
void repeat(node* prev_node, node* temp_ptr,node* next_node)
{
temp_ptr->next = prev_node;
prev_node = temp_ptr;
temp_ptr = next_node;
if (next_node != NULL)
{
next_node = temp_ptr->next;
repeat(prev_node,temp_ptr,next_node);
}
head = temp_ptr;
}
void recursive_reverse()
{
node* prev_node;
node* next_node;
node* temp_ptr;
prev_node = NULL;
temp_ptr = head;
next_node = temp_ptr->next;
repeat(prev_node,temp_ptr,next_node);
}
};
int main()
{
LinkedLists l; // create a linked list object
l.add_data(110);
l.add_data(140);
l.add_data(101);
l.add_data(140);
l.add_data(101);
l.add_data(140);
l.add_data(101);
l.add_data(120);
cout<<endl;
l.print_list();
l.reverse();
cout<<endl;
l.print_list();
l.recursive_reverse();
cout<<endl;
l.print_list();
}
Вывод:
110 140 101 140 101 140 101 120
120 101 140 101 140 101 140 110
101 120
Ожидаемый результат:
110 140 101 140 101 140 101 120
120 101 140 101 140 101 140 110
110 140 101 140 101 140 101 120
Комментарии:
1. Несвязанный: вы используете,
new
но неdelete
. Ваша программа дает утечку. Я бы сказал, что исправление этого важнее, чем его изменение.2. Где я должен использовать
delete
? Разве это не приведет к удалению узла? Или мне не следует использоватьnew
в первую очередь?3. Если вы создаете
LinkedLists
и заполняете его данными, аLinkedLists
он выходит за пределы области видимости — предполагается, что это очистит всю выделенную ему память. Вы просто оставляете его выделенным. Если вы будете делать это в цикле, у вас в конечном итоге закончится память. Иногда вы можете использовать интеллектуальные указатели, но для такого рода вещей, вероятно, вам нужны необработанные указатели, но это делает управление памятью действительно важным. Первым шагом может быть деструктор, такой как:~LinkedList() { for(node* temp; head; head = temp) { temp = head->next; delete head; } }
или аналогичный.4. О
delete
вопросе: просто реализуйте деструкторLinkedLists::~LinkedLists()
, который удалит все ранее выделенные данные. Вы также можете сделать это рекурсивно, однако обычно лучше выполнять такого рода действия итеративно для больших объектов.5. @Yatin Да, вам нужен деструктор (подобный тому, который я показал выше), но вам также нужно подумать о правиле трех / пяти / нуля
Ответ №1:
Для начала неясно, почему класс связанного списка имеет имя во множественном числе.
class LinkedLists
^^^
Будет более естественно назвать его как LinkedList
без окончания 's'
.
Элемент данных temp
является избыточным и должен быть удален. Вместо этого вы можете заменить этот элемент данных локальными переменными в функциях-членах.
Структура node
должна быть закрытым членом класса LinkedList
.
Я не просмотрел все ваши реализации функций, но функция recursive_reverse
может быть определена следующим образом, как показано в демонстрационной программе ниже.
Вот вы где.
#include <iostream>
#include <functional>
class LinkedList
{
private:
struct node
{
int data;
node* next;
} *head = nullptr;
public:
LinkedList() = default;
// These two special member functions you can implement yourself
LinkedList( const LinkedList amp;) = delete;
LinkedList amp; operator =( const LinkedList amp; ) = delete;
~LinkedList()
{
clear();
}
void clear()
{
while ( head )
{
delete std::exchange( head, head->next );
}
}
void add_data( int d )
{
node **current = amp;head;
while ( *current ) current = amp;( *current )->next;
*current = new node { d, nullptr };
}
friend std::ostream amp; operator <<( std::ostream amp;os, const LinkedList amp;list )
{
for ( const node *current = list.head; current != nullptr; current = current->next )
{
os << current->data << " -> ";
}
return os << "null";
}
void recursive_reverse()
{
if ( head != nullptr amp;amp; head->next != nullptr )
{
node *current = head;
head = head->next;
recursive_reverse();
current->next->next = current;
current->next = nullptr;
}
}
};
int main()
{
LinkedList list; // create a linked list object
const int N = 10;
for ( int i = 0; i < N; i )
{
list.add_data( i );
}
std::cout << list << 'n';
list.recursive_reverse();
std::cout << list << 'n';
list.recursive_reverse();
std::cout << list << 'n';
return 0;
}
Вывод программы
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
Если использовать вспомогательную функцию для функции-члена recursive_reverse
, то функция должна быть частной статической функцией-членом. Пользователь списка не должен вызывать его напрямую.
Вот демонстрационная программа.
#include <iostream>
#include <functional>
class LinkedList
{
private:
struct node
{
int data;
node* next;
} *head = nullptr;
static void repeat( node *previous, node * amp;head )
{
if ( head )
{
node *current = head;
if ( head->next )
{
head = head->next;
repeat( current, head );
}
current->next = previous;
}
}
public:
LinkedList() = default;
~LinkedList()
{
clear();
}
void clear()
{
while ( head )
{
delete std::exchange( head, head->next );
}
}
void add_data( int d )
{
node **current = amp;head;
while ( *current ) current = amp;( *current )->next;
*current = new node { d, nullptr };
}
friend std::ostream amp; operator <<( std::ostream amp;os, const LinkedList amp;list )
{
for ( const node *current = list.head; current != nullptr; current = current->next )
{
os << current->data << " -> ";
}
return os << "null";
}
void recursive_reverse()
{
repeat( nullptr, head );
}
};
int main()
{
LinkedList list; // create a linked list object
const int N = 10;
for ( int i = 0; i < N; i )
{
list.add_data( i );
}
std::cout << list << 'n';
list.recursive_reverse();
std::cout << list << 'n';
list.recursive_reverse();
std::cout << list << 'n';
return 0;
}
Выходные данные программы такие же, как показано выше
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
Комментарии:
1. Это
recursive_reverse
довольно аккуратно!
Ответ №2:
Ваш код излишен, вам не нужно так много локальных переменных и аргументов функции для реализации рекурсии. Также в этой части:
if (next_node != NULL)
{
next_node = temp_ptr->next;
repeat(prev_node,temp_ptr,next_node);
}
head = temp_ptr;
кажется, вы присваиваете nullptr
своему head
when next_node == nullptr
.
Исправленная версия:
void repeat(node* previous, node* current)
{
node* next_node = current->next;
current->next = previous;
if (next_node == nullptr)
{
head = current;
return;
}
repeat(current, next_node);
}
void recursive_reverse()
{
repeat(nullptr, head);
}
Кроме того, у вас есть утечки памяти, потому что вы никогда не delete
выделяли память. Реализуйте деструктор и пометьте свои конструкторы копирования / назначения как deleted
явно (или вы также можете реализовать их, если у вас есть время):
LinkedLists(const LinkedListsamp; other) = delete;
чтобы избежать дальнейших ошибок и соблюдать правило трех (или пяти, или нуля).