Lisp 合并2个排序列表

Lisp 合并2个排序列表,lisp,Lisp,我被要求对以下问题提出尽可能多的解决方案: 编写一个函数,该函数包含两个数字列表(均假定为 以升序排列),并将它们合并到一个列表中(也以升序排列) 升序) 我的第一个解决方案是将列表1附加到列表2上,然后重新排序 然后我找到了一个内置的合并 然后我决定自己实际实现一个解决方案,我提出了一个尾部递归函数,目前它只适用于列表的子集 问题本身似乎是我终于有了阅读Knuth的理由,但唉,Uni和图书馆因为下雪而关闭了 所以我转向你们,对于这个问题,有什么有趣的、有效的或反模式的方法 另外,我不是在寻找

我被要求对以下问题提出尽可能多的解决方案:

编写一个函数,该函数包含两个数字列表(均假定为 以升序排列),并将它们合并到一个列表中(也以升序排列) 升序)

我的第一个解决方案是
列表1
附加到
列表2
上,然后重新排序

然后我找到了一个内置的
合并

然后我决定自己实际实现一个解决方案,我提出了一个尾部递归函数,目前它只适用于列表的子集

问题本身似乎是我终于有了阅读Knuth的理由,但唉,Uni和图书馆因为下雪而关闭了

所以我转向你们,对于这个问题,有什么有趣的、有效的或反模式的方法



另外,我不是在寻找实现,除非这是证明这个想法的最佳方式。我只是想看看人们是如何处理这类问题的。

合并两个排序列表在算法上很简单

从每个列表的第一个元素开始,进行比较,将较低的元素写入输出,并将列表推进到找到较低元素的位置。继续,直到你到达一个列表的末尾,然后把另一个列表的剩余部分放出来

这只需要在每个列表上循环一次,每个输出元素最多进行一次比较


编辑:对于两个列表,这是最有效的。当您有大量列表要合并时,它会变得(有点)棘手。

您正在寻找创建列表(通过合并两个列表)的函数的递归定义

可以预期,该函数将通过根据某种原则提取head元素,然后从其余元素递归构造list tail来创建一个列表


由于需要返回一个已排序的列表,因此列表头始终是列表中最小的元素。所以,您所需要做的就是找到一个算法,从两个排序的列表中提取最小的元素(这应该相当容易,因为它们已经排序)

如果列表可以加倍为,则可以将
list2
的所有元素插入
b-tree(list1)
并转换回列表。

可以编写一个函数
is-sorted-p
,确定列表是否仍按升序排列。然后将两个列表连接起来,并随机洗牌结果,直到它通过谓词


…你没有说任何关于性能的事。

如果你正好有两个,那就相对容易了

  • 两个列表都是空的吗?如果是,则结果为空列表
  • 一个列表是空的,另一个不是?结果是非空列表
  • 找到最小的列表头(“car”),结果就是该列表头,与该列表和另一个列表的尾部的自调用结果相关联

  • 这可以通过使用一些辅助参数(可能还有一个自定义的“反转和反转”辅助函数)转换为尾部递归过程。

    有几种方法可以解决这个问题

    因为有两个链表,而且它们都是以最简单的方式在高层进行排序的 忽略基本情况

    list_t*
    merge(list_t *head_1P, list_t *head_2P) {
    
          if (head_1P == NULL) {
                 return head_2P;
          } else if (head_2P == NULL) {
                 return head_1P;
          }
    
          list_t *temp = NULL;
          list_t *new_headP = NULL;
    
          while(head_2P != NULL) {
                // Remove one element from
                // the second list.
                temp = head_2P;
                head_2P = head_2P->next;
                temp->next = NULL;
    
                insert_sort(&head_1P, temp);
          }
    }
    
    void
    insert_sort(list_t **headP, list_t *temp) {
    
          if (*headP == NULL || (*headP)->data > temp->data) {
    
                 temp->next = *headP;
                 *headP = temp;
                 return;
          }
    
          list_t *currentP = *headP;
    
          while(currentP->next != NULL) {
    
                if (currentP->next->data > temp->data) {
    
                        temp->next = currentP->next;
                        currentP->next = temp;
                        return;
                }
    
                currentP = currentP->next;
          }
    
          temp->next = currentP->next; // Assigning NULL.
          currentP->next = temp;
    }
    
    为了进一步优化这一点,我们可以使insert_排序重新运行插入的位置
    避免每次从一开始就发生插入。因为在最坏的情况下,如果第二个链表元素全部插入到第一个链表的末尾,这可能需要O(m*n)时间复杂度。

    如果它们可以加倍,将大型链表转换为BBT会比合并更便宜吗?链表通常不如树加倍,但数组做得很简单。。。。再说一次,插入数组并不像插入列表那么简单。@彼得:是的,向平衡树中添加元素是一个O(logn)操作,而列表的插入(合并)是O(n),但同样地,O(n)对于一个小的n是非常好的,而且大多数时候n是小的,而列表实现为(几乎)平衡的二叉树,没有必要来回转换。列表合并变成树合并。通过将一棵树的元素逐个插入另一棵树来合并树是
    O(n*log(n+m))
    。列表合并是
    O(n+m)
    。因此,如果一个比另一个短得多,那么选择较短的一个可能会胜过使用简单的列表。我有点不安,因为它有一个名字。我很不安,这是这里的第二级答案。:-)嘿,OP要求“尽可能多的解决方案”,所以这绝对是一个有效的答案:-)我认为书面回答并不那么好,即使如此:也不能保证它会成功。与其随机洗牌列表,它应该尝试所有可能的顺序,顺序!