【数据结构】之双向链表的复杂度分析

双向链表的复杂度分析

我们先看一下双向链表的的概念:

它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。(摘抄自百度百科)

和单向链表不同的是,双链表除了有一个指向下一节点的指针外,还有一个指向前一节点的指针,这也就意味着,双向链表能够快速找到前驱节点,也能快速找到后驱节点。

和单向链表相比,对于插入或删除指定节点的操作,因为单向链表需要从头遍历定位到这个节点的前驱节点,所以它的平均复杂度是O(n)。而对于双向链表,因为知道了指定节点,就能马上知道它的前驱节点,所以它的复杂度是O(1)。

那么,问题来了,既然双链表效率比单链表高,为什么还要保留单链表呢,甚至单链表的应用比双链表还要广泛?

依我看,主要是因为以下几个方面:

1、单链表的学习成本要双链表低,而且学习了单链表就能更好的理解双链表

2、存储效率,每个双链表的节点要比单链表的节点多一个指针,也就意味着更高的内存开销,如果你对于效率和内存开销,更在意的是内存开销,又或者你的程序就没有指定节点插入或删除的操作,那这时候就可以用单链表。

下面我们来对双链表进一步分析 !

节点定义

我们先来双向链表的节点定义:

public class DoublyNode {
    int val;
    DoublyNode next;
    DoublyNode prev;

    DoublyNode(int element) {
        this.val = element;
    }
}

每个节点包含一个值val、前节点和后节点。(为了方便,这里直接以int作为val的类型)

所以,它的链式结构大概是长这样的:… <-> A <-> B <-> C <-> …

操作复杂度分析

双向链表插入

1、头插/尾插,O(1)

头插和尾插的比较容易理解,因为双向链表内部会维护指向头部和尾部的指针的,所以找到头部和尾部的节点,并在前或后插入数据,只需要O(1)的复杂度。

2、在指定节点前/后插入数据,O(1)

对于这个操作,我们举个例子来理解一下:

假设我们有一个双向链表:… <-> A <-> B <-> C <-> …现在我们要往【B】节点后插入【X】节点,即:… <-> A <-> B <-> X <-> C <-> …

这个插入操作可以分为以下几个步骤:

1、找到节点 B 的下一个节点,即 C (复杂度O(1),因为节点 B 已经存储了节点 C 的位置)
2、插入节点 X 将 prev 指针指向节点 B, next 指针指向节点 C (O(1))
3、修改节点 B 的 next 指针指向 X (O(1))
4、修改节点 C 的 prev 指针指向 X (O(1))

相加所得,时间复杂度为 O(1)

3、找到值等于x的节点,并插入在其前/后插入数据,O(n)

还是上面那个例子,相对于【在指定节点前/后插入数据】的操作来说,多了一个查找并定位节点 B 的操作,而等值查找操作在双向链表中的平均时间复杂度为 O(n), 故当前操作的时间复杂度也是 O(n)。

双向链表的删除

1、头删,O(1)

2、尾删,O(1)

3、删除指定节点前/后的数据,O(1)

4、删除值为x的节点,O(n)

删除和插入的操作同理,就不再赘述了。

双向链表的查找

1、查找值为x的节点,O(n)

2、查找索引为x的节点,O(n)

这个很好理解,跟单向链表一样,不管是按值查找还是按索引查找,都需要遍历取得对应值的节点,所以复杂度O(n)

数组、单链表、双链表的时间复杂度对比表

下面贴一个数组、单链表、双链表的时间复杂度对比表:

【数据结构】之双向链表的复杂度分析_第1张图片

你可能感兴趣的:(数据结构与算法,java,数据结构,链表)