【问题标题】:Sorting doubly linked list in c在c中对双向链表进行排序
【发布时间】:2012-03-07 08:18:30
【问题描述】:

我想在插入元素时保持一个有序的链表(列表中大约有 200000 个元素),您可以推荐哪种算法?我使用插入排序做了一个简单的实现,但是它的性能非常非常糟糕(大量的 CPU 使用)。

感谢您的帮助。

我对归并排序和插入排序做了一些比较,但似乎插入排序的性能更好,我对这个结果有点困惑。你能告诉我哪里出了问题,是否有更好的算法?

我的代码(为简单起见,我省略了节点结构中的 prev 节点):

struct node {
    int number;
    struct node *next;
};

插入排序:

void insert_node(int value) {
    struct node *new_node = NULL;
    struct node *cur_node = NULL;
    struct node *last_node = NULL;
    int found; /* 1 means found a place to insert the new node in, 0 means not*/


    new_node = (struct node *)malloc(sizeof(struct node *));
    if(new_node == NULL) {
        printf("memory problem\n");
    }
    new_node->number = value;
    /* If the first element */
    if (head == NULL) {
        new_node->next = NULL;
        head = new_node;
    } 

    else if (new_node->number < head->number) {
        new_node->next = head;
        head = new_node;    
    } 

    else {
        cur_node = head;
        found = 0;
        while (( cur_node != NULL ) && ( found == 0 )) {
            if( new_node->number < cur_node->number )
            {
                found = 1;
            }
            else
            {
                last_node = cur_node;
                cur_node = cur_node->next;
            }
        }
    /* We got the right place to insert our node */
    if( found == 1 )
    {
        new_node->next = cur_node; 
    }
    /* Insert at the tail of the list */
    else
    {
        last_node->next = new_node;
        new_node->next = NULL;
    }           
}

合并排序:

/* add a node to the linked list */
struct node *addnode(int number, struct node *next) {
    struct node *tnode;

    tnode = (struct node*)malloc(sizeof(*tnode));

    if(tnode != NULL) {
        tnode->number = number;
        tnode->next = next;
    }

    return tnode;
}

/* perform merge sort on the linked list */
struct node *merge_sort(struct node *head) {
    struct node *head_one;
    struct node *head_two;

    if((head == NULL) || (head->next == NULL))
        return head;

    head_one = head;
    head_two = head->next;
    while((head_two != NULL) && (head_two->next != NULL)) {
        head = head->next;
        head_two = head->next->next;
    }
    head_two = head->next;
    head->next = NULL;

    return merge(merge_sort(head_one), merge_sort(head_two));
}

/* merge the lists.. */
struct node *merge(struct node *head_one, struct node *head_two) {
    struct node *head_three;

    if(head_one == NULL)
        return head_two;

    if(head_two == NULL)
        return head_one;

    if(head_one->number < head_two->number) {
        head_three = head_one;
        head_three->next = merge(head_one->next, head_two);
    } else {
        head_three = head_two;
        head_three->next = merge(head_one, head_two->next);
    }

    return head_three;
}

【问题讨论】:

  • 必须是链表吗?如果您不允许自己使用更合适的数据结构,则没有什么可做的。
  • 您可以对元素进行预排序,然后将它们一次插入到列表的尾部,总复杂度为 O(N) ;-)
  • 合并排序应该仍然有效。
  • 欢迎来到 O(n)。如果您需要一个插入时排序的数据结构,那么双向链表不应该是您的首选。您可以破解一个涉及指向列表中 1/4、1/2 和 3/4 点的指针的解决方案(从而重新发明跳过列表)......但是当您可以使用数据结构时为什么还要麻烦更适合这项任务。
  • @funnyCoder - 不,不是。如果必须在插入时对其进行排序,则链接列表是您的最糟糕选择,正如人们试图指出的那样。例如,skip-list 是 O(log n)。

标签: c performance algorithm sorting linked-list


【解决方案1】:

要在链表中插入元素,排序的前提条件没有帮助! 没有算法可以帮助。

您可能希望为您的元素考虑另一种结构。根据您的情况,一个简单的堆或二叉搜索树可能会很好地为您服务。

如果你想将大量元素插入到一个大的排序链表中,你可以对它们进行排序,然后进行非常快速的合并 O(N)。

【讨论】:

    【解决方案2】:

    对于在线解决方案(在项目到达时插入它们),平衡二叉树可能是一个不错的选择。它允许在 O(Log(N)) 时间内插入(以及删除)。

    否则,MergeSort 可以应用于完整列表。

    在这两种情况下,总共进行 O(N.Log(N)) 次比较。

    【讨论】:

    • 感谢Yves的回复,但是使用树比使用链表更复杂,我只是对插入和排序没有删除等操作感兴趣。
    【解决方案3】:

    您没有正确实现基于递归地将列表分成两部分、对它们进行排序和合并结果的合并排序。但在您的代码中,您并没有真正将列表分成两半。

    请注意以下几行:

    while((head_two != NULL) && (head_two->next != NULL)) {
        head = head->next;
        head_two = head->next->next;
    }
    head_two = head->next;
    head->next = NULL;
    

    当 head_two 到达列表末尾时退出 while 循环:例如,如果在循环中到达 head_two-&gt;next == NULL,则使用 head-&gt;next-&gt;next == NULL 退出它。当你运行head_two = head-&gt;next; 时,你会得到一个head_two 这样head_two-&gt;next == NULL 这是列表中的最后一项。

    这意味着您基本上是在进行插入排序而不是合并排序。

    所以尝试跟踪列表的长度,通过在函数merge_sort 中添加参数length 以便能够将其拆分为2。这是wikipedia 中算法的一个很好的解释。

    【讨论】:

    【解决方案4】:

    链表必须需要 O(N) 步才能移动到链表中的任意位置。所以,听起来这不是适合您的数据结构。

    您可以尝试使用排序集 - C++ 中的 std::set 或 C# 中的 SortedSet。

    如果排序集抽象不适合您的问题,可能类似于排序链表的最简单数据结构是跳过列表:http://igoro.com/archive/skip-lists-are-fascinating/。更复杂的替代方案是 AVL 树和红黑树。

    【讨论】:

    • 注意:OP 询问的是 C,而不是 C++、C#、Piet 或 Assembler。
    • 啊,我明白了 - 我错过了“c”标签。
    【解决方案5】:

    Priority queues(或特别是Skip Lists)是您正在寻找的。对于简单的链表,它们允许对数插入而不是 O(n)

    这是skip list implementation in C on GitHub

    【讨论】:

      【解决方案6】:

      如果您需要将m 元素插入到大小为n 的排序列表中,您可以直接插入复杂度m·n,或对元素进行预排序(我建议为此进行合并排序)和将它们合并到具有复杂性m·ln(m) + (n + m)的原始列表中。

      基本上,您将使用插入和合并排序的特殊版本,它们都可以作为在线算法实现,因此非常适合链表。请记住,合并排序的“教科书”实现表现良好,因为它在将列表划分为子列表时不必要地迭代列表:您需要稍微复杂的基于堆栈的版本...

      【讨论】:

      • 感谢您的回答,因此我可以完成所有插入操作,并且在完成排序(合并排序)之后,我同意。你有一些关于如何做到这一点的链接。再次感谢。
      【解决方案7】:

      实际上您不想对列表进行排序。 合并排序用于对未排序的列表进行排序。 (这里有些人已经指出了这一点)。

      要保持列表排序,您必须在正确的位置插入每个元素。 所以基本上你必须:

      1. 搜索插入新条目的位置
      2. 插入

      这里的复杂性在于搜索算法。

      因此,二叉树甚至更好的是 AVL 树 (http://en.wikipedia.org/wiki/AVL_tree) 会非常有效。

      另一种选择是使用二进制搜索 (http://en.wikipedia.org/wiki/Binary_search_algorithm)。但同样,这仅对跳过列表有效。

      因此,无论您更改数据结构还是使用简单的双链表,O(N) 复杂度都是您可以达到的最佳值。

      【讨论】:

        【解决方案8】:

        我喜欢这样的事情。插入和查找是 O(log(n)),遍历是 O(n)。 'heapness' 属性可确保始终对数据进行排序。您可以向前和向后遍历,从而获得双向链表的优势。

        【讨论】:

          【解决方案9】:

          我已对您的代码进行了必要的更改。我已经对其进行了测试,并且效果很好。您现在应该可以关闭查询了。

          struct node *insert_node( struct node *head, int *value )
          {
                  struct node *new_node = NULL;
                  struct node *cur_node = NULL;
                  struct node *last_node = NULL;
                  int found; /* 1 means found a place to insert the new node in, 0 means not*/
                  new_node = (struct node *)malloc(sizeof(struct node *));
                  if(new_node == NULL)
                  {
                          printf("memory problem\n");
                  }
                  new_node->number = *value;
                  /* If the first element */
                  if (head == NULL)
                  {
                          new_node->prev = new_node->next = NULL;
                          head = new_node;
                  }
                  else if (new_node->number < head->number)
                  {
                          new_node->next = head;
                          head = new_node;
                  }
                  else
                  {
                          cur_node = head;
                          found = 0;
                          while (( cur_node != NULL ) && ( found == 0 ))
                          {
                                  if( new_node->number < cur_node->number )
                                          found = 1;
                                  else
                                  {
                                          last_node = cur_node;
                                          cur_node = cur_node->next;
                                  }
                          }
                          /* We got the right place to insert our node */
                          if( found == 1 )
                          {
                                  new_node->next = cur_node;
                                  new_node->prev = last_node;
                                  last_node->next = new_node;
                                  cur_node->prev = new_node;
                          }
                          /* Insert at the tail of the list */
                          else
                          {
                                  last_node->next = new_node;
                                  new_node->next = NULL;
                                  new_node->prev = last_node;
                          }
                  }
                  return head;
          }
          

          【讨论】:

            猜你喜欢
            • 2016-06-19
            • 1970-01-01
            • 1970-01-01
            • 2011-02-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-22
            • 1970-01-01
            相关资源
            最近更新 更多