Изображения часто помогают при работе со связанными списками. Вот начальный список.
head
|
V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Теперь мы будем вызывать ll1->swapNode(&ll1->head, &ll1->head->next->next->next)
, что добавляет к изображению две переменные.
start
|
V
head end
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Далее идет вызов swap(*start, *end)
, который меняет указатель головы и указатель next
в узле с данными 1
. Таким образом, указатель головы будет указывать на узел 3
, а узел 1
будет указывать на узел 6
.
start
|
V
end head
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | | 3 | NULL |
------------ ------------ ------------ ------------
^ |
| |
L---------------------------------------
Наконец, вызов swap(((*start)->next), (*end)->next)
, который меняет next
указатели в узлах, чьи данные 3
и 6
. Поэтому узел 3
будет указывать на узел 2
, а указатель узла 6
станет нулевым.
start
|
V
head
|
V
------------
| 3 | next |
------------ end
| |
V V
------------ ------------ ------------
| 6 | NULL | | 2 | next | -> | 1 | next |
------------ ------------ ------------
^ |
| |
L---------------------------------------
Ура! Узлы поменялись местами.
Теперь посмотрите на вызов ll2->swapNode(&ll2->head, &tail);
, который добавляет три переменные к исходному изображению.
start end
| |
V V
head tail
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Далее идет вызов swap(*start, *end)
, который меняет указатель головы и tail
(не указатель хвоста списка, а локальную переменную main()
).
end start
| |
V V
tail head
| |
V V
------------ ------------ ------------ ------------
| 6 | next | -> | 2 | next | -> | 1 | next | -> | 3 | NULL |
------------ ------------ ------------ ------------
Уже есть заметное отличие от первого случая в том, что узлы из списка без изменений. Хм ... ну, давайте продолжим и назовем swap(((*start)->next), (*end)->next)
, который поменяет указатели next
в узлах, данные которых 3
и 6
. Таким образом, узел 3
будет указывать на узел 2
, а указатель 6
станет нулевым. Это то, что произошло в первом случае, так сработает?
end start
| |
V V
tail head
| |
V V
------------ ------------ ------------ ------------
| 6 | NULL | | 2 | next | -> | 1 | next | -> | 3 | next |
------------ ------------ ------------ ------------
^ |
| |
L---------------------------------------
ПРОБЛЕМА! Мы остались с циклом! Что случилось с шагом, когда узел 1
был изменен, чтобы указывать на узел 6
? Правильно, вместо этого мы изменили tail
, и в результате получилась катастрофа.
Я бы порекомендовал три основных момента, чтобы переместить ваш код на более надежную основу. (То есть, пока не пытайтесь исправить текущую ошибку. Сначала сделайте это, поскольку они изменят игровое поле.)
- Избавьтесь от
#include<bits/stdc++.h>
; вместо этого включите заголовки, которые вам нужны. Для этого примера вам нужно просто #include <cstdio>
. - Сделать
node
private
подробность реализации linkedList
. Надежность имеет тенденцию увеличиваться вместе с герметизацией. Если никто не знает структуры данных, используемые linkedList
, вы ограничиваете то, что может делать другой код. Имея меньше возможностей для учета, легче убедиться, что вы учли все из них. - Не объявляйте поле (
linkedList::tail
), которое никогда не используется. Кто-то захочет использовать это поле в какой-то момент, не осознавая, что оно содержит мусор.
(Существуют и другие улучшения, которые, на мой взгляд, являются наиболее важными.)