Можем ли мы перевернуть элементы в одноцикловом связанном списке, используя только два указателя? Возможно ли это, эффективно и какова временная сложность?

Мне нужно четко знать только одну вещь, поскольку я пробовал обращение одинарного циклического связанного списка с использованием языка C. В этом я не могу найти правильный путь и понятия не имею об этом. Может ли кто-нибудь помочь мне правильно и кратко рассказать о временной сложности и, наконец, нельзя ли повернуть вспять, не объявляя NULL?

struct node *Reversescll(struct node *head) {
    if (head == NULL) {
        head->next = head;
        return head;
    }

    struct node *back = NULL;
    struct node *c = head;
    struct node *next = NULL;

    do {
        next = c->next;
        c->next = back;
        back = c;
        c = next;
    } while (c != head);

    head->next = back;
    head = back;
    return head;
}

Вы хотели что-то поместить в спойлер?

Barmar 15.04.2024 16:56

Да, да, в некоторой степени (поскольку связанные списки по своей сути неэффективны), O(n).

n. m. could be an AI 15.04.2024 16:56
if (head==NULL) { head->next==head; хорошо не закончится.
Retired Ninja 15.04.2024 16:57

Тебе не хватает ; после while(c!=head)

Barmar 15.04.2024 16:58

См. medium.com/@acharya.piyush224/… для ознакомления с рекурсивным алгоритмом обратного списка. он использует только один указатель на итерацию.

Barmar 15.04.2024 17:00

Пожалуйста, научитесь правильно форматировать код. Это очень важно. С нечитаемым кодом сложно работать, особенно новичкам. Я сделал это для тебя.

Jabberwocky 15.04.2024 18:36

Действительно ли актуальна часть заголовка «использование только двух указателей»? Я собирался спросить, как вы считаете, но потом увидел, что код в вопросе использует более двух в любом способе счета.

John Bollinger 15.04.2024 19:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
102
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш подход почти правильный, но у вас проблема с пустым списком: вам нужно просто вернуть head или NULL, а не разыменовывать head->next, когда head равно NULL.

Вот исправленная версия:

struct node *Reversescll(struct node *head) {
    if (head == NULL || head->next == head)
        return head;

    struct node *back = NULL;
    struct node *c = head;

    do {
        struct node *next = c->next;
        c->next = back;
        back = c;
        c = next;
    } while (c != head);

    head->next = back;
    return head;
}

Обратите внимание, что функция всегда возвращает свой аргумент.

Теперь, чтобы полностью ответить на ваш вопрос:

Можем ли мы перевернуть элементы в одноцикловом связанном списке, используя только два указателя?

Да, это можно сделать, используя всего два дополнительных указателя в дополнение к аргументу head (в приведенном выше коде используются 3 дополнительных указателя):

struct node *Reversescll(struct node *head) {
    if (head == NULL)
        return head;

    struct node *back = NULL;
    while (head->next) {
        struct node *next = head->next;
        head->next = back;
        back = head;
        head = next;
    }
    head->next = back;
    return head;
}

Эффективно ли это и какова временная сложность?

Он эффективен, а сложность линейна, O(n). Это оптимально, поскольку необходимо менять каждый узел.

Порядок слов в ОП странный, но я думаю, что они говорят, что у них есть круговой (односвязный) список. Если да, то ваш цикл while (head->next) не завершится.

John Bollinger 15.04.2024 20:58

@JohnBollinger: Я позволю себе не согласиться, поскольку указатель next начального узла будет установлен на начальное значение back, а именно на нулевой указатель, цикл остановится, когда он снова достигнет этого узла, если список действительно является циклическим. Если список не является циклическим, классический код в первой версии выше приведет к сбою при достижении нулевого указателя, тогда как этот код остановится на последнем узле, свяжет его с предыдущим и вернет его. IOW код переворачивает список независимо от того, является ли он циклическим или нет. Первичный тест head->next == head даже не нужен.

chqrlie 15.04.2024 21:15

Мои извинения. Кажется, я только что выскочил из альтернативной вселенной, где ты этого не делал. Я знаю это, потому что искал что-то в этом роде, прежде чем комментировать. +1 тогда. Не обращайте на меня внимания.

John Bollinger 15.04.2024 21:24

Другие вопросы по теме