Ruby链表add_at(index)方法功能异常,请求故障排查
Let’s walk through the problems in your add_at method—there are a few edge cases you haven’t accounted for that are throwing off your insertion behavior.
1. Inserting at index 0 (the list head) is broken
When you call add_at(0, 13), (index - 1).times evaluates to -1.times, which doesn’t run the loop at all. Your code tries to modify current.next_node (where current is the original head), but inserting at the start should make the new node the new head, with its next_node pointing to the old head. Right now, this case is completely ignored, so the new node never replaces the head.
2. Inserting at the end of the list doesn’t update the tail
If you insert at an index equal to the list’s length (e.g., inserting a 3rd element at index 2 in a 2-element list), your loop moves current to the tail node—but your code doesn’t update the LinkedList’s @tail reference. This means future add calls will break, and the new node won’t be recognized as the tail.
3. Empty list and out-of-bounds checks are incomplete
Calling add_at on an empty list will immediately throw an error, since current starts as nil. You also don’t handle cases where the index exceeds the list’s length properly.
Fixed Code for add_at
Here’s the revised method with all edge cases handled, plus a helper length method to make testing easier:
class Node attr_accessor :value, :next_node alias_method :next, :next_node # You can remove the duplicate `def next` below—this alias does the same thing def initialize(value, next_node = nil) @value = value @next_node = next_node end end class LinkedList attr_accessor :head, :tail def initialize @head = nil @tail = nil end def add(number) if @head.nil? new_node = Node.new(number) @head = new_node @tail = new_node else new_node = Node.new(number) @tail.next_node = new_node @tail = new_node end end def get(index) current = @head index.times do raise "Index out of bounds" if current.nil? current = current.next_node end raise "Index out of bounds" if current.nil? current.value end def add_at(index, item) # Handle inserting at the head (index 0) if index == 0 new_node = Node.new(item, @head) @head = new_node # If list was empty, update tail too @tail = new_node if @tail.nil? return end current = @head # Traverse to the node right before the insertion point (index - 1).times do raise "List not long enough" if current.nil? current = current.next_node end # Handle inserting at the tail if current == @tail new_node = Node.new(item) @tail.next_node = new_node @tail = new_node else # Handle inserting in the middle raise "List not long enough" if current.nil? new_node = Node.new(item, current.next_node) current.next_node = new_node end end # Helper method to check list length (useful for testing) def length count = 0 current = @head while current count += 1 current = current.next_node end count end end
Testing the Fixed Code
Running your original test case now produces the expected output:
list = LinkedList.new list.add(3) list.add(5) list.add_at(1, 11) list.add_at(0, 13) puts list.get(0) # 13 puts list.get(1) # 3 puts list.get(2) # 11 puts list.get(3) # 5
Key Fixes Explained
- Head insertion: We explicitly create a new node pointing to the old head, then update
@head. If the list was empty, we also set@tailto the new node. - Tail insertion: When inserting after the tail, we update
@tailto the new node to maintain the LinkedList’s tail reference. - Robust error handling: We added checks to prevent
nilreference errors and clearly raise exceptions for out-of-bounds indices.
内容的提问来源于stack exchange,提问作者Feber Castellon

