【问题标题】:Trying to swap 2 adjacent nodes in a linked list in plain C without double pointers尝试在没有双指针的普通 C 中交换链表中的 2 个相邻节点
【发布时间】:2021-12-27 22:47:17
【问题描述】:

[ 哇 - 有人给了我和我的问题的负面观点] [你至少可以评论一下你为什么不喜欢我的问题]

我被困住了。 我记得在 C++ 中做过类似的事情,但由于某种原因,我无法让它在普通 C 中工作。

我正在尝试交换单链表中的 2 个节点。

起始列表填充为[9,8,7,5,3,2],我正在尝试对其进行冒泡排序,一次2个节点到[2,3,5,7,8,9]的最终列表

第一次迭代(交换)使用头部查找。列表完美返回 [8,9,7,5,3,2] ...但是在第二次迭代中,我松开了 7 并得到了 WTF 的 [8,9,5,3,2],我尝试稍微更改代码但我失去了希望。

真的有人能找出我做错了什么吗?请不要使用双指针...如果只能使用双指针...为什么以及如何?因为我不知道什么是双指针?

到目前为止,这是我的程序:

/*
    ___ENTER TITLE HERE___
    Author : Patrick Miron
    Date : Oct 20, 2021
*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct listNode
{
    int data;
    struct listNode *next;
} listNode;

typedef struct list
{
    struct listNode *head;
    struct listNode *tail;
} list;

int isEmpty( const list *l) 
{
    return (l == NULL);
}

void printList(list *ptrToList, char *title)
{
    int counter = 0; //Counter to count the listItem printed so we can add a carriage return at each 5th element.
    printf("%s\n", title);
    listNode *ptrToCurrentItem = ptrToList->head;
    while (ptrToCurrentItem != NULL)
    {
        counter++;
        printf("%d", ptrToCurrentItem->data);
        if (counter % 5 != 0)
        {
            printf(" : ");
        }
        else
        {
            printf("\n");
        }
        ptrToCurrentItem = ptrToCurrentItem->next;
    }
}

list *createListWithHeadData(int data)
{
    list *ptrList = malloc((sizeof(ptrList)));
    listNode *ptrNewNode = malloc(sizeof(listNode));
    ptrNewNode->data = data;
    ptrList->head = ptrNewNode;
    ptrList->tail = ptrNewNode;
    return ptrList;
}

void addToFrontList(list *ptrList, listNode *ptrListNode)
{
    listNode *tempPtr = ptrList->head;
    ptrList->head = ptrListNode;
    ptrListNode->next = tempPtr;
}

list *arrayToList(int data[], int size)
{
    list *ptrToNewList = createListWithHeadData(data[0]);
    for (int i = 1; i < size; i++)
    {
        listNode *ptrToNewListNode = malloc(sizeof(listNode));
        ptrToNewListNode->data = data[i];
        addToFrontList(ptrToNewList, ptrToNewListNode);
    }
    return ptrToNewList;
}

int count(listNode *ptrToHead)
{
    if (ptrToHead == NULL)
    {
        return 0;
    }
    else
    {
        return (1 + count(ptrToHead->next));
    }
}

void concat(listNode *head1, listNode *head2)
{
    assert(head1 != NULL);
    if (head1->next == NULL)
    {
        head1->next = head2;
    }
    else
    {
        concat(head1->next, head2);
    }
}

void insert(
    listNode *p1, // first element
    listNode *p2, // second element
    listNode *q) // new element to insert between first and second element
    {
        assert(p1->next == p2);
        p1->next = q;
        q->next = p2;
    }

void delete(listNode *listNode)
{
    assert(listNode != NULL);
    listNode = NULL;
}

void deleteList(list *list)
{
    if (list->head != NULL)
    {
        list->head = list->head->next;
        deleteList(list);
    }
}

void swapListNodeWithNext(listNode *ptrToListNode1)
{
    //Swap items
    listNode *ptrTempNode1 = ptrToListNode1->next;
    listNode *ptrTempNode2 = ptrToListNode1->next->next;

    //Set the next node from temp1 (ptrToListNode->next->next) to itself
    //Could be written as ptrToListNode->next->next = ptrToListNode
    ptrTempNode1->next = ptrToListNode1;
    ptrToListNode1->next = ptrTempNode2;
    ptrToListNode1 = ptrTempNode1;

    ptrTempNode1 = NULL;
    ptrTempNode2 = NULL;
}

void sortList(list *ptrToListToSort)
{
    if (ptrToListToSort->head == NULL)
    {
        return;
    }
    listNode *ptrToCurrentItem = ptrToListToSort->head;
    listNode *ptrToLastUnsortedItem = ptrToListToSort->tail;

    while (ptrToLastUnsortedItem != ptrToListToSort->head)
    {
        ptrToCurrentItem = ptrToListToSort->head;
        while(ptrToCurrentItem->next != NULL)
        {
            if (ptrToCurrentItem->data > ptrToCurrentItem->next->data)
            {
                listNode *ptrToHead = ptrToListToSort->head;
                if (ptrToCurrentItem == ptrToListToSort->head)
                {
                    ptrToHead = ptrToCurrentItem->next;
                }
                //Swap items
                swapListNodeWithNext(ptrToCurrentItem);
                ptrToListToSort->head = ptrToHead;
            } 
            else 
            {
                ptrToCurrentItem = ptrToCurrentItem->next;
            }
        }
        ptrToLastUnsortedItem = ptrToCurrentItem;
    }
}

int main(void)
{  
    printf("\n");

    list listOfInt;
    int data[6] = { 2, 3, 5, 7, 8, 9 };
    list *ptrToNewList = arrayToList(data, 6);
    printList(ptrToNewList, "Array to Element List");

    sortList(ptrToNewList);

    printList(ptrToNewList, "Sorted List");

    printf("\n");
    printf("...End of line...\n");
    printf("\n");
    return 0;
}

【问题讨论】:

  • 那是很多代码。如果您的编译器支持它,我建议您使用 AddressSanitizer。如果您使用g++clang++,请尝试使用-g -fsanitize=address,undefined 进行编译。它会在您运行程序时向您报告like this,以便您查看程序何时出现异常。
  • list *ptrList = malloc((sizeof(ptrList))); 显然是错误的; ptrList 是一个指向 list 结构的指针,但您只分配了足够的内存来存储一个指向列表结构的 指针。理想情况下,该行应为 list *ptrList = malloc(sizeof *ptrList); 。并保持一致;下一行按 type 的大小分配,而这一行(不正确)按 var 的大小分配。
  • 谢谢@WhozCraig,我纠正了我的错字。但这不是我的问题。
  • 谢谢@TedLyngmo,我从您发送给我的链接的地址清理程序中得到的只是:==1==错误:地址清理程序:未知地址上的 SEGV(pc 0x00000040133f bp 0x7ffda71a6be0 sp 0x7ffda71a6bb0 T0)= =1==该信号是由 READ 内存访问引起的。 ==1==提示:此故障是由取消引用高值地址引起的(请参阅下面的寄存器值)。拆卸提供的电脑以了解使用了哪个寄存器。我知道这在我的 Mac 上不是问题,内存检查出来了,我也发现了我的问题,当我在几分钟内回答我自己的问题时,你会看到。
  • @DragonAngeltheOriginal 在线 AddressSanitizer 还显示了一个以 ptrList-&gt;tail = ptrNewNode; 结尾的调用链和一个堆缓冲区溢出。 “我知道这在我的 Mac 上不是问题”——这只是未定义行为的标志。

标签: c linked-list swap singly-linked-list


【解决方案1】:

我发现了几个问题,但主要问题是我没有更改我以前的 ptr 的下一个 (ptrPreviousItem->next) 以在交换后指向正确的项目。我只是将指针更改为指向当前项目的指针,这只是通过我的迭代推进的头部的副本,而上一个->下一个仍然指向原始项目。

到底为什么它对我的第一次迭代有效?可能是因为我不会更新最后一项,因为它的顺序正确,之后就不会更新。

我已经包含了带有指针的单链表冒泡排序的正确版本。它有效,我希望它可以帮助下一个人。相信我很多关于这个主题的教程几乎没有让代码比“a”和“b”这样的标识符更具可读性......

哦,@chqrlie,我不明白你在我之前的问题中所做的一些更正,其中一些是完全不必要的,当我将代码放在括号中并且指针中的 * 可以放在你想要的地方。 int* likeThis; int * likeThis;或 int *likeThis。但是我通常会像您一样使用 asterix 对标识符进行格式化。当我指的是取消引用的值时,我在它们之间使用了一个空格。干杯!

/*
    Singly Linked List Bubble Sort with Pointers
    Author : Patrick Miron
    Date : Nov 17, 2021
*/

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef struct listNode
{
    int data;
    struct listNode * next;
} listNode;

typedef struct list
{
    struct listNode *head;
    struct listNode *tail;
} list;

int isEmpty( const list *l) 
{
    return (l == NULL);
}

void printList(list *ptrToList, char *title)
{
    int counter = 0; //Counter to count the listItem printed so we can add a carriage return at each 5th element.
    printf("%s\n", title);
    listNode *ptrToCurrentItem = ptrToList->head;
    while (ptrToCurrentItem != NULL)
    {
        counter++;
        printf("%d", ptrToCurrentItem->data);
        if ((counter % 5) != 0 && (ptrToCurrentItem->next != NULL))
        {
            printf(" : ");
        }
        else
        {
            printf("\n");
        }
        ptrToCurrentItem = ptrToCurrentItem -> next;
    }
}

list *createListWithHeadData(int data)
{
    list *ptrList = malloc((sizeof(list)));
    listNode *ptrNewNode = malloc(sizeof(listNode));
    ptrNewNode->data = data;
    ptrNewNode->next = NULL;
    ptrList->head = ptrNewNode;
    ptrList->tail = ptrNewNode;
    
    return ptrList;
}

void addToFrontList(list *ptrList, listNode *ptrListNode)
{
    listNode *tempPtr = ptrList->head;
    ptrList->head = ptrListNode;
    ptrListNode->next = tempPtr;
}

list *arrayToList(int data[], int size)
{
    list *ptrToNewList = createListWithHeadData(data[0]);
    for (int i =1; i<size; i++)
    {
        listNode *ptrToNewListNode = malloc(sizeof(listNode));
        ptrToNewListNode->data = data[i];
        addToFrontList(ptrToNewList, ptrToNewListNode);
    }
    return ptrToNewList;
}

int count(listNode *ptrToHead)
{
    if (ptrToHead == NULL)
    {
        return 0;
    }
    else
    {
        return (1 + count(ptrToHead->next));
    }
}

void concat(listNode *head1, listNode *head2)
{
    assert(head1 != NULL);
    if (head1->next == NULL)
    {
        head1->next = head2;
    }
    else
    {
        concat(head1->next, head2);
    }
}

void insert(
    listNode *p1, // first element
    listNode *p2, // second element
    listNode *q) // new element to insert between first and second element
    {
        assert(p1->next == p2);
        p1->next = q;
        q->next = p2;
    }

void delete(listNode *listNode)
{
    assert(listNode != NULL);
    listNode = NULL;
}

void deleteList(list *list)
{
    if (list->head != NULL)
    {
        list->head = list->head->next;
        deleteList(list);
    }
}

void swapListNodeWithNext(listNode *ptrToCurrentNode, listNode *ptrToPreviousNode)
{
    //Swap items
    listNode *ptrTempNode1 = ptrToCurrentNode->next;
    listNode *ptrTempNode2 = ptrToCurrentNode->next->next;

    //Set the next node from temp1 (ptrToListNode->next->next) to itself
    //Could be written as ptrToListNode->next->next = ptrToListNode
    ptrTempNode1->next = ptrToCurrentNode;
    ptrToCurrentNode->next = ptrTempNode2;
    ptrToCurrentNode = ptrTempNode1;
    if (ptrToPreviousNode != NULL)
    {
        ptrToPreviousNode->next = ptrToCurrentNode;
    }
    ptrTempNode1 = NULL;
    ptrTempNode2 = NULL;
}

void sortList(list *ptrToListToSort)
{
    if (ptrToListToSort->head == NULL)
    {
        return;
    }
    listNode *ptrToCurrentItem = ptrToListToSort->head;
    listNode *ptrToPreviousItem = NULL;
    int sizeOfList = count(ptrToListToSort->head);
    int innerLoopCounter = 0;
    int unsortedElementLeft = sizeOfList;
    listNode *ptrToHead = ptrToListToSort->head;
    int swappedAtLeastOneItem = 0;

    for (int indexOuterLoop = 0; indexOuterLoop < sizeOfList; indexOuterLoop++)
    {
        ptrToCurrentItem = ptrToListToSort->head;
        while((ptrToCurrentItem->next != NULL) && (innerLoopCounter < unsortedElementLeft))
        {
            // If the data in the next item greater then the current item, swap nodes.
            if (ptrToCurrentItem->data > ptrToCurrentItem->next->data)
            {
                swappedAtLeastOneItem = 1;
                // If the current item is the head of the list, and since it will be swap, point to the next item.
                if (ptrToCurrentItem == ptrToListToSort->head)
                {
                    ptrToHead = ptrToCurrentItem->next;
                }
                //Swap items
                swapListNodeWithNext(ptrToCurrentItem, ptrToPreviousItem);
                //if the ptrToHead has changed, then update the changes.
                if (ptrToListToSort->head != ptrToHead)
                {
                    ptrToListToSort->head = ptrToHead;
                }
            } 
            // if the nodes do not need to swap, make sure to update the current item and previous items.
            else 
            {
                if (ptrToCurrentItem->next != NULL)
                {
                    ptrToCurrentItem = ptrToCurrentItem->next;
                }
            }
            if (ptrToPreviousItem != NULL)
            {
                ptrToPreviousItem = ptrToPreviousItem->next;
            }
            else
            {
                ptrToPreviousItem = ptrToHead;
            }
            innerLoopCounter++;
        }
        // If during the first loop no items were swap then exit early all items are already in order.
        if (!swappedAtLeastOneItem) 
        {
            printf("**List is already sorted!**\n");
            return; 
        }
        unsortedElementLeft--;
        innerLoopCounter=0;
        ptrToPreviousItem = NULL;
        if (ptrToCurrentItem->next == NULL)
        {
            ptrToListToSort->tail = ptrToCurrentItem;
        }
    }
}

int main(void)
{  
    printf("\n");

    int data1[6] = {2,3,5,7,8,9};
    list *ptrToNewList = arrayToList(data1,6);
    printList(ptrToNewList, "Array to Element List");
    sortList(ptrToNewList);
    printList(ptrToNewList, "Sorted List");
    printf("\n");
    printf("----------------------------\n");
    printf("\n");

    int data2[8] = {10,11,2,3,5,7,8,9};
    ptrToNewList = arrayToList(data2,8);
    printList(ptrToNewList, "Array to Element List");
    sortList(ptrToNewList);
    printList(ptrToNewList, "Sorted List");
    printf("\n");
    printf("\n");
    printf("----------------------------\n");
    printf("\n");

    int data3[10] = {10,11,2,3,5,7,8,1,9,1};
    ptrToNewList = arrayToList(data3,10);
    printList(ptrToNewList, "Array to Element List");
    sortList(ptrToNewList);
    printList(ptrToNewList, "Sorted List");
    printf("\n");    
    printf("\n");
    printf("----------------------------\n");
    printf("\n");

    int data4[10] = {1,1,1,1,1,1,1,1,1,1};
    ptrToNewList = arrayToList(data4,10);
    printList(ptrToNewList, "Array to Element List"); 
    sortList(ptrToNewList);
    printList(ptrToNewList, "Sorted List");
    printf("\n");    
    printf("\n");
    printf("----------------------------\n");
    printf("\n");

    int data5[10] = {21,19,16,13,10,9,6,2,1,1};
    ptrToNewList = arrayToList(data5,10);
    printList(ptrToNewList, "Array to Element List");
    sortList(ptrToNewList);
    printList(ptrToNewList, "Sorted List");
    printf("\n");    
    printf("\n");
    printf("----------------------------\n");

    printf("\n");
    printf("...End of line...\n");
    printf("\n");
    return 0;
}

请注意,我没有完成我的评论所以不要评判我,我总是最后完成。

希望这对与我有类似问题的人有所帮助。

【讨论】:

  • 感谢您为自己的问题撰写答案;我认为这是完全合适的。 C 用户通常更喜欢变量上的星号,而 C++ 通常在类型上。如果你想要更快的速度,通常列表上的合并排序会给你。
  • 很高兴您似乎发现了问题所在。我注意到这是泄漏的,所以我添加了一个 freeList 函数 here
  • 对@TedLyngmo ,从技术上讲,它没有泄漏,因为程序结束并且它释放了当时的所有资源。我确实有一个 deleteList 功能,但我在发布之前已将其删除,因为它不是要求的一部分,并且永远不会上线。但你是对的......我通常会取消一切,这次我没有。好眼力!
  • @Neil,感谢您澄清这一点。你看,我学习了 Basic,然后是 Fortran,然后是 VB.net,然后是 Swift,然后是一些 C++,这就是为什么我认为这很简单,但我想要的工作,我需要学习 C++ 和 C#。我真的不喜欢用 C 编程……但我必须从那里开始了解所有 C 之间的区别。至少那 3 个。干杯!
  • 现代 C++ 在设计上与 C 非常不同。它唯一共享的是相似的语法。你基本上是在学习一种具有不同最佳实践的新语言。这是 Linux 上的list sort
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多