Замена порядка узлов в списке обратным.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Есть неотсортированный связный список целых чисел. Надо заменить порядок узлов обратным. Создавать и удалять узлы нельзя.

Если бы можно было создавать узлы, то согласно правилу стека (FILO), узлы нового списка после поузельного копирования были бы упорядочены как надо.

Долго ломал голову и пришел к следующему алгоритму.
Вид узла списка:
struct Node
{
int value;
Node *link;
};
typedef Node* NodePtr;

Список:
NodePtr head;
...

Необходимы следующие указатели на список:
- start = head; - первый элемент списка
- before_end = head; - предпоследний элемент списка
- end = NULL; - конец списка
- iter - для прохождения через список циклом for

Переменная для пузырьковой сортировки:
int temp;

Код: Выделить всё

for(iter = head; iter != end; iter = iter->link) //проходим по всему списку
{
before_end = before_end->link;
}

//По окончанию прохождения по списку, before_end, теоретически, должен указывать на предпоследний элемент
//но почему-то он проскакивает дальше

//Меняем значения первого и последнего элементов списка местами
temp = start->value;
start->value = iter->value;
iter->value = temp;

start = start->link; //следующий не упорядоченный элемент
end = before_end; //предыдущий неупорядоченный элемент
before_end = start; //поиск предпоследнего элемента
Повторять это до тех пор, пока список не будет упорядочен.
Условия окончания упорядочивания приблизительно представляю:
- прогонять цикл, пока после последних 3 строк before_end == end.

Но проверка алгоритма срывается после первого цикла for. Если список представляет из себя: 1 3 2 5 4, то во время цикла before_end указывает на 3 2 5 4 и выскакивает за пределы списка, завершая программу ошибкой.

Что не так работает? Корректен ли алгоритм или есть проще варианты?
Albor
Сообщения: 491
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

Dragon писал(а):Есть неотсортированный связный список целых чисел. Надо заменить порядок узлов обратным. Создавать и удалять узлы нельзя.
Разве в задании говорится о сортировке? По-моему, всё что нужно - это хвост списка сделать головой и изменить ссылки так, чтобы список остался списком.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Я в списках еще не очень свободно плаваю, зависаю, но разве в связном списке не только в одну сторону идти можно?
Или имеется в виду создать новый список в который перенести данные по принципу стека (FILO):

Код: Выделить всё

void insert_node(NodePtr& head, int the_number)
{
    NodePtr temp;
    temp = new Node;
    temp->value = the_number;
    temp->link = head;
    head = temp;
}

void reverse(NodePtr& head)
{
    NodePtr iter, temp;

    for(iter=head; iter != NULL; iter = iter->link)
    {
        insert_node(temp, iter->value);
    }

    head = temp;
}
Хотя если подумать, то создание буферного списка - не является "созданием/удалением узлов". Или тоже не то?
Albor
Сообщения: 491
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

С чего ты взял что нужно создавать что-то новое. Вссё что нужно - это переписать в узлах имеющегося списка значение указателей
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Эта операция называется реверсом списка. Для выполнения её достаточно пройтись по списку однократно (линейная сложность). Перевыделения памяти не требуются. Для алгоритма нужны всего две вспомогательные переменные.

На каждой итерации в отдельной переменной должен быть сохранён адрес "следующего" элемента, затем ссылка должна быть перенаправлена в предыдущий элемент (взятый из второй временной переменной), затем следует перезаписать переменную, хранящую пред. элемент адресом текущего элемента, затем мы должны перейти к следующем элементу и выполнить ту же самую последовательность действий снова.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Хм, надо будет попробовать сделать так (на первый взгляд замудрено сильно).
Значит мой вариант перепаковки списка не годится вообще?

Хотя в стандартной библиотеке видел класс List в котором есть функция reverse. Правда как она работает не смотрел, но похоже именно так, как вы и пишете (создание буферного списка - лишняя нагрузка на память).
licenok
Сообщения: 14
Зарегистрирован: 25 авг 2010, 17:08

Код: Выделить всё

//дорабатываем немножко структуру
struct Node
{
int value;
Node *linkPrev; // предыдущий элемент
Node *linkNext; // последующий элемент
// NULL - если нет Node (это встречается в голове и хвосте)
};

void MovPrev(Node* nd) // перетаскивание элемента на 1 позицию в направлении хвоста
{
  if (nd == head) head = nd->linkPrev;
  if (nd->linkNext) nd->linkNext->linkPrev = nd->linkPrev;
  if (nd->linkPrev) nd->linkPrev->linkNext = nd->linkNext;
  Node* prev = NULL;
  if (nd->linkPrev) prev = nd->linkPrev->linkPrev;
  if (nd->linkPrev) nd->linkPrev->linkPrev = nd;
  nd->linkNext = nd->linkPrev;
  nd->linkPrev = prev;
}

int count = количество всех элементов списка ;


for (int i=count-1 ; i>0; i--)
{
  Node* nd = head; // берём голову
  for (int j=0; j<i; j++) MovPrev(nd); // и тащим её в хвост
}

// собственно всё ..
Аватара пользователя
rrrFer
Сообщения: 237
Зарегистрирован: 07 сен 2008, 14:15
Контактная информация:

не по теме:
хотите перерешать все задачи? :)
Приглашаю на свой блог о программировании: pro-prof.com
licenok
Сообщения: 14
Зарегистрирован: 25 авг 2010, 17:08

вдруг кому-нибудь пригодится ?
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Между прочим очень похвально :) Только таги code используй, плиз.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить