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

List 一次合并两个列表,O(1)时间复杂度,list,complexity-theory,List,Complexity Theory,我想问的是,在C语言中,由于我们需要一个while循环来获取所有元素,如何在恒定时间内将两个未排序的列表合并成一个未排序的列表 例: 不要评判我是初学者。这取决于内存中的数据结构以及是否可以修改现有列表 如果您可以修改现有列表,那么您可以询问List1它的最后一个元素(即O(1),列表头有一个指向列表末尾的指针),然后只需执行List1->last->next=List2->head。然后,迭代List1将迭代所有元素 如果您不能更改列表1,则必须复制列表;这对于O(1)来说很棘手,但如果您将所

我想问的是,在C语言中,由于我们需要一个while循环来获取所有元素,如何在恒定时间内将两个未排序的列表合并成一个未排序的列表

例:


不要评判我是初学者。

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

如果您可以修改现有列表,那么您可以询问
List1
它的最后一个元素(即O(1),列表头有一个指向列表末尾的指针),然后只需执行
List1->last->next=List2->head
。然后,迭代
List1
将迭代所有元素


如果您不能更改列表1,则必须复制列表;这对于O(1)来说很棘手,但如果您将所有元素保留在单个内存区域中(即,您不使用指向节点的指针,而是将所有节点保留在一个数组中),则仍然有可能做到这一点。在这种情况下,为两个列表的节点分配内存,然后可以使用两次
memcopy()
填充结果。诚然,
memcopy();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;
}

其他常见的结构是双链接列表——同样是使用sentinel节点,而不是使用
head->prev==tail->next==NULL

如果您希望某些东西表现为串联列表,那么确实可以通过创建由两个现有列表类构造的串联列表类来实现。如果你使用纯C而不是C++,那么类的概念可能需要稍微模糊,但是结构上,这个想法是相同的。 ConcatenatedList类可以有两个属性:header属性(只不过是对第一个列表的引用)和tail属性(只不过是对第二个列表的引用)

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

if(ksize()){
返回表头->获取元素(k);
}
返回tail->getElement(k-header->size())

其余的编码应该直接从那里开始

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


值得注意的是,(not)invoible给出的答案也是有效的,但是它假设列表具有LinkedList结构,而这种方法没有这个要求。但是,在重复连接之后,这种方法在性能方面可能会开始看起来像LinkedList实现。

请澄清您是如何实现列表的。这将帮助其他人提供更好的答案。提供一个代码的工作示例将有助于进一步了解。我不太理解阿德里亚诺的逻辑……你能告诉我更多吗?如果我将第一个列表的尾部和第二个列表的头部作为参数,并使尾部指向第二个列表的头部,是否有效?@programfrog是,使第一个列表的尾部指向第二个列表的头部,而不是NULL。您用C标记了这个问题,虽然最终实现它的编程语言与这个问题没有太大关系。我不是问你列表的制作……我是说一个合并它们的函数。你没有详细说明列表的含义。如果你指的是像第二个一样的东西——任何你有固定时间访问尾部的单链接列表——或者循环双链接列表等等,并且你被允许破坏你的输入列表,那么你的问题的答案是微不足道的。太琐碎了,我以为你对数据结构而不是算法(在某种程度上,它们是分开的)感到困惑。我很抱歉我问了这么愚蠢的问题……对你认识的人好一点是可以的,即使是像梅这样愚蠢的人,我(试图)帮助你,但你没有给我太多的帮助。有一种链表类型的示例解决方案,但我不知道这是否与您的链表类型相同。如果你用的是别的什么?存储列表的方法有很多种,你不会说你有什么,从哪里得到的,或者你对它的了解。它是一个单链接的列表,有fiels head,tail和next…两个类似于这样的列表一个类似于这样的函数:合并(*listnode tail1,*listnode head1,*listnode head2){*listnode head3;tail1->next=head2;head3=head1;return head3;}可以吗?
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;
}