Objective: Create a LinkedList In Swift 5 and Xcode Playgrounds.

My name is Maxwell Nelson and you can find me on maxcodes.io or maxnelson.io

What is a LinkedList?

A LinkedList is a collection of nodes, each node containing data, and pointers to the previous and next node in the collection. This differs from say, an array, in that a LinkedList’s data is not ordered by a node’s physical placement in memory but instead which other nodes it is connected to.

How do nodes connect?

Nodes connect via pointers to other nodes. Here is a diagram of a singly linked list showing each node connected via a pointer to a node object.

Singly Linked List with Pointers to the Next Node in a LinkedList

However, we will be creating a Doubly LinkedList whos nodes contain not only a next node pointer but also a previous node pointer. This diagram illustrates what a doubly linked list would look like.

Doubly Linked List with Pointers to the Next Node and Previous Node in a LinkedList

The Head and The Tail

The first and last nodes are referred to as the head and the tail, respectively.

-Linked List Diagram illustrating the head and tail nodes with nil pointers

The Head’s previous node pointer is nil because no node comes before it.

because no node comes before it. The Tail’s next node pointer is nil because no node comes after it.

Performance ***

Read about the performance of linked list compared to an array in this here article: https://beginnersbook.com/2013/12/difference-between-arraylist-and-linkedlist-in-java/

Let’s Start Coding.

gameplan

step 1 — create a Node class

step 2 — create a LinkedList class

step 3 — provide a last Node

step 4 — add the ability to append Nodes

step 5 — fetch the number Nodes in a LinkedList

step 6 — print each Node value from a LinkedList

step 7 —fetch a Node from a specified index

step 8 — insert a Node at a specific index

step 9 — remove a Node at a specific index

step 1 — create a Node structure

A Node has three properties

A value

a pointer to the next node in the list

node in the list a pointer to the previous node in the list

Generics

We want our list to be able to use any type, in other words, we want our list to be generic. So when defining our node structure, we make sure to give it a generic type by writing <T> after Node. More info on Generics can be found in the Swift docs here.

public class Node<T> {

var value: T

var next: Node?

weak var previous: Node?



public init(value: T) {

self.value = value

}

}

Things to note

The value is of type T, so whatever type we give it.

The next and previous nodes are optional since the head does not have a previous node and the tail does not have a next node.

Step 2 — Create a Linked List Class

public class LinkedList<T> {



private var head: Node?



public var isEmpty: Bool {

return head == nil

}



public var first: Node? {

return head

}

}

We declare LinkedList as a generic class by putting <T> after the title so we can use any value with our linked list. In this tutorial, we will be using String.

In the class, we provide an optional variable head of type Node. This will be the first value in our linked list upon declaration.

We also provide a boolean variable “isEmpty” that simply returns nil if there is no head. If head has a value then our list is indeed not empty and that conditional check will return false.

Step 3 — Provide a Last node to keep track of the tail.

public var last: Node? {

guard var node = head else {

return nil

}



while let next = node.next {

node = next

}

return node

}

We can get access to the last Node in our list by first checking if the head even has a value. By doing this, if the head is nil we can immediately communicate that there is indeed no last node given there isn’t even a head to begin with.

If the head does have a value then we need to check it’s next node to see if it has a value and if it does, check it’s next value to see if it has a value and so on until there is no next value on the final node.next Node.

When the while loop fails, we will know we have reached the end of our list and we can return the last node.

Step 4 — Appending Nodes

we can add data to our list by appending it.

public func append(value: T) {

let newNode = Node(value: value)

if let lastNode = last {

newNode.previous = lastNode

lastNode.next = newNode

} else {

head = newNode

}

}

create a new Node from the value passed in

check if we have a last node

if we do have a last node, it’s previous is now equal to the last node, and it’s next is now equal to the new node.

look at this diagram to better understand what is going on here.

appending a new node

step 5 — counting the nodes

public var count: Int {

guard var node = head else {

return 0

}



var count = 1

while let next = node.next {

node = next

count += 1

}

return count

}

first check if head has a value, if it does not we simply return 0

if head does have a value then we automatically know there is at least one value, so when we declare count we start at 1

use a while loop to traverse our LinkedList until there is no node.next just like we did to reach the end of our list when looking for the last node.

When node.next no longer has a value we know we have reached the tail and we have our full list count, return the count.

Step 6 — Print The Contents of Our List

public var print: String {

var s = "["

var node = head

while node != nil {

s += "\(node!.value)"

node = node!.next

if node != nil { s += ", " }

}

return s + "]"

}

create a public variable called print.

set the current node to our head node

use a while loop to traverse our list. while node is not nil, it must have a value, add that value to our string

once node.next has a value we know we have reached the tail of our list and we can proceed to return the string of nodes.

step 7 — fetch a node at a given index

public func node(atIndex index: Int) -> Node {

if index == 0 {

return head!

} else {

var node = head!.next

for _ in 1..<index {

node = node?.next

if node == nil { //(*1)

break

}

}

return node!

}

}

check if the index is 0, if it is, then just return the head.

if index is not 0, then use a for loop to navigate through our nodes until we reach the index, then return that node if it is not nil.

if all else fails, just return the node

step 8— insert a new node at a specific index

public func insert(value: T, atIndex index: Int) {

let newNode = Node(value: value)

if index == 0 {

newNode.next = head

head?.previous = newNode

head = newNode

} else {

let previousNode = self.node(atIndex: index-1)

let nextNode = previousNode.next



newNode.previous = previousNode

newNode.nextNode = previousNode.next

previousNode.next = newNode

nextNode?.previous = newNode

}

}