ruby 中没有扩展数组的最佳链表?

Posted

技术标签:

【中文标题】ruby 中没有扩展数组的最佳链表?【英文标题】:best linked list in ruby WITHOUT extending array? 【发布时间】:2013-09-08 05:34:48 【问题描述】:

在不使用/扩展 Array 类的情况下,在 Ruby 中实现链表的最佳方法是什么?这是我过去使用过的一个实现,但它似乎不是最好的方法:

class Node

    attr_accessor :value, :next_node

    def initialize(value = nil)
        @value = value
    end

    def to_s
        @value
    end

end

class SinglyLinkedList

    attr_accessor :head

    def initialize(first_value=nil)
        @head = Node.new(first_value) if first_value
    end

    def add(value)
        #adds a new node to the list, amakes it the new head and links it to the former head
        new_node = Node.new(value)
        new_node.next_node = @head

        @head = new_node
    end

    def remove
        @head = @head.next_node
    end
end

【问题讨论】:

你不喜欢它什么? (除了它不起作用) Sergio,我粘贴了错误版本的 SLL 类。它现在应该可以工作了。我不喜欢它的是我没有一个好的遍历或“find_node”方法。关于如何以有效的方式从头开始实施的任何想法? 在链表中查找总是O(n)。只需遍历数组即可。 取决于你认为什么是“有效的”。访问一个节点是 O(N)。 @screenmutt:那就是作弊,什么也没做。你两全其美。由于您假装它是一个链表,因此您获得的界面有限。而且因为它是一个数组,所以不会有 O(1) 次插入。 【参考方案1】:

我假设你是为了自学而实现这个,否则它没有任何意义。只需使用Array,这是 ruby​​ 的列表实现。

在我看来,你应该用 C 来编写这些练习,因为实现链表的重点是更接近机器并更好地理解它是如何工作的。 C 是完成这项任务的更好工具,了解计算机的工作原理。

这是在大学教授经典链表的方式:O(n) 插入,O(n) 搜索,O(n) 删除。之后,大多数书籍都讨论了关于它的改进。

我在脑海中写了一篇文章,根本没有对其进行任何测试,但这应该会给你一个想法,并希望能通过练习来纠正你发现的任何错误。 (更新:@Nitesh 发现了一个错字,并修复了它。希望现在可以进行一些测试)。

class Node
    attr_accessor :value, :next

    def initialize(value = nil)
        @value = value
    end

    def to_s
        @value
    end
end

class SinglyLinkedList

    attr_accessor :head

    def initialize(first_value=nil)
      @head = Node.new(first_value) if first_value
    end

    def add(value)
      if head.nil?
        head = Node.new(value)
      else
        current_node = @head
        while current_node.next
          current_node = current_node.next
        end
        current_node.next = Node.new(value)
      end
    end

    def find(value)
      current_node = head
      while current_node != nil
        return current_node if current_node.value == value
        current_node = current_node.next
      end
      nil
    end

    def remove(value)
      if head.value == value
        head = head.next
      else
        current_node = head.next
        prev_node = head
        while current_node
          if current_node.value == value
            prev_node.next = current_node.next
            return true
          end
          prev_node = current_node
          current_node = current_node.next
        end
        nil
      end
    end
end

【讨论】:

谢谢!我喜欢你在这方面的改进。我确实这样做是为了自学,并在 Ruby 中制作一个小型 Rub​​y 库,其中包含额外的数据结构供个人使用和参考。 缺少 '@' 用于在添加、删除方法中设置成员变量。本来只是编辑答案,但 SO 不喜欢 2 个字符的编辑。【参考方案2】:

我最近遇到了一个有趣的链表实现(不知道不幸在哪里),Node 类实现了插入和删除方法。以下是双链表的Node 类,但同样的原则也适用于单链表。

class Node
  protected
  attr_writer :prev, :next

  public
  attr_reader :value, :prev, :next

  def initialize(value)
    @value = value
  end

  def remove
    @prev.next = @next if @prev
    @next.prev = @prev if @next
    @next = @prev = nil
  end

  def insert_after(node)
    remove

    @next = node.next
    @next.prev = self if @next
    @prev = node
    node.next = self
  end
end

我发现这个实现很有趣,因为我发现它非常通用。只需 Node 即可开始构建和操作列表。

head = Node.new(1)

middle = Node.new(2)
middle.insert_after(head)

tail = Node.new(3)
tail.insert_after(middle)

middle.remove

在此实现的基础上,您可以构建一个更高级且易于理解的 API。以下是LinkedListDeque 的更完整版本。列表本身是Node 的子类,并链接到列表的头部和尾部。

class ListNode < Node
  protected :remove # prevent incorrect access to Node methods

  def initialize
    @next = @prev = self
  end

  def head
    @next unless @next == self
  end

  def tail
    @prev unless @prev == self
  end

  def add_to_head(node)
    node.insert_after(self)
  end

  def add_to_tail(node)
    node.insert_after(self.prev)
  end

  def each(&block)
    return enum_for(:each) unless block_given?
    node = @next
    while node != self
      yield node
      node = node.next
    end
  end

  def to_a
    each.collect |node| node.value 
  end
end

下面是如何在实践中使用它:

# create a new list
list = ListNode.new

# add some items to the head or tail
list.add_to_head(Node.new('Hello'))
list.add_to_tail(Node.new('World'))
list.to_a # => ["Hello", "World"]

# remove items from the head
list.head.remove
list.to_a # => ["World"]
list.head == list.tail # => true

# remove items from the tail
list.tail.remove
list.to_a # => []

更好的是each 方法,它允许我们使用任何Enumerable 函数来搜索和遍历节点:

list = ListNode.new
list.add_to_head(Node.new(1))
list.add_to_head(Node.new(2))
list.add_to_head(Node.new(3))
list.add_to_head(Node.new(4))

# list each value
list.each |node| puts node.value 

# select all nodes with values greater than 2
list.each.select |node| node.value > 2 

# find the first node with a value of 4
list.each.find |node| node.value == 4 

【讨论】:

以上是关于ruby 中没有扩展数组的最佳链表?的主要内容,如果未能解决你的问题,请参考以下文章

链表扩展

#yyds干货盘点# 有序链表的基本用法

JS中数据结构之链表

JavaScript数据结构——链表的实现

链表都有哪些优点和缺点?

数据结构与算法JavaScript描述——链表