对于有尾结点的链表,我这样想可不可以?

来源:4-7 带有尾指针的链表:使用链表实现队列

alexMerce

2018-07-01

    在4-7章节中,为了实现队列,向链表中添加了Tail 尾结点。然而我认为还有可优化的余地。在链表中,可以用Node cur = head.next ; (循环  cur = cur.next ) 从头遍历到尾,但是并不能从尾遍历到头。如视频所言,这样链表是不对称的,那可不可以让链表对称?


    链表只能从头往尾遍历, 那我可以在Node内部类中再添加一个属性Node prev,这样Node内部类中就有了三个属性:E data保存数据,Node next 指向下一个结点, Node prev指向上一个结点。


    类似地,也可以来一个虚拟尾节点dummTail,在链表类的构造方法中,分别令dummyHead指向虚拟头结点,dummyTail指向虚拟尾结点,然后令dummyHead.next = dummyTail, dummyTail.prev = dummyHead , 而dummyHead. prev = null, dummyTail.next = null   。   这样就构造好了初始的链表


    然后向链表中增添或者删除一个结点,先判断这个索引是离头结点还是离尾节点更近,如果index < size/2,说明离头近,从头遍历,  如果index > size/2 说明离尾近,从尾遍历。 遍历的方法都是类似的,都是设置一个临时指针temp,如果离头近,那么我就 Node temp = dummyhead ,(循环 temp =temp.next ) ; 如果离尾近,我就Node temp = dummyTail,(循环 temp = temp.prev) .

这样的话,添加头,添加尾  和   删除头,删除尾的时间复杂度都是1 .    这样的话,链表就对称了,意思是从头到尾进行操作,和从尾到头进行操作,并没有复杂度不同的问题。

    不知道我这样想有没有合理性?  

写回答

1回答

liuyubobobo

2018-07-02

赞思考:)


首先,对于每一个节点,除了包含next,还包含prev,本身是一种很标准的链表实现方式,称为双链表(或者双向链表,Double Linked List)。在第五章最后,我会对更多链表形式进行简单的提及。强烈建议同学们在学习完单链表以后,自己编写一个双链表的类:)


你后面的分析完全合理:)

对于双链表,无论是头尾的添加还是头尾的删除,时间复杂度都是O(1)级别的;

同时对某个索引的添加和删除,可以使用你说的方法进行优化,不过时间复杂度依然是O(n)级别的:)

如果我们想删除指定元素(而非删除指定索引的元素),或者在添加元素的时候保持链表有序(而非在指定索引添加元素),依然要遍历整个链表,时间复杂度是O(n)的。


在大多数场景,我们使用单链表是足够的,比如使用基于链表的栈或者队列;

双链表最适合应用于在头尾都需要进行插入和删除的场景。有一种抽象的数据结构,叫做“双端队列”,通常叫deque,就是指在队列的两端均可以进行插入和删除元素的数据结构。deque的底层实现如果使用链式结构,就需要使用双端链表。当然,deque也可以使用动态数组来实现。有兴趣可以自己编写尝试一下,是一个很好的练习:)


加油!

0
2
liuyubobobo
回复
alexMerce
大赞!保持这个学习劲头,你就是未来的大神!:)
2018-07-03
共2条回复

玩转数据结构

动态数组/栈/队列/链表/BST/堆/线段树/Trie/并查集/AVL/红黑树…

6221 学习 · 1704 问题

查看课程