Бесконечный цикл при прохождении через связанный список [закрыт]

Удаление и просмотр всей очереди вызывает проблему - я предполагаю, что это случай бесконечного цикла для обхода. Он прекрасно работает для добавления элемента, но выполнение останавливается сразу после выбора пользователем 2 или 3.

Я просто добавляю несколько строк случайного текста перед кодом, потому что стек не позволяет такой большой код с небольшим объяснением.

Sed ut perspiciatis undemnis iste natus error sit vulptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam volptatem quia volptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos quiratione volptatem sequinescunt. Neque porro Quisquam Est, Qui Dolorem Ipsum Quia Dolor Sit Amet, Concetetur, Adipisci Velit, Sed Quia Non Numquam Eius Modi Tempora Incidunt U Labore и Dolore Magnam Аликвам Quaerat Volptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit labouriosam, nisi ut aliquid ex ea Goodsi Conquatur? Quis autem vel eum iure preshenderit qui in eus vulptate velit esse quam nihil molestiae coequatur, vel illum qui dolorem eum fugiat quo volptas nulla pariatur?

#include<iostream>
using namespace std;
class linkedlist
{
    struct node
    {
        int number;
        node *next;
    }*HEAD;
public:

    void addelement(int num);
    void exitelement();
    void displayqueue();

};

void linkedlist::addelement(int num)
{
    node *t;
    t= new node;
    t -> number=num;
    t -> next=HEAD;
    HEAD=t;
    cout<<num<<"has successfully been added to the queue /n";
}

void linkedlist::exitelement()
{
    node *t;
    t=HEAD;
    while(t -> next !=NULL)
    {
        t=t -> next;
    }
    cout<<t->number;
    delete t;
}

void linkedlist::displayqueue()
{
    node *t;
    t=HEAD;
    while(t->next !=NULL)
    {
        cout<<t -> number<<"	";
        t=t->next;
    }
    cout<<" 
 that's the end of the list 
";
}

int main()
{
    int lol;
    linkedlist m;
    int rpt=1;
    while(rpt==1)
    {
        int c;
        cout<<"
 please select 1 to add an element, 2 to remove an element from the queue and 3 to display the entire queue 
";
        cin>>c;
        cout<<"/n";
        if(c==1)
        {
            cout<<"enter the number: ";
            cin>>lol;
            m.addelement(lol);
        }
        else if(c==2)
        {
            m.exitelement();
        }
        else if(c==3)
        {
            m.displayqueue();
        }
        else
        {
            cout<<"you have entered an invalid input. Sorry 
";
        }
        cout<<"
 Do you wish to start the queue again??? 
";
        cin>>rpt;
    }

}

Всего 2 ответа


У вас есть ряд проблем, связанных с тем, как вы linkedlist::exitelement() свой список в обоих linkedlist::exitelement() и связанных linkedlist::displayqueue() . Но прежде чем мы рассмотрим особенности, когда вы используете стрелочный оператор ( -> ), с обеих сторон не остается места . То есть вы бы сделали t->next = HEAD; НЕ t -> next = HEAD; (пробел по обе стороны от '=' является необязательным - но рекомендуется для удобства чтения). Вы также должны убедиться, что указатель HEAD объявленный в вашем классе, инициализирован в nullptr либо прямой инициализацией, либо предоставлением конструктора. На данный момент это будет делать:

class linkedlist
{
    struct node {
        int number;
        node *next;
    } *HEAD = nullptr;
    ...

Ваша linkedlist::addelement(int num) в порядке, она использует метод под названием forward-chaining для добавления узлов в начало списка при каждом добавлении. Единственное предостережение - ваш список заканчивается в обратном порядке ввода (что требуется в некоторых случаях, но не в других)

Ваш связанный linkedlist::exitelement() не работает, так как он не предоставляет метод установки указателя prev->next на nullptr перед удалением текущего узла. Простой способ справиться с этим - использовать адрес текущего и последнего узлов, а также указатель на текущий узел, когда вы выполняете итерацию до конца списка. См. Линус на Понимают Указатели . При подходе к итерации списка таким образом вы избавляетесь от необходимости проверять какие-либо особые условия.

(здесь проверка, if (HEAD == nullptr) необходима только для печати сообщения "(queue-empty)" в случае, если вы пытаетесь удалить узел из пустого списка и предотвратить разыменование nullptr для вашего cout числа в узел, который будет удален в этом случае).

Преимущество использования адреса указателя в том, что он не изменяется в памяти. (вы можете изменить или удалить то, что хранится там, но сам адрес не меняется) Отслеживая адреса с помощью указателя node ** (вместо самого указателя node* ), вы просто выполняете итерацию до конца вашего список с указателем, (это будет nullptr в конце), а затем delete то, что хранится по последнему адресу, прежде чем установить память в этом месте на nullptr . (вы ДОЛЖНЫ установить указатель ->next для узла в вашем списке перед тем, который вы удаляете, на nullptr когда вы удаляете последний узел, или вы не будете знать, где находится конец вашего списка после delete ). Например:

void linkedlist::exitelement()
{
    if (HEAD == nullptr) {
        cout << "(queue-empty)
";
        return;
    }

    node **ppt = &HEAD,     /* address of HEAD */
        **last = ppt;       /* address of last (initialized to HEAD) */
    node *pt = HEAD;        /* pointer to node */

    while (pt != nullptr) { /* loop over each node */
        last = ppt;         /* set last to address of previous */
        ppt = &pt->next;    /* set address of current to next */
        pt = pt->next;      /* set current to next */
    }

    cout << (*last)->number << '
'

    delete *last;           /* delete node at address of last */
    *last = NULL;           /* set memory at last address to nullptr */
}

( примечание: выше итерация по списку происходит, в while (pt != nullptr) нет, в while (pt->next != nullptr) - то же самое верно для вашего linkedlist::displayqueue() ниже)

Нет необходимости отслеживать адреса указателей при итерации списка, чтобы просто печатать значения в каждом узле. Вы просто используете простой указатель и проверяете, является ли узел nullptr , и, если это не так, выведите number узла, продвиньте указатель и повторите, например,

void linkedlist::displayqueue()
{
    if (!HEAD) {
        cout << "queue-empty
";
        return;
    }

    node *t = HEAD;
    while (t != nullptr) {
        cout << t->number << "	";
        t = t->next;
    }
    cout << "
(end of the list)
";
}

Остальная часть ваших проблем - ваша неспособность проверить КАЖДЫЙ ввод и невозможность очистить стандартный stdin в случае сбоя ввода. Обязательно, как минимум, вы оба. Вывод вашей программы сделал почти невозможным выполнение того, что просил ваш код. Ваше меню в одной строке было трудно читать. Символ новой строки представлен как ' ' NOT '/n' (это не что иное, как косая черта и буквальный символ 'n' ).

Очистка вашего меню и проблема новой строки , ваше меню теперь отображается как:

please select:

  1 to add an element
  2 to remove an element
  3 to display the entire queue

choice:

Вместо того, чтобы в единственной беспорядочной линии. Чтобы поставить улучшения в целом, вы можете сделать что-то похожее на следующее:

int main (void)
{
    int lol;
    linkedlist m {};
    int rpt = 1;

    m.addelement(10);    /* queue elements 10, 20, 30 added for testing to avoid */
    m.addelement(20);    /* having to navigate the menu and input multiple times */
    m.addelement(30);    /* (remove when you are done testing) */

    while (rpt == 1)
    {
        int c;
        cout << "
please select:

"
                "  1 to add an element
"
                "  2 to remove an element
"
                "  3 to display the entire queue

"
                "choice: ";

        if (!(cin >> c)) {  /* validate EVERY input */
            cerr << "error: invalid integer - choice.
";
            std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '
');
            continue;
        }

        if (c == 1) {
            cout << "enter the number: ";
            if (cin >> lol)
                m.addelement(lol);
            else {
                cerr << "error: invalid integer input.
";
                std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '
');
            }
        }
        else if (c == 2) {
            m.exitelement();
        }
        else if (c == 3) {
            m.displayqueue();
        }
        else {
            cout << "you have entered an invalid input. Sorry 
";
        }
        cout << "
Do you wish to start the queue again? (0-no, 1-yes): ";
        if (!(cin >> rpt)) {
            cerr << "error: invalid integer input.
";
            std::cin.ignore (std::numeric_limits<std::streamsize>::max(), '
');
        }
    }
}

Проверьте свое меню и список отдельно

Это означает, что громоздкая природа вашего меню затрудняет тестирование операций со списком из-за необходимости многократно вводить выбор меню и продолжать ввод, чтобы добавлять и удалять узлы. Вместо этого подумайте, как можно упростить тестирование ваших алгоритмов (отдельно от вашего пользовательского интерфейса). Например, здесь вы можете добавить простую is_empty() член is_empty() для отчета, когда ваш список пуст. Это позволит вам многократно вызывать m.exitelement(); пока он не пуст, не вводя ничего.

Вы можете добавить is_empty() член is_empty() так же просто, как:

    ...
  public:

    void addelement(int num);
    void exitelement();
    void displayqueue();
    bool is_empty() { return HEAD == nullptr; }
    ...

Теперь, чтобы протестировать ваш список, удалите ваше меню полностью, и вы можете уменьшить свой список тестового кода до следующего:

int main (void)
{
    linkedlist m {};

    for (int i = 1; i < 40; i++)
        m.addelement(i);

    m.displayqueue();

    do
        m.exitelement();
    while (!m.is_empty());
    m.exitelement();        /* just to validate it works as intended */
}

(делает тестирование вашего списка намного проще :)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.


В вашем методе addelement () всякий раз, когда вы добавляете новый узел, вы всегда помещаете

t -> next=HEAD;

Следовательно, для всех ваших узлов следующий никогда не будет нулевым . Следовательно, функции display и exiteElement всегда будут попадать в бесконечный цикл.

Кроме этого, у всей реализации связного списка есть некоторые другие проблемы, которые необходимо исправить, если вы хотите, чтобы какая-либо функциональность работала правильно. Вы можете взглянуть на эту ссылку для лучшего понимания.


Есть идеи?

10000