Natasha
Natasha

Reputation: 6893

bug in the remove function in my LinkedList in swift

I am trying to implement a generic linkedList API like in Java in swift.This has been done probably in hundreds of ways but my question is not on how to do it rather, on what is wrong with a part of my logic when removing an item whose index is zero. I know, there are alternate ways to achieve what I want and I did that but I need to understand the flaw in a particular logic. please have a look.

I have following functions for now.

var size : Int { get } //give the size of the list
func add(_ value: Item) //add items to the list
func removeAt(index: Int) // removes the item from the specified index
func print() //prints the list

My logic flaw is in the remove(index:) method only when I try to remove an item from the beginning of the list(index=0). My logic is to set the head's next node to be the head if there are more than 1 item in the list(list.size>0).

The following is the full implementation-

import Foundation

class Node<Item>{
    var value: Item
    var next: Node?
    
    init(value: Item) {
        self.value = value
        self.next = nil
    }
}

class LinkedList<Item> {
    var head: Node<Item>?
    
    public init(){
        self.head = nil
    }
    
    public init(_ value: Item){
        self.head = Node(value: value)
    }
    
    private func isEmpty()->Bool{
        if head == nil{
            return true
        }
        return false
    }
    
    var size : Int {
        guard var current = head else{
            return 0
        }
        var count = 1
        while current.next != nil {
            guard let next = current.next else {
                return count
            }
            count = count + 1
            current = next
        }
        return count
    }
    
    func add(_ value: Item){
        let node = Node(value: value)
        if isEmpty(){
            head = node
            return
        }
        guard let head = head else {
            return
        }
        var currentNode = head
        while currentNode.next != nil {
            guard let nextNode = currentNode.next else {
                break
            }
            currentNode = nextNode
        }
        currentNode.next = node
    }
    
    func removeAt(index: Int) {
        if isEmpty() || index >= self.size{
            return
        }
        guard var last = head, var secondLast = head else{
            return
        }
        // when index is zero and either the list has only one item or it has multiple items
        if index == 0{ //here is the bug
            if let next = last.next{
                last = next
            }else{
                head = nil
            }
            return
        }
        var count = 0
        while count < index{
            guard let next = last.next else{
                return
            }
            secondLast = last
            last = next
            count = count + 1
        }
        secondLast.next = last.next
    }
    
    func print(){
        guard var current = head else {
            return
        }
        if current.next == nil{ //if list contains only head
            Swift.print(current.value)
            return
        }
        while current.next != nil{
            Swift.print(current.value)
            guard let next = current.next else {
                return
            }
            current = next
        }
        Swift.print(current.value)
    }
}

Testing the code like -

let list = LinkedList<Int>()
for i in 0...5{
   list.add(i)
}
list.print()
let index = 0
list.removeAt(index: index)
print("After removing..")
list.print()

Output:

1
2
3
4
5
After removing..
1
2
3
4
5

Can anyone please points out my fault.

Upvotes: 0

Views: 111

Answers (2)

Natasha
Natasha

Reputation: 6893

Firstly, I really appreciate the time and effort @Shadowrun put into. However, the explanation still lack the reason as of why the code works when I am iterating the list and removing in between the list. The reason is List itself only holds one pointer, head which is referring to an address in memory. Sure the last node points to the same address but when I try to update update the head (index = 0) with the help of last, last is points to a different address which means it deviates from head as soon as it leaves the scope. While list has no clue that there was a change and list still holds the same pointer to address as of before.

Now when I am iterating from head(index>0), the last is same as head and with each iteration the last moves forward to the next node. I change the next pointer of any node at anytime, the next pointer gets updated for that node successfully. Here the starting node is same as head as last and head is pointing to the same address. So, for iteration over the list, it doesn't matter whether I start with head or last, the chain linking is same. Here head and last is same at the beginning. But for index = 0, as list's head, itself needs to be changed, making last pointing to a different node won't work because list knows head and the head is not updated. So, the real reason is the list has no clue of how the node's are inter-related with one exception and that is the head.

It's not about assigning object or anything else. It's about list pointing to only one node and that is head.

Upvotes: 0

Shadowrun
Shadowrun

Reputation: 3867

The head of the list becomes the next item, if there is one. If the next item is nil, then the head of the list is also nil, like so:

    if index == 0 { //here is the bug
        head = head?.next
        return
    }

This code:

if let next = last.next{
    last = next
}else{
    head = nil
}

In the first branch, you return without changing "head", so the head of the list remains unchanged.

suppose head is at address 100 and the second item in the list is at address 200

let list = LinkedList<Int>()
list's head -> 100

in remove at index 0 we set last to head, and secondLast also to head

last -> 100[ |next -> 200]
secondLast -> 100[ |next -> 200]

then we say

if let next = last.next { last = next } return

Now last points to 200:

last -> 200[ |next -> 300]

But list's head hasn't changed

list's head -> 100[ |next -> 200]
secondLast -> 100[ |next -> 200]

Last goes out of scope, when we return, no changes to the list structure (in the heap) have happened.

In the working branch (where index > 0):

    while count < index{
        guard let next = last.next else{
            return
        }
        secondLast = last
        last = next
        count = count + 1
    }
    secondLast.next = last.next

we go over the loop manipulating secondLast and last, and at some point we then reach the line

secondLast.next = ...

So then we actually assign to the object, we mutate the object in the heap, it's next pointer, we go from something like:

secondLast -> 100[ |next -> 200]

secondLast -> 100[ |next -> 300]

So now the list itself has changed, in the heap, the object at address e.g. 100 has a next pointer that is pointing to some object, e.g. at address 300.

Upvotes: 1

Related Questions