何时双链表比单链表更有效?

时间:2013-03-22 05:00:14

标签: algorithm linked-list

今天在接受采访时,我被问到了这个问题。

除了回答反转名单以及向前和向后遍历之外,面试官还在继续强调“基本”。我放弃了,当然在采访后做了一些研究。似乎插入和删除在双链表中比单链表更有效。我不太确定如何对双链表更有效,因为很明显需要更多的引用才能改变。 任何人都可以解释背后的秘密吗?我老老实实地进行了相当多的研究,并且无法理解我的主要问题是双链表仍然需要进行O(n)搜索。

7 个答案:

答案 0 :(得分:41)

只要您满足于始终插入头部或某些已知元素之后,插入显然不会在单链表中工作。 (也就是说,您不能在已知元素之前插入,但请参见下文。)

另一方面,删除比较棘手,因为你需要知道要删除的元素之前的元素。

这样做的一种方法是使删除API与要删除的元素的前任一起使用。这反映了插入API,它采用了将作为新元素的前身的元素,但它不是很方便而且很难记录。但这通常是可能的。一般来说,您通过遍历列表来到达列表中的元素。

当然,你可以从头开始搜索列表,找到要删除的元素,这样你才能知道它的前身是什么。这假设删除API包括列表的头部,这也是不方便的。此外,搜索速度非常慢。

几乎没有人使用但实际上非常有效的方法是将单链接列表迭代器定义为指向迭代器当前目标之前的元素的指针。这很简单,只有一个间接比使用直接指向元素的指针慢,并且快速插入和删除。缺点是删除元素可能会使列表元素的其他迭代器无效,这很烦人。 (它不会使被删除元素的迭代器无效,这对于删除某些元素的遍历很有用,但这并不是很多补偿。)

如果删除不重要,可能因为数据结构是不可变的,单链表提供了另一个非常有用的属性:它们允许结构共享。单链表可以很好地成为多个头的尾部,这对于双向链表是不可能的。出于这个原因,单链表通常是功能语言选择的简单数据结构。

答案 1 :(得分:19)

以下是一些让我更清楚的代码......拥有:

class Node{
      Node next;
      Node prev;
}

删除单个链接列表中的节点 -O(n) -

您不知道哪个是前一个节点,因此您必须遍历该列表,直到找到它为止:

deleteNode(Node node){
    prevNode = tmpNode;
    tmpNode = prevNode.next;

    while (tmpNode != null) {
        if (tmpNode == node) {
            prevNode.next = tmpNode.next;
        }
        prevNode = tmpNode;
        tmpNode = prevNode.next;
    }
}

删除双链接列表中的节点 -O(1) -

你可以简单地更新这样的链接:

deleteNode(Node node){
    node.prev.next = node.next;
    node.next.prev = node.prev;
}

答案 2 :(得分:10)

以下是我对双重关联名单的看法:

  • 您可以在两端准备好访问\ insert。

  • 它可以同时作为队列和堆栈使用。

  • 节点删除不需要额外的指针。

  • 您可以应用Hill-Climb遍历,因为您已经可以访问两端了。

  • 如果您正在存储数值,并且您的列表已排序,您可以保留指针/变量用于中位数,然后使用统计方法可以非常优化搜索操作。

答案 3 :(得分:5)

如果要删除链接列表中的元素,则需要将前一个元素链接到下一个元素。使用双向链表,您可以随时访问这两个元素,因为您可以链接到这两个元素。

这假设您已经有一个指向您需要删除的元素的指针,并且不涉及搜索。

答案 4 :(得分:2)

'除了回答列表的反转以及向前和向后遍历之外,还有一些东西"基本的"'。

似乎没有人提到:在双向链表中,只需通过指向已删除元素的指针即可重新插入已删除的元素。请参阅Knuth的Dancing Links论文。我认为这非常重要。

答案 5 :(得分:0)

Doubly Linked list is more effective than the Singly linked list when the location of the element to be deleted is given.Because it is required to operate on "4" pointers only & "2" when the element to be deleted is at the first node or at the last node.


struct  Node {

       int  Value;
       struct Node  *Fwd;
       struct Node  *Bwd;
);


Only the below line of code will be enough to delete the element ,If the element to be deleted is not in the first or last node.

X->Bwd->Fwd = X->Fwd; X->Fwd->Bwd = X->Bwd ;

答案 6 :(得分:0)

单链表 vs 双链表 vs 动态数组

在比较三种主要数据结构时,从时间复杂度来看,双向链表在所有主要任务和操作中效率最高。对于双向链表,除了仅按索引访问之外,它在所有操作的恒定时间运行,它在线性时间 (n) 运行,因为它需要遍历每个节点以获得所需的索引。当涉及到插入、删除、第一个、最后一个、连接和计数时,双向链表以恒定时间运行,而动态数组以线性时间 (n) 运行。

在空间复杂度方面,动态数组只存储元素,因此时间复杂度恒定,单链表存储每个元素的后继,因此线性空间复杂度(n),最糟糕的是双链表存储每个元素的前驱和后继元素,因此也是线性空间复杂度,但是 (2*n)。

除非您的资源/空间极其有限,否则动态数组或单向链表可能更好,但是,如今空间和资源越来越丰富,因此双链表要好得多,但要以更多空间为代价。

相关问题