Связанный список возвращает значения в неправильном порядке - PullRequest
2 голосов
/ 05 октября 2011

Я пытался написать LinkedList, однако, когда я зацикливаю все элементы в списке, элементы выдаются в другом порядке, чем они вставляются.

Скажем, я вставляю ихвот так:

slist_insert(list, "red");
slist_insert(list, "green");
slist_insert(list, "blue");
slist_insert(list, "yellow");
slist_insert(list, "pink");
slist_insert(list, "purple");
slist_insert(list, "beige");
slist_insert(list, "white");
slist_insert(list, "black");
slist_insert(list, "brown");
slist_insert(list, "fuchsia");
slist_insert(list, "aqua");
slist_insert(list, "magenta");

Но в цикле это дает вместо этого:

green
magenta
aqua
fuchsia
brown
black
white
beige
purple
pink
yellow
blue
red

Я не делал этого раньше, заметьте, так что есть очень хороший шанс, что этот кодизобилует элементарными ошибками, связанными с алгоритмами связанных списков: http://codepad.org/Sl0WVeos

Код, как таковой, работает нормально, но меня беспокоит несколько вещей:

  • Получен неправильный порядок (как объяснено выше)
  • Приходится использовать макросы (есть ли лучший способ сделать это?)
  • Даже после вызова slist_destroy все еще происходит утечка памяти,и я не могу понять, откуда он приходит

Помощь действительно ценится!

Ответы [ 3 ]

6 голосов
/ 05 октября 2011

о неправильном заказе товара

Ваша логика для slist_impl_insertl() неверна.

давайте следовать вашему коду:

stringlist_t* slist_impl_insertl(stringlist_t* list, const char* str, unsigned int len)
{
    stringlist_t* newnode;
    if(list == NULL) // if the list is empty
    {
        newnode = slist_createl(str, len); // create a new item
        list = newnode;                    // insert the new item at the start of the list
        return list;
    }
    else // if the list is not empty
    {
        if(list->next == NULL) // if it contains only one item
        {
            list = slist_insertb(list, str, len); // insert a new item at the front of the list
            return list;
        }
        else // if it contains more than one item
        {
            newnode = slist_createl(str, len);                // create a new node
            newnode->next = (struct stringlist_t*)list->next; // insert the new node just after the first item !?!.
            list->next = (struct stringlist_t*)newnode;
            return list;
        }
    }
    return list; /* not reached */
}

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

тривиальное исправление - всегда вставлять новый узел в начало списка, тогда элементы будут возвращаться в обратном порядке. или вы можете перебирать список до конца (list->next == NULL) и вставлять новый элемент после этого последнего элемента:

stringlist_t* slist_impl_insertl(stringlist_t* list, const char* str, unsigned int len)
{
    stringlist_t *iter;
    if(list == NULL)
    {
        list = slist_createl(str, len);
    }
    else
    {
        // find the last ist item
        iter = list; 
        while(iter->next!=NULL)
            iter = iter->next;
        // insert the new item at the end of the list
        iter->next = slist_createl(str,len);
    }
    return list;
}

об использовании макросов

если список пуст (list == NULL), процедура вставки изменит список, чтобы сделать его первым элементом. макрос заботится о переназначении измененного списка. если вы не хотите использовать макросы, тогда вы должны передать аргумент списка в качестве указателя, чтобы вы могли изменить его непосредственно в процедуре вставки.

(парень, который написал код в первую очередь, сделал его таким образом, чтобы он мог вставить элемент в любое место в середине списка без необходимости писать специальные процедуры для этого)

вот вариант реализации slist_insert() без использования макроса:

void slist_insert(stringlist_t** list, const char* str)
{
    *list = slist_impl_insertl(*list, str);
}

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

slist_insert(&list, "red"); // note the use of '&'

об утечке памяти

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

void slist_destroy(stringlist_t* list)
{
    stringlist_t *temp;
    while(list != NULL)
    {
        // free the data contained in the current list item
        free(list->data);
        // save the pointer to the next item
        temp = slist_next(list);
        // free the current item
        free(list);
        // continue with the next item
        list = temp;
    }
}
2 голосов
/ 08 октября 2011

Здесь я даю вам связанный список, созданный мной, который, возможно, может помочь вам понять эту структуру данных (я поставил функции для вставки и удаления узлов)

void insert(LISTNODEPTR* sPtr, char item)
{
    LISTNODEPTR previousPtr,currentPtr,newPtr;

    newPtr = malloc(sizeof(LISTNODE));

    if(newPtr != NULL)
        {
        newPtr->value = item;
        newPtr->nextPtr = NULL;

        currentPtr = *sPtr;
        previousPtr = NULL;

        while(currentPtr != NULL && currentPtr->value < item)
        {
            previousPtr = currentPtr;
            currentPtr = currentPtr->nextPtr;
        }

        if(previousPtr == NULL)
        {
            newPtr->nextPtr = *sPtr;
            *sPtr = newPtr;
        }
        else
        {
            previousPtr->nextPtr = newPtr;
            newPtr->nextPtr = currentPtr;
        }
    }
    else
    {
        printf("No memory available\n");
    }
}

void delete(LISTNODEPTR* sPtr,char item)
{
    LISTNODEPTR currentPtr,previousPtr,tempPtr;

    if((*sPtr)->value == item)
    {
        tempPtr = *sPtr;
        *sPtr = (*sPtr)->nextPtr;
        free(tempPtr);
    }
    else
    {
        previousPtr = NULL;
        currentPtr = *sPtr;

        while(currentPtr != NULL && currentPtr->value != item)
        {
            previousPtr = currentPtr;
            currentPtr = currentPtr->nextPtr;
        }
        tempPtr = currentPtr;
        previousPtr->nextPtr = currentPtr->nextPtr;
        free(tempPtr);
    }
}
1 голос
/ 05 октября 2011
newnode = slist_createl(str, len);
newnode->next = (struct stringlist_t*)list->next;//1)
list->next = (struct stringlist_t*)newnode;//2)
return list;

список состояния, когда: зеленый-> красный-> NULL

1) newnode-> красный-> NULL

2) зеленый -> newnode-> красный-> NULL

newnode всегда вставляется между зеленым (зеленый после) и красным.

и,

slist_destroy

slist_destroy освобождает буфер строки, не выпускать узел!

...