我想问一下如何在C中将两个未排序列表合并到一个未排序列表中,因为我们需要一个while循环来获取所有元素。在一个合并两个列表,O(1)时间复杂度
例如:
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
不要评判我,我是初学者。
我想问一下如何在C中将两个未排序列表合并到一个未排序列表中,因为我们需要一个while循环来获取所有元素。在一个合并两个列表,O(1)时间复杂度
例如:
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
不要评判我,我是初学者。
这取决于内存中的数据结构以及是否可以修改现有列表。
如果你可以修改现有的列表,那么你可以询问List1
它的最后一个元素(这是O(1)是列表头部有一个指向列表末尾的指针),然后它只是一个问题List1->last->next = List2->head
。之后,遍历List1
将迭代所有元素。
如果您不能更改List1
,那么您必须复制该列表;这对于O(1)来说很棘手,但如果将所有元素保留在单个内存区域(即,不使用指向节点的指针;而是将所有节点保留在数组中),仍然可能。在这种情况下,您为两个列表的节点分配内存,然后您可以使用两次memcopy()
填充结果。当然,memcopy()
并不是真正的O(1),但是对于当前的CPU(可以每秒复制千兆字节),通常不会注意到差异。
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
。
我不问你有关列表的问题......我正在谈论一个合并它们的函数 – programfrog 2014-10-28 16:54:36
你没有详细说明你的意思是什么。如果你的意思是类似于第二种 - 任何单向链接的列表,你可以随时访问尾部 - 或者一个循环双向链表等,并且允许你损害你的输入列表,那么你的答案是问题是微不足道的。如此微不足道,我假设你对数据结构而不是算法(在可分离的程度上)感到困惑。 – Useless 2014-10-28 16:59:03
我很抱歉,我问了这么愚蠢的事情......对于你认识的人来说,即使是像我这样的愚蠢的人也不错。 – programfrog 2014-10-28 17:02:08
如果你想要的东西行为作为一个连接列表,你确实可以通过创建一个由两个现有List类构造的ConcatenatedList类来实现。如果你正在使用纯C而不是C++,那么类的概念可能需要付出一点努力,但在结构上,这个想法是一样的。
ConcatenatedList类可以具有两个属性:一个头部属性,它只不过是对第一个List的引用,另一个是尾部属性,它只不过是对第二个List的引用。
然后,您可以使用ConcatenatedList模仿基本List类的方法。要访问ConcatenatedList的第k个元素,尝试这样的事:
if (k < header->size()) { return header->getElement(k); } return tail->getElement(k - header->size());
编码的其余部分应该是从那里直接的。
使用这种方法,连接过程将是O(1)。
值得注意的是,通过给出的答案(不)无用也是有效的,但它假定一个LinkedList结构到你的列表,而这种做法并没有这一要求。然而,有可能在重复连接之后,这种方法将在性能方面看起来像LinkedList实现。
请说明你是如何实现一个列表。这将帮助其他人提供更好的答案。提供您的代码的工作示例将有助于进一步。 – 2014-10-28 16:30:21
我真的不明白逻辑adriano ...你能告诉我更多吗? – programfrog 2014-10-28 16:31:30
如果我将第一个列表的尾部和第二个列表的尾部作为参数,并将尾部指向第二个列表的头部,它会起作用吗? – programfrog 2014-10-28 16:40:13