【问题标题】:Bubble sort singly linked list in C with pointersC中带指针的冒泡排序单链表
【发布时间】:2014-02-18 18:24:39
【问题描述】:

我正在尝试使用 C 中的指针操作对单链表进行冒泡排序。我在网站上查看了其他一些冒泡排序的实现,但我觉得这里的代码逻辑应该是有意义的。即便如此,还是进入了无限循环。任何帮助将不胜感激!

int counter;
struct node* current = head;
struct node* previous = (struct node*) malloc(sizeof(struct node));
struct node* next = (struct node*) malloc(sizeof(struct node));

for (counter = 0; counter < num_nodes; counter++){
    current = head;
    next = current->m_next;

    while(next != NULL){
        int compare = strcmp(current->m_last_name, next->m_last_name);
        if (compare > 0){
            if (current == head){
                head = next;
            }
            previous->m_next = next;
            current->m_next = next->m_next;
            next->m_next = current; 

            previous = next;
            next = current->m_next;
        }
        else {
            previous = current;
            current = current->m_next;
            next = current->m_next;
        }
    }
}
printf("Loop completely done\n");

}

【问题讨论】:

  • 这不是双向链表吗?每个节点都有下一个和上一个指针...
  • calloc() 而不是 malloc() 会很好,所以我们知道指针开始为空。必须猜测你的节点声明(我猜是单链接的?)
  • 嗯。缺少太多代码,甚至没有 cmets 来指示您删除了哪些代码(好吧,除非这 所有代码,在这种情况下,哎呀)。例如,以前是如何真正初始化的。等等。
  • 找到一个进入无限循环的列表。在一张纸上写下您认为应该按顺序执行的每条语句。现在在调试器中遍历您的算法。执行不在您列表中的语句的那一刻,这就是错误所在
  • 您的mallocs 似乎触发了内存泄漏。你写next=malloc(),四行之后,你写next=...。 malloc 后next 指向的内存丢失了!冒泡排序可以不使用malloc。只需仔细修改您的指针...

标签: c list sorting linked-list bubble-sort


【解决方案1】:

这里有几个问题。首先,为什么要为nextprevious 节点指针分配内存?您正在排序,没有涉及新节点,您只是在调整现有节点之间的链接。更重要的是,您重新分配您拥有malloced 内存的指针,例如:

previous = current;

这意味着,previous 的值以及分配的内存都丢失了。

接下来,你的头指针很可能在排序后就不一样了。调用函数需要了解此更改。因此,您应该将指向节点指针的指针传递给排序函数。 (我相信您将 ndes 添加到列表的功能也可以做到这一点。)

previous 指针也是如此。如果这是一个节点指针,那就是:另一个指向同一个节点的指针。更新它时,会更新列表结构外部的指针。因此,previous 也应该是指向节点指针的指针。这个指针首先与指向头节点指针的指针相同,然后在遍历链表时指向前一个节点的next 指针。如果你更新prev 指向的指针,你就会更新列表结构中的指针,这就是你想要的。

此外,在您的代码中明确使用num_nodes 非常不像列表。您应该使用列表结构本身来编写算法。

死循环是由current 交换后没有正确更新引起的。鉴于对 malloced 指针的误解,我没有对此进行过多研究。

这是一个有效的实现:

void list_bubble_sort(struct node **head) 
{
    int done = 0;         // True if no swaps were made in a pass

    // Don't try to sort empty or single-node lists
    if (*head == NULL || (*head)->m_next == NULL) return;

    while (!done) {
        struct node **pv = head;            // "source" of the pointer to the
                                            // current node in the list struct
        struct node *nd = *head;            // local iterator pointer
        struct node *nx = (*head)->m_next;  // local next pointer

        done = 1;

        while (nx) {
            int cmp = strcmp(nd->m_last_name, nx->m_last_name);

            if (cmp > 0) {
                nd->m_next = nx->m_next;
                nx->m_next = nd;
                *pv = nx;

                done = 0;
            }
            pv = &nd->m_next;
            nd = nx;
            nx = nx->m_next;
        }
    }
}

请注意pv 始终包含指向nd 的内部指针的源,以及如何通过*head 指向头节点并非特殊情况。

算法从头到尾重复地传递列表,直到不需要进行任何更改。这有点粗略,但毕竟是冒泡排序。

编辑 在另一个答案中,aehrwyn 解释了为什么会出现无限循环。他的修复工作有效,但存在不必要地分配内存并且从未真正使用过的缺陷,更不用说释放它了。应用 aehrwyn 的发现,您的代码应如下所示:

struct node *bubble (struct node *head)
{
    int num_nodes = count(head);
    int counter;

    for (counter = 0; counter < num_nodes; counter++) {
        struct node* current = head;
        struct node* next = current->m_next;
        struct node* previous = NULL;

        while(next != NULL) {
            int compare = strcmp(current->m_last_name, next->m_last_name);
            if (compare > 0) {
                if (current == head){
                    head = next;
                } else {
                    previous->m_next = next;
                }
                current->m_next = next->m_next;
                next->m_next = current; 

                previous = next;
                next = current->m_next;
            }
            else {
                previous = current;
                current = current->m_next;
                next = current->m_next;
            }
        }
    }
    printf("Loop completely done\n");
    return head;
}

注意事项:

  • 不需要 malloc 任何东西。指针nextprevious 只是没有正确数据的“工作指针”;它们指向现有节点。

  • 我已将工作指针的定义移到外循环内。在我看来,这反映了它们的范围,并使诸如忘记重新设置它们之类的错误更容易被发现。

  • 您分配previous-&gt;m_next。我认为您害怕取消引用 NULL 指针,因此分配了一个虚拟节点。相反,您应该在取消引用之前检查NULL。在您的代码中,条件previous != NULL 等同于current == head,因此分配给previous-&gt;m_next 可以发生在else 子句中。事实上,if/else 反映了两种基本情况:头节点和后续节点。

  • 在我上面的(有点粗鲁的)解释中,我说过你应该使用指向节点的指针,但那是因为我认为你的代码来自一个单独的函数。但它可能是main 的一部分,所以使用简单的struct node * 是可以的。请注意,当您返回新的头节点时,这也适用于单独的函数。你必须调用你的函数:

    head = bubble(head);
    

    在我看来这很容易出错,因为忽略返回值是合法的。我更喜欢上面解释的双指针方法。 (旁注:我在 SO 上看到很多代码,这是一个很长的 main 函数。学习将代码拆分为可以重复使用的小型、独立的函数。起初可能看起来很乏味,但在从长远来看,这种编码风格会带来好处。)

【讨论】:

  • 非常感谢您的彻底回复! :) C 新手,所以这很有帮助。
【解决方案2】:

除了其他人提到的不必要的mallocing之外,您的大部分代码都是正确的。

您进入一个无限循环只是因为您在进入下一个 for 循环迭代时没有重置“上一个”。所以,当你设置:

上一个->下一个 = 下一个;

previous 可能是列表中倒数第二个项目,这是您在最后一次 for 循环迭代中的 while 循环中设置的。要快速解决问题以查看问题所在,请将您的第 3 行替换为:

struct node* previousDummy = (struct node*) malloc(sizeof(struct node));
struct node* previous;

并在你的while循环之前添加这个:

previous = previousDummy;

【讨论】:

  • 很好的解决方案,但你为什么要加入不必要的mallocing? next 不需要任何 mallocing,它会立即被覆盖。 previous 也没有。如果您想避免取消引用 NULL 指针,请有条件地这样做。所以真正的解决方法是将previous 初始化为NULL,并且只设置previous-&gt;m_next 如果是非空的。 (这可以是else 子句到(current == head)
  • 是的,你的真正修复是正确的,但正如我所提到的,我只是提供了一个“快速”修复来显示他的逻辑错误在哪里,因为这就是他要问的。代码肯定有更多改进的方法......
猜你喜欢
  • 2012-02-10
  • 2012-02-17
  • 2018-05-25
  • 1970-01-01
  • 1970-01-01
  • 2012-07-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多