Swift Linked List

Filed Under: Swift

In this tutorial, we’ll be discussing and implementing Linked List data structures using Swift.

What is LinkedList?

LinkedList is a type of Data structure that holds the data linearly/sequentially.

Typically, we call every element of a LinkedList as a Node.

So every Node consists of the value and a reference to the next element.

Following diagram illustrates a Node.

swift linked list node

A LinkedList is simply a list of nodes where every node stores a reference for it’s next node.

The first Node is typically referred to as the HEAD. The LinkedList finishes when the NEXT of the last node is a Nil.

swift linked list flow

A Doubly LinkedList stores the reference for it’s previous and next nodes both.

In the following section, we’ll create Node structure and do various operations on LinkedList such as:

  • appending new element
  • Size of the LinkedList
  • inserting an element at a position
  • deleting element
  • printing the LinkedList forward and backward
  • Retrieving the Node from a particular position

Open your Xcode Playground and lets Swift!

Create a Swift LinkedList Node

We can create a Node of Generic type as shown below:


public class SNode<T> {
    var value: T
    var next: SNode<T>?
    
    init(value: T) {
        self.value = value
    }
}

SNode represents an instance of Node of the LinkedList.

Let’s create a class SingleLinkedList<T> in which we’ll create and modify our LinkedList


class SingleLinkedList<T> {
    
    var head: SNode<T>? // head is nil when list is empty
    
    public var isEmpty: Bool {
        return head == nil
    }
    
    public var first: SNode<T>? {
        return head
    }

isEmpty is a property that checks if the LinkedList is empty or not. If the head is nil the LinkedList would be empty.

Appending Elements in a Swift LinkedList

Add the following append function in the above class.


public func append(value: T) {
        let newNode = SNode(value: value)
        if var h = head {
            while h.next != nil {
                h = h.next!
            }
            h.next = newNode
            
        } else {
            head = newNode
        }
    }

The append function adds a new Node to the end of the list. For this, we traverse to the end of the LinkedList using a temporary Node reference of the header named h

When the next reference of h is nil, it means we are at the end of the LinkedList. We set the newNode here by setting the next of the last Node to it.

Inserting an Element at a Position

To insert an element at a position, we need to :
set the previous Node NEXT reference to the new element. Set the NEXT of the new element to the current element present at that point.

Add the following function insert to our above LinkedList class.


func insert(data : T, at position : Int) {
        let newNode = SNode(value: data)
        
        if (position == 0) {
            newNode.next = head
            head = newNode
        }
        else {
            var previous = head
            var current = head
            for _ in 0..<position {
                previous = current
                current = current?.next
            }
            
            newNode.next = previous?.next
            previous?.next = newNode
        }
    }
    

Deleting a Node from the Swift LinkedList

To delete a Node we need to set the reference of the previous Node to the NEXT of the node to be deleted.

Add the following function in the above class.


func deleteNode(at position: Int)
    {
        if head == nil{
        return
        }
        var temp = head
        
        if (position == 0)
        {
            head = temp?.next;   // Change head
            return
        }
    
        for _ in 0..<position-1 where temp != nil {
            temp = temp?.next;
        }

        if temp == nil || temp?.next == nil{
           return
        }

        let nextToNextNode = temp?.next?.next
        
        temp?.next = nextToNextNode
    }

Printing Elements

To print the elements we need to traverse the LinkedList until we reach the last element and print the elements along the way.

To print the elements in reverse order we use recursive functions!


func printList() {
        var current: SNode? = head
        //assign the next instance
        while (current != nil) {
            print("LL item is: \(current?.value as? Int ?? 0)")
            current = current?.next
        }
    }
    
    
    func printReverseRecursive(node: SNode<T>?) {
    if node == nil{
    return
    }
    printReverseRecursive(node: node?.next)
    print("LL item is: \(node?.value as? Int ?? 0)")
    }
    
    func printReverse() {
        printReverseRecursive(node: first)
        
    }

Clubbing all the above operations in our class would make our class look like this:


class SingleLinkedList<T> {
    
    var head: SNode<T>? // head is nil when list is empty
    
    public var isEmpty: Bool {
        return head == nil
    }
    
    public var first: SNode<T>? {
        return head
    }
    
    public func append(value: T) {
        let newNode = SNode(value: value)
        if var h = head {
            while h.next != nil {
                h = h.next!
            }
            h.next = newNode
            
        } else {
            head = newNode
        }
    }
    
    func insert(data : T, at position : Int) {
        let newNode = SNode(value: data)
        
        if (position == 0) {
            newNode.next = head
            head = newNode
        }
        else {
            var previous = head
            var current = head
            for _ in 0..<position {
                previous = current
                current = current?.next
            }
            
            newNode.next = previous?.next
            previous?.next = newNode
        }
    }
    
    func deleteNode(at position: Int)
    {
        if head == nil{
        return
        }
        var temp = head
        
        if (position == 0)
        {
            head = temp?.next
            return
        }
    
        for _ in 0..<position-1 where temp != nil {
            temp = temp?.next
        }

        if temp == nil || temp?.next == nil{
           return
        }

        let nextToNextNode = temp?.next?.next
        
        temp?.next = nextToNextNode
    }
    
    func printList() {
        var current: SNode? = head
        //assign the next instance
        while (current != nil) {
            print("LL item is: \(current?.value as? Int ?? 0)")
            current = current?.next
        }
    }
    
    
    func printReverseRecursive(node: SNode<T>?) {
    if node == nil{
    return
    }
    printReverseRecursive(node: node?.next)
    print("LL item is: \(node?.value as? Int ?? 0)")
    }
    
    func printReverse() {
        printReverseRecursive(node: first)
        
    }
}

Let’s add, insert, delete elements from the LinkedList.


let ll = SingleLinkedList<Int>()
ll.append(value: 1)
ll.append(value: 2)
ll.append(value: 4)
ll.insert(data: 5, at: 0)
ll.insert(data: 10, at: 1)
ll.printList()
ll.deleteNode(at: 0)
print("Printing Reverse:")
ll.printReverse()

Following is the output:
swift linked list implementation output

Doubly LinkedList

Let’s create a Node for our Doubly LinkedList.


public class DNode<T> {
  var value: T
  var next: DNode<T>?
  weak var previous: DNode<T>?

  init(value: T) {
    self.value = value
  }
}

To prevent memory cycles we’ve set the previous reference as weak.


public class DoublyLinkedList<T> {
  var head: DNode<T>?
  private var tail: DNode<T>?

  public var isEmpty: Bool {
    return head == nil
  }

  //first
  public var first: DNode<T>? {
    return head
  }

  //last
  public var last: DNode<T>? {
    return tail
  }
  //Append node to LinkedList
  public func append(value: T) {
    let newNode = DNode(value: value)
    if let tailNode = tail {
      newNode.previous = tailNode
      tailNode.next = newNode
    } else {
      head = newNode
    }
    tail = newNode
  }
    
    func insert(node: DNode<T>, at index: Int) {
        if index == 0,
            tail == nil {
            head = node
            tail = node
        } else {
            guard let nodeAtIndex = nodeAt(index: index) else {
                print("Index out of bounds.")
                return
            }
            
            if nodeAtIndex.previous == nil {
                head = node
            }
            
            node.previous = nodeAtIndex.previous
            nodeAtIndex.previous?.next = node
        
            node.next = nodeAtIndex
            nodeAtIndex.previous = node
        }
    }

  //Find Node at Index
  public func nodeAt(index: Int) -> DNode<T>? {
    if index >= 0 {
      var node = head
      var i = index
      while node != nil {
        if i == 0 { return node }
        i -= 1
        node = node!.next
      }
    }
    return nil
  }
    
  public func removeAll() {
    head = nil
    tail = nil
  }

  //remove Node
  public func remove(node: DNode<T>) -> T {
    let previousNode = node.previous
    let nextNode = node.next

    if let prev = previousNode {
      prev.next = nextNode
    } else {
      head = nextNode
    }
    nextNode?.previous = previousNode

    if nextNode == nil {
      tail = previousNode
    }

    node.previous = nil
    node.next = nil
    
    return node.value
  }
    
    var count: Int {
        if (head?.value == nil) {
            return 0
        }
        else {
            var current: DNode? = head
            var x: Int = 1
            
            //cycle through the list of items
            while ((current?.next) != nil) {
                current = current?.next!
                x = x + 1
            }
            return x
        }
    }
    
    func printReverse() {
        var current: DNode? = tail;
        //assign the next instance
        while (current != nil) {
            print("DLL item is: \(current?.value as? String ?? "NA")")
            current = current?.previous
        }
    }
    
    func printForward() {
        var current: DNode? = head
        //assign the next instance
        while (current != nil) {
            print("DLL item is: \(current?.value as? String ?? "NA")")
            current = current?.next
        }
    }
    //remove from index
    func remove(at index: Int) {
        var toDeleteNode = nodeAt(index: index)
        guard toDeleteNode != nil else {
            print("Index out of bounds.")
            return
        }
        
        let previousNode = toDeleteNode?.previous
        let nextNode = toDeleteNode?.next
        
        if previousNode == nil {
            head = nextNode
        }
        
        if toDeleteNode?.next == nil {
            tail = previousNode
        }
        
        previousNode?.next = nextNode
        nextNode?.previous = previousNode
        
        toDeleteNode = nil
    }
}

In the doubly linked list, we take care of both the previous and next references.


let dd = DoublyLinkedList<String>()
dd.append(value: "Harry Potter")
dd.append(value: "Ron")
dd.append(value: "Hermione")
dd.append(value: "Hagrid")
dd.append(value: "Draco")
dd.append(value: "Snape")

//Run the following
print(dd.count)
dd.printReverse()
dd.remove(at: 1)
var dNode = DNode(value: "Harry Potter")
dd.remove(node: dNode)
print(dd.nodeAt(index: 4))
print(dd.nodeAt(index: 10))
dd.insert(node: dNode, at: 4)
dd.printForward()

The output we get from the above operations is:
swift doubly linked list example

This brings an end to this tutorial on LinkedList in Swift.

Download Swift LinkedList Playground code SwiftLinkedList.playground

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages