单链表

时间:2011-04-07 17:00:04

标签: c#

互联网中可用的一些示例使用单个链表的头部和尾部节点,并且一些示例不解释为什么存在这样的差异。

4 个答案:

答案 0 :(得分:3)

一个尾节点让O(1)将一个项附加到列表的末尾,而不是必须从头开始迭代以在追加之前找到尾,使其成为O(n)操作。因此,它对提高效率非常有用,但从根本上并不需要制作可用的列表。我怀疑这些例子的不同之处在于学术纯度和相对有用的列表。

在没有记住头节点的情况下拥有单链表是完全没有意义的:)

答案 1 :(得分:3)

当列表需要将新项目添加到列表末尾时,保持头部和尾部的原因主要是速度。

虽然找到列表末尾是微不足道的,但与在列表修改期间跟踪它所花费的成本相比,它需要花费太多时间。

答案 2 :(得分:0)

单链表使用较少的空间 - 每个单元少1个指针。添加到列表的头部并从列表的前半部分删除也更有效(因为不需要维护另一个链接所以操作更少)。

双向链表更有效地删除或添加列表的后半部分。

在广泛使用列表的Lisp中,只使用单链表。实际上,如果列表短于某个长度,某些实现(如Symbolics)会使用数​​组。

使用哪种选择取决于应用。如果追加是一种常见操作,并且速度比空间更重要,那么双重链表可能是合适的。但是,我认为单链表在大多数情况下更合适,因此更常见。

答案 3 :(得分:0)

正如其他人指出的那样,尾部引用提供了一种在恒定时间内将节点附加到列表末尾的方法。如果链接列表实现没有尾部引用,则必须遍历列表中的所有节点才能将其添加到末尾。下面是一个java链接列表示例,它使用3种不同的方法将元素添加到列表中。

  • 推送(节点节点) - 将节点添加到列表的开头。由于磁头参考,这需要恒定的时间O(1)。
  • add(int index,Node node) - 在指定的索引处添加节点。这需要O(n)时间,因为它必须遍历每个节点,直到找到正确的索引。
  • add(节点节点) - 将节点添加到列表末尾。如上所述,由于我们使用尾部引用,因此此操作只需要一段时间。

    // Insert element at the beginning of the list
    public void push(T _data) {
    
        Node<T> addNode = new Node<T>(_data);
    
        // Set head and tail to new pointer if list is empty
        if(this.head == null) {
            this.head = addNode;
            this.tail = addNode;
        } else {
            addNode.setNext(this.head); // Set new node's next pointer to the current head
            this.head = addNode;  // Set head to new node
        }
    
        this.numberNodes++;
    }
    
    // Insert element at the specified index
    public void add(int _index, T _data) {
    
        // Continue if _index is valid
        if(_index >= 0 && _index <= this.numberNodes) {
    
            if(_index == 0) { // Push element to front of list if _index is 0
                this.push(_data);
            } else if(_index == this.numberNodes) { // Add element to end of list if index is last element 
                this.add(_data);
            } else {
    
                // Continue if list is not empty
                if(this.head != null && this.tail != null) {
    
                    Node<T> addNode = new Node<T>(_data);
                    Node<T> currentNode = this.head.getNext();
                    Node<T> previousNode = this.head;
    
                    int count = 1;
                    while(currentNode != null) { // Traverse list to find element at _index
    
                        // Insert element when _index is found
                        if(count == _index) {
                            previousNode.setNext(addNode);
                            addNode.setNext(currentNode);
                            this.numberNodes++;
                            break;
                        }
    
                        // Prepare for next iteration
                        previousNode = currentNode;
                        currentNode = currentNode.getNext();
                        count++;
                    }   
                }
            }
        }
    }
    
    // Insert element at the end of the list
    public void add(T _data) {
    
        Node<T> addNode = new Node<T>(_data);
    
        // Set head and tail to new pointer if list is empty
        if(this.head == null) {
            this.head = addNode;
            this.tail = addNode;
        } else {
            this.tail.setNext(addNode); // Set tail's next pointer to new node
            this.tail = this.tail.getNext(); // Set tail to new node
        }
    
        this.numberNodes++;
    }
    

添加尾部引用会显着减少在链接列表末尾插入元素的时间。使用Java,C ++,Python和JavaScript中的尾部引用,查看my blog的Linked List实现。