【链表】(四) 双链表

目录

一、简介 - 双链表

二、添加操作 - 双链表

三、删除操作 - 双链表

四、设计链表

4.1 题目要求

4.2 解决过程


一、简介 - 双链表

// C++ implementation
// Definition for doubly-linked list.
struct DoublyListNode {
    int val;
    DoublyListNode *next, *prev;
    DoublyListNode(int x) : val(x), next(NULL), prev(NULL) {}
};
# Python implementation
# Definition for doubly-linked list.
class DoublyListNode:
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None

参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/756/


二、添加操作 - 双链表

参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/757/


三、删除操作 - 双链表

 

参考文献:https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/758/


四、设计链表

4.1 题目要求

4.2 解决过程

面试要点

原理简述

class MyLinkedList:
    def __init__(self):
        self.size = 0
        # sentinel nodes as pseudo-head and pseudo-tail
        self.head, self.tail = ListNode(0), ListNode(0) 
        self.head.next = self.tail
        self.tail.prev = self.head

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None
        self.prev = None

# 伪代码
new.prev = pred
new.next = succ
pred.next = new
succ.prev = new

# 伪代码
pred.next = succ
succ.prev = pred

# 伪代码
# choose the fastest way: to move from the head or to move from the tail
if index + 1 < self.size - index:
    curr = self.head
    for _ in range(index + 1):
        curr = curr.next
else:
    curr = self.tail
    for _ in range(self.size - index):
        curr = curr.prev

官方实现

class ListNode:
    def __init__(self, x):
        self.val = x
        self.next, self.prev = None, None

class MyLinkedList:
    def __init__(self):
        self.size = 0
        # sentinel nodes as pseudo-head and pseudo-tail
        self.head, self.tail = ListNode(0), ListNode(0) 
        self.head.next = self.tail
        self.tail.prev = self.head
        
    def get(self, index: int) -> int:
        """
        Get the value of the index-th node in the linked list. If the index is invalid, return -1.
        """
        # if index is invalid
        if index < 0 or index >= self.size:
            return -1
        
        # choose the fastest way: to move from the head
        # or to move from the tail
        if index + 1 < self.size - index:
            curr = self.head
            for _ in range(index + 1):
                curr = curr.next
        else:
            curr = self.tail
            for _ in range(self.size - index):
                curr = curr.prev    
                
        return curr.val
            
    def addAtHead(self, val: int) -> None:
        """
        Add a node of value val before the first element of the linked list. 
        After the insertion, the new node will be the first node of the linked list.
        """
        pred, succ = self.head, self.head.next
        
        self.size += 1
        to_add = ListNode(val)
        to_add.prev = pred
        to_add.next = succ
        pred.next = to_add
        succ.prev = to_add
        
    def addAtTail(self, val: int) -> None:
        """
        Append a node of value val to the last element of the linked list.
        """
        succ, pred = self.tail, self.tail.prev
        
        self.size += 1
        to_add = ListNode(val)
        to_add.prev = pred
        to_add.next = succ
        pred.next = to_add
        succ.prev = to_add

    def addAtIndex(self, index: int, val: int) -> None:
        """
        Add a node of value val before the index-th node in the linked list. 
        If index equals to the length of linked list, 
        the node will be appended to the end of linked list. 
        If index is greater than the length, the node will not be inserted.
        """
        # If index is greater than the length, the node will not be inserted.
        if index > self.size:
            return
        # [so weird] If index is negative, the node will be inserted at the head of the list.
        if index < 0:
            index = 0
        
        # find predecessor and successor of the node to be added
        if index < self.size - index:
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next
        else:
            succ = self.tail
            for _ in range(self.size - index):
                succ = succ.prev
            pred = succ.prev
        
        # insertion itself
        self.size += 1
        to_add = ListNode(val)
        to_add.prev = pred
        to_add.next = succ
        pred.next = to_add
        succ.prev = to_add

    def deleteAtIndex(self, index: int) -> None:
        """
        Delete the index-th node in the linked list, if the index is valid.
        """
        # if the index is invalid, do nothing
        if index < 0 or index >= self.size:
            return
        
        # find predecessor and successor of the node to be deleted
        if index < self.size - index:
            pred = self.head
            for _ in range(index):
                pred = pred.next
            succ = pred.next.next
        else:
            succ = self.tail
            for _ in range(self.size - index - 1):
                succ = succ.prev
            pred = succ.prev.prev
            
        # delete pred.next 
        self.size -= 1
        pred.next = succ
        succ.prev = pred

优化实现

此处对双链表进行 类内嵌、方法封装、成员非公有化 等优化处理。例如,创建一个轻量级非公有 _ListNode 类,由于该类不会直接暴露给用户,所以被定义为非公有的、最终的 MyLinkedList 类的内部嵌套类;各种属性成员和成员函数也被同理被定义为非公有的;为提高内存的利用率,专门定义了 __slots__ 方法,因为一个链表中通常具有若干个节点实例。实现如下:

2020/07/08 - 94.35%

## 个人特别实现 (未完)
class MyLinkedList:
    """ An implementation of doubly linked list """
    
    # -------------------------- nested _ListNode class -------------------------- #
    class _ListNode: 
        """ Lightweight, nonpublic class for storing a doubly linked node """
        __slots__ = "_val", "_next", "_prev"    # straemline memory usage (special)
        
        def __init__(self, elem=0, nxt=None, pre=None):  # initialize node's fields
            self._val = elem                    # reference to user's element
            self._next = nxt                    # reference to the next node
            self._prev = pre                    # reference to the previous node
    
    # ------------------------- linked list basic methods ------------------------ #
    def __init__(self):
        """ initialize with five nonpublic properties """
        self._size = 0                          # the number of elements
        self._head = self._ListNode()           # sentinel node as pseudo-head
        self._tail = self._ListNode()           # sentinel node as pseudo-tail
        self._head._next = self._tail           # cross reference  →
        self._tail._prev = self._head           # cross reference  ←
        
    def __len__(self):
        """ return the length of the doubly linked list """
        return self._size
    
    def isEmpty(self):
        """ return if the doubly linked list is empty """
        return self._size == 0

    # ----------------------- linked list nonpublic methods ---------------------- #
    def _findNode(self, left_step, right_step):
        """ find the specified node with the fastest starting point and direction """
        if (left_step) < (right_step):          # choose the fastest way
            curr = self._head                   # close to left, move from the head → 
            for _ in range(left_step):
                curr = curr._next
        else:
            curr = self._tail                   # close to right, move from the tail ← 
            for _ in range(right_step):
                curr = curr._prev
        return curr
        
    def _insertNode(self, val, pred, succ):
        """ insert a specific node with three parameters """
        new_node = self._ListNode(val)          # initialize the new node
        new_node._prev = pred                   # cross reference
        new_node._next = succ
        pred._next = new_node
        succ._prev = new_node
        self._size += 1                         # increment the node count
        
    def _deleteNode(self, pred, succ):
        """ delete a specific node with two parameters """
        pred._next = succ                       # skipping cross reference
        succ._prev = pred
        self._size -= 1                         # decrement the node count
        
    # ------------------------ linked list public methods ------------------------ #
    def get(self, index: int) -> int:
        """
        Get the value of the index-th node in the linked list. 
        If the index is invalid, return -1.
        """
        if (index < 0) or (index >= self._size):  # return -1 if index is invalid
            return -1
        curr = self._findNode(index+1, self._size-index)  # find the specified node
        return curr._val
            
    def addAtHead(self, val: int) -> None:
        """
        Add a node of value val before the first element of the linked list. 
        After the insertion, the new node will be the first node of the linked list.
        """
        pred, succ = self._head, self._head._next  # head node's predecessor and sucessor
        self._insertNode(val, pred, succ)

        
    def addAtTail(self, val: int) -> None:
        """
        Append a node of value val to the last element of the linked list.
        """
        succ, pred = self._tail, self._tail._prev  # tail node's sucessor and predecessor
        self._insertNode(val, pred, succ)

    def addAtIndex(self, index: int, val: int) -> None:
        """
        Add a node of value val before the index-th node in the linked list. 
        If index equals to the length of linked list, 
        the node will be appended to the end of linked list. 
        If index is greater than the length, 
        the node will not be inserted.
        """
        if index > self._size:
            return
        if index < 0:
            index = 0
                                                # find predecessor and successor of the node to be added       
        if index < (self._size-index):          
            pred = self._head                   # close to left, move from the head →  
            for _ in range(index):
                pred = pred._next
            succ = pred._next
        else:
            succ = self._tail                   # close to right, move from the tail ←  
            for _ in range(self._size-index):
                succ = succ._prev
            pred = succ._prev
                                                
        self._insertNode(val, pred, succ)       # insert itself

    def deleteAtIndex(self, index: int) -> None:
        """
        Delete the index-th node in the linked list, if the index is valid.
        """
        if (index < 0) or (index >= self._size):  # if the index is invalid, do nothing
            return
                                                # find predecessor and successor of the node to be deleted
        if index < (self._size-index):  
            pred = self._head                   # close to left, move from the head → 
            for _ in range(index):
                pred = pred._next
            succ = pred._next._next
        else:
            succ = self._tail                   # close to right, move from the tail ← 
            for _ in range(self._size-index-1):
                succ = succ._prev
            pred = succ._prev._prev
        
        self._deleteNode(pred, succ)            # delete the node 

注意,其中还加入了一些其他没用到但有意义的方法。从结果来看,几乎比单链表快 2 倍,可见 含哨兵节点的双链表 及其各种优化是有意义的。此外,还可以增加 反转 (reverse)、遍历 (traverse)、更新 (update)、排序 (sort) 等更多功能。

复杂度分析

参考文献

https://leetcode-cn.com/explore/learn/card/linked-list/196/doubly-linked-list/759/

https://blog.csdn.net/qq_39478403/article/details/107147763

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值