将两个列表合并为一个,O(1)时间复杂度

时间:2014-10-28 16:28:27

标签: list complexity-theory

我想问一下,如何在C中以恒定时间将两个未排序的列表合并到一个未排序的列表中,因为我们需要一个while循环来获取所有元素。

前:

List1: 2 5 1 4 3
List2: 5 9 4 2 5 7 8
List3: elements of the two lists,don't care about order

不要判断我是初学者。

3 个答案:

答案 0 :(得分:1)

这取决于内存中的数据结构以及是否可以修改现有列表。

如果您可以修改现有列表,那么您可以向List1询问它的最后一个元素(其中O(1)是列表标题具有指向列表末尾的指针)和那么它只是List1->last->next = List2->head的问题。之后,迭代List1将迭代所有元素。

如果您不能更改List1,则必须复制列表;这对于O(1)来说很棘手,但是如果将所有元素保存在单个内存区域中(例如,您不使用指向节点的指针;而是将所有节点保留在数组中),这仍然是可能的。在这种情况下,您为两个列表的节点分配内存,然后您可以将结果填充两次memcopy()。当然,memcopy()并非真的是O(1)但是当前的CPU(可以每秒复制千兆字节),你通常不会注意到差异。

答案 1 :(得分:1)

tl; dr:去阅读有关链表数据结构的内容。所有这些都很常见。


真正的O(1)追加的最低要求是你有可变链接列表,可以持续访问尾部。

因此,最简单的链表是:

struct ListNode {
  struct ListNode *next;
  int data; /* or void*, or whatever */
};

typedef struct ListNode *SinglyLinkedList;

即,只需按住指向列表第一个元素的指针即可。在这种情况下,到达尾部是线性时间(O(n)),因此您无法做到您想要的。但是,如果我们使用

struct ListHeadTail {
    struct ListNode *head;
    struct ListNode *tail;
    /* could keep length here as well, if you want it */
};

然后插入列表会稍微困难一些,但您可以轻松地进行常量时间追加:

struct ListHeadTail append(struct ListHeadTail *first,
                           struct ListHeadTail *second) {
    struct ListHeadTail result;

    /* special cases first, where either first or second is empty */
    if (first->head == NULL) {
        result = *second;
        second->head = second->tail = NULL;
    } else if (second->head == NULL) {
        result = *first;
        first->head = first->tail = NULL;
    } else {
        result.head = first->head;
        result.tail = second->tail;
        first->tail->next = second->head;
        first->head = first->tail = NULL;
        second->head = second->tail = NULL;
    }
    return result;
}

其他常见结构是双向链表 - 再次使用标记节点而不是head->prev == tail->next == NULL

答案 2 :(得分:0)

如果你想要的是表现作为连接列表的东西,你可以通过创建一个由两个现有List类构造的ConcatenatedList类来实现。如果你使用的是纯C而不是C ++,那么类的概念可能需要稍微捏一下,但从结构上来说,这个想法是一样的。

ConcatenatedList类可以有两个属性:一个头属性,它只不过是对第一个List的引用,以及一个tail属性,它只不过是对第二个List的引用。

然后使用ConcatenatedList模拟基本List类的方法。要访问ConcatenatedList的第k个元素,可以尝试以下方法:

if (k < header->size()) { return header->getElement(k); } return tail->getElement(k - header->size());

编码的其余部分应该是直接的。

使用这种方法,连接过程将是O(1)。

值得注意的是,(not)Useless提供的答案也是有效的,但它假定了列表的LinkedList结构,而这种方法没有这个要求。但是,在重复连接之后,这种方法在性能方面将开始看起来像LinkedList实现。