diff --git a/README.md b/README.md index 5eb4cd0..55882dc 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ For a glimpse into my journey and the projects I've been involved in, feel free - Checking Validity of a Sudoku Grid - Encoding and Decoding Functions for Strings - Finding the Longest Consecutive Sequence in an Array + - Linked Lists ## Technologies Used - [Hugo Static Site Generator](https://gohugo.io/) diff --git a/content/contents.md b/content/contents.md index 99bedea..6e79f42 100644 --- a/content/contents.md +++ b/content/contents.md @@ -28,13 +28,14 @@ summary: "Index of all content" - Data Structures and Algorithms - Time Complexity - Arrays, Strings, and HashMaps - - Rabin-Karp Substring Search - - Checking an Array for Duplicate Values - - Identifying Anagrams - - Finding Elements in an Array that Sum Up to a Target Value - - Group Anagrams in an Array - - Finding Most Frequent Elements in an Array - - Building a Product Array without the Element Itself - - Checking Validity of a Sudoku Grid - - Encoding and Decoding Functions for Strings - - Finding the Longest Consecutive Sequence in an Array + * Rabin-Karp Substring Search + * Checking an Array for Duplicate Values + * Identifying Anagrams + * Finding Elements in an Array that Sum Up to a Target Value + * Group Anagrams in an Array + * Finding Most Frequent Elements in an Array + * Building a Product Array without the Element Itself + * Checking Validity of a Sudoku Grid + * Encoding and Decoding Functions for Strings + * Finding the Longest Consecutive Sequence in an Array + - Linked Lists diff --git a/content/posts/dsa/is-valid-sudoku/index.md b/content/posts/dsa/is-valid-sudoku/index.md index 70cc588..fb2cc2e 100644 --- a/content/posts/dsa/is-valid-sudoku/index.md +++ b/content/posts/dsa/is-valid-sudoku/index.md @@ -3,7 +3,7 @@ author: "Avnish" title: "Checking the Validity of a Sudoku Grid" date: "2023-11-02" description: "Implementing an isValidSudoku function that takes a matrix representing a Sudoku grid as inputs and returns true if it is valid and false otherwise" -tags: ["data-structures", "arrays", "sudoku", "hashmaps", "go", "neetcode-150", "leetcode-medium"] +tags: ["data-structures", "arrays", "sudoku", "hashmaps", "go", "neetcode-150", "leetcode-medium", "nested-arrays"] categories: ["Data Structures"] series: ["Data Structures and Algorithms"] aliases: diff --git a/content/posts/dsa/linked-lists/index.md b/content/posts/dsa/linked-lists/index.md new file mode 100644 index 0000000..820aa4b --- /dev/null +++ b/content/posts/dsa/linked-lists/index.md @@ -0,0 +1,624 @@ +--- +author: "Avnish" +title: "Linked Lists" +date: "2023-11-17" +description: "A Linked List uses dynamic allocation of memory to store sequential data" +tags: ["data-structures", "linked-lists", "doubly-linked-lists", "circular-linked-lists", "go"] +categories: ["Data Structures"] +series: ["Data Structures and Algorithms"] +aliases: ["linked-lists"] +ShowToc: true +TocOpen: false +comments: false +cover: + image: "linked-list-cover.png" + linkFullImages: true + alt: "Linked Lists" + caption: "" + relative: false + responsiveImages: false +math: true +--- + +The linked list data structure is used to store sequential data using *nodes*. A **node** contains a value and the memory address of the next node. + +

Linked List

+ +Memory for a new node is allocated dynamically i.e. nodes are stored in the next available memory location. Unlike arrays where contiguous blocks of memory are allocated during declaration. + +The first element in a linked list is marked by its *head* pointer. + +Here is an implementation of Linked List in Go + +```Go +type Node struct{ + // Integer value + value int + + // Pointer to the next node + next *Node +} + +type LinkedList struct{ + // Pointer to the head node + head *Node +} +``` + +# Linked List Operations +## Accessing a Value +In arrays, we can access an element using its index. For linked lists we have to iterate over all the previous elements to reach the element specified by the index, so to access the 7th element in a linked list we have to iterate over the first 6 elements. + +

Accessing a value in a Linked List

+ +This operation has linear time complexity ($O(n)$) in the worst-case scenario (accessing the last element). + +```Go +package main + +import "fmt" + +type Node struct{ + value int + next *Node +} + +type LinkedList struct{ + head *Node +} + +func main(){ + // Creating a LinkedList and its head node + var linkedList LinkedList + linkedList.head = &(Node{value:2, next:nil}) + + // Accessing the head of LinkedList + fmt.Println("Head node of the linked list:", *(linkedList.head)) + + // Adding a new nodes after head + linkedList.head.next = &(Node{value:7, next:nil}) + linkedList.head.next.next = &(Node{value:8, next:nil}) + linkedList.head.next.next.next = &(Node{value:9, next:nil}) + + // Accessing the node at a specific location + index := 3 + + // Iterator variable to store the current node + var temp *Node + + // Starting from head + temp = linkedList.head + + // Until the value of the index is 0 + // increment the temp to the next node + for ;index>0;index--{ + temp = temp.next + } + + // Display the value at the index + fmt.Println("Value at index", index, "is:", temp.value) +} +``` +## Inserting an Element +Before we insert an element in a linked list, we have to allocate memory for the new node. + +### Inserting an Element (at the beginning) +The first element in a linked list is decided by the value of the *head* pointer. So we can reassign the `head` to point to a new node containing the value to be inserted. + +

Inserting a value at the start of a Linked List

+ +Since we are only modifying the head of the linked list, this is a constant time complexity ($O(1)$) operation. + +```Go +package main + +import "fmt" + +type Node struct{ + value int + next *Node +} + +type LinkedList struct{ + head *Node +} + +func (ll *LinkedList) insertAtBeginning(input int){ + // Create a new node with the input value + newNode := &(Node{value: input, next:nil}) + + // Set the current head of the linked list + // as the next node + newNode.next = (ll.head) + + // Point the head to newNode + ll.head = newNode +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value:2, next:nil}) + linkedList.head.next = &(Node{value:7, next:nil}) + linkedList.head.next.next = &(Node{value:8, next:nil}) + linkedList.head.next.next.next = &(Node{value:9, next:nil}) + + fmt.Println("Head node of the linked list:", *(linkedList.head)) + fmt.Println("2nd node of the linked list:", *(linkedList.head.next)) + fmt.Println("3rd node of the linked list:", *(linkedList.head.next.next)) + + linkedList.insertAtBeginning(90) + fmt.Println("After inserting a new element at the start of the Linked List") + fmt.Println("Head node of the linked list:", *(linkedList.head)) + fmt.Println("2nd node of the linked list:", *(linkedList.head.next)) + fmt.Println("3rd node of the linked list:", *(linkedList.head.next.next)) +} + +// Output +// Head node of the linked list: {2 0xc00009e240} +// 2nd node of the linked list: {7 0xc00009e250} +// 3rd node of the linked list: {8 0xc00009e260} +// After inserting a new element at the start of the Linked List +// Head node of the linked list: {90 0xc00009e230} +// 2nd node of the linked list: {2 0xc00009e240} +// 3rd node of the linked list: {7 0xc00009e250} +``` + +### Inserting an Element (at the end) +To insert an element at the end of a linked list we first have to find the last element. For that, we have to iterate over the complete list, which will take $O(n)$ time. + +

Inserting a value at the end of a Linked List

+ +```Go +package main + +import "fmt" + +type Node struct{ + value int + next *Node +} + +type LinkedList struct{ + head *Node +} + +func (ll *LinkedList) insertAtEnd(input int){ + // Assigning a temp pointer to the head of the linked list + temp := ll.head + + // Until we reach the node with no next value + for ;temp.next!=nil;{ + + // Increment temp to the next node in the linked list + temp=temp.next + } + + // Add a new node to the linked list + temp.next = &Node{value: input, next:nil} +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value:2, next:nil}) + linkedList.head.next = &(Node{value:7, next:nil}) + + fmt.Println("Head node:", *(linkedList.head)) + fmt.Println("2nd node:", *(linkedList.head.next)) + + linkedList.insertAtEnd(90) + fmt.Println("After inserting a new element at the end of the Linked List") + fmt.Println("Head node:", *(linkedList.head)) + fmt.Println("2nd node:", *(linkedList.head.next)) + fmt.Println("3rd node:", *(linkedList.head.next.next)) + + linkedList.insertAtEnd(91) + fmt.Println("4th node:", *(linkedList.head.next.next.next)) +} + +// Output +// Head node: {2 0xc000014270} +// 2nd node: {7 } +// After inserting a new element at the end of the Linked List +// Head node: {2 0xc000014270} +// 2nd node: {7 0xc0000142a0} +// 3rd node: {90 } +// 4th node: {91 } +``` + +If we modify the `LinkedList` data structure to add a `tail` pointer (to store the address of the last node), the time taken to complete this operation will be reduced to $O(1)$. + +### Inserting an Element (at a specific location) +To insert an element at a specific location in a linked list, we can use the same approach as `insertAtEnd` but we have to stop the loop just before we reach the specified location. + +

Inserting a value at a specific location in a Linked List

+ +```Go +package main + +import "fmt" + +type Node struct{ + value int + next *Node +} + +type LinkedList struct{ + head *Node +} + +func (ll *LinkedList) insertAtIndex(input int, index int){ + // Creating a new node with the input value + newNode := &(Node{value:input, next:nil}) + + if index==0{ + // Same as inserting a new node at the beginning + newNode.next = ll.head + ll.head = newNode + + } else { + // Starting temp iterator from the head of the linked list + temp := ll.head + + // While temp is not at the end + // AND i is smaller than the index + // Increment the temp to the next node + for i:=1;i newNode -> temp.next + newNode.next = temp.next + temp.next = newNode + } +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value:2, next:nil}) + linkedList.head.next = &(Node{value:7, next:nil}) + + fmt.Println("Head node:", *(linkedList.head)) + fmt.Println("2nd node:", *(linkedList.head.next)) + + linkedList.insertAtIndex(90, 0) + fmt.Println("After inserting a new element at 0th index") + fmt.Println("Head node:", *(linkedList.head)) + + linkedList.insertAtIndex(91, 1) + fmt.Println("After inserting a new element at 1st index") + fmt.Println("Head node:", *(linkedList.head)) + fmt.Println("2nd node:", *(linkedList.head.next)) + fmt.Println("3rd node:", *(linkedList.head.next.next)) + fmt.Println("4th node:", *(linkedList.head.next.next.next)) +} + +// Output +// Head node: {2 0xc000014280} +// 2nd node: {7 } +// After inserting a new element at 0th index +// Head node: {90 0xc000014270} +// After inserting a new element at 1st index +// Head node: {90 0xc0000142d0} +// 2nd node: {91 0xc000014270} +// 3rd node: {2 0xc000014280} +// 4th node: {7 } +``` + +The worst-case time complexity of this operation is $O(n)$. + +## Deleting an Element +We can remove an element from the linked list if we just reassign the pointers storing its location. + +### Deleting an Element (from the beginning) +To delete an element from the beginning we can just change the address stored in the `head` pointer to the next node. + +

Deleting a value from the start of a Linked List

+ +The time taken to complete this operation is independent of the size of the linked list, thus the time complexity will be $O(1)$. + +```Go +package main + +import "fmt" + +type Node struct { + value int + next *Node +} + +type LinkedList struct { + head *Node +} + +func (ll *LinkedList) deleteFromStart(){ + // Linked List's head will now point to the next node + ll.head = ll.head.next +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value: 2, next:nil}) + linkedList.head.next = &(Node{value:3, next:nil}) + + fmt.Println("Head node:", *linkedList.head) + fmt.Println("Second node:", *linkedList.head.next) + + linkedList.deleteFromStart() + fmt.Println("After deleting element from the start") + fmt.Println("Head node:", *linkedList.head) +} + +// Output +// Head node: {2 0xc00009e230} +// Second node: {3 } +// After deleting element from the start +// Head node: {3 } +``` +### Deleting an Element (from the end) +To delete an element from the end of a linked list we have to iterate over all the preceding elements. This will result in the worst-case time complexity of $O(n)$. + +

Deleting a value from the end of a Linked List

+ +```Go +package main + +import "fmt" + +type Node struct { + value int + next *Node +} + +type LinkedList struct { + head *Node +} + +func (ll *LinkedList) deleteFromEnd(){ + temp := ll.head + + if temp.next == nil { + + // If the linked list contains only one element + // Set the value of the head to nil + ll.head = nil + + } else { + + // Iterate over the linked list until you reach + // the second last element + for ;temp.next.next!=nil;{ + temp = temp.next + } + + // Set the next value of the second last element to nil + temp.next = nil + } +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value: 2, next:nil}) + linkedList.head.next = &(Node{value:3, next:nil}) + + fmt.Println("Head node:", linkedList.head) + fmt.Println("Second node:", linkedList.head.next) + + linkedList.deleteFromEnd() + fmt.Println("After deleting element from the end") + fmt.Println("Head node:", linkedList.head) + fmt.Println("Second node:", linkedList.head.next) +} + +// Output +// Head node: &{2 0xc000014280} +// Second node: &{3 } +// After deleting element from the end +// Head node: &{2 } +// Second node: +``` + +### Deleting an Element (from a specific location) +Similar to `insertAtIndex` we will iterate over the linked list and stop just before the specified location to change the references for its `next` pointer. + +

Deleting a value from a specific location in a Linked List

+ +In the worst-case scenario for this operation, we have to remove the last element, resulting in $O(n)$ time complexity. + +```Go +package main + +import "fmt" + +type Node struct { + value int + next *Node +} + +type LinkedList struct { + head *Node +} + +func (ll *LinkedList) deleteFromIndex(index int){ + temp := ll.head + if index == 0{ + + // If we have to remove the first node (head) + // then change its reference to the next node + ll.head = ll.head.next + + } else { + + // Exit the loop at the second last element or + // at index-1 + for i:=1;i} +// After deleting element from index: 2 +// Head node: &{2 0xc00009e240} +// Second node: &{3 0xc00009e260} +// Third node: &{9 } +// Fourth node: +// After deleting element from index: 0 +// Head node: &{3 0xc00009e260} +// Second node: &{9 } +``` + +## Displaying Linked List +We have to iterate over all the elements in a linked list to display them in the output. + +```Go +package main + +import "fmt" + +type Node struct { + value int + next *Node +} + +type LinkedList struct { + head *Node +} + +func (ll *LinkedList) display(){ + temp := ll.head + + // Since we have to reach the last element itself + // we will stop the loop only when the temp is nil + for ;temp!=nil;{ + fmt.Printf("%d->", temp.value) + temp = temp.next + } + + fmt.Println() +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value: 2, next:nil}) + linkedList.head.next = &(Node{value:3, next:nil}) + linkedList.head.next.next = &(Node{value: 5, next:nil}) + linkedList.head.next.next.next = &(Node{value: 9, next:nil}) + + linkedList.display() +} + +// Output +// 2->3->5->9-> +``` + +# Variations of a Linked List +## Doubly Linked List +We can add a `prev` pointer in the linked list node to store the address of the previous node. Although it increases the memory space allocated for each node, the time complexity of iterating in reverse (from any particular node) will be improved. + +

Doubly Linked List

+ +```Go +type Node struct{ + prev *Node + value int + next *Node +} + +type DoublyLinkedList struct{ + head *Node +} +``` + +## Circular Linked List +In a **circular linked list**, a node is referenced two or more times. Iterating over a circular linked list results in an infinite loop. + +

Circular Linked List

+ +```Go +package main + +import "fmt" + +type Node struct { + value int + next *Node +} + +type LinkedList struct { + head *Node +} + +func (ll *LinkedList) display(limit int){ + temp := ll.head + + // Loop will end upon reaching the last element + // or hitting the limit of elements + for ;limit>0 && temp!=nil;limit--{ + fmt.Printf("%d->", temp.value) + temp = temp.next + } + + fmt.Println() +} + +func main(){ + var linkedList LinkedList + linkedList.head = &(Node{value: 2, next:nil}) + linkedList.head.next = &(Node{value:3, next:nil}) + + newNode := Node{value:99, next:nil} + + linkedList.head.next.next = &newNode + newNode.next = &(Node{value: 9, next:nil}) + + linkedList.head.next.next.next = &(Node{value:6, next:nil}) + linkedList.head.next.next.next.next = &newNode + + linkedList.display(10) +} + +// Output +// 2->3->99->6->99->6->99->6->99->6-> +``` + +
+Thank you for taking the time to read this blog post! If you found this content valuable and would like to stay updated with my latest posts consider subscribing to my RSS Feed. + +# Resources +How Does a Linked List Work? A Beginner's Guide to Linked Lists diff --git a/content/posts/dsa/linked-lists/linked-list-access.png b/content/posts/dsa/linked-lists/linked-list-access.png new file mode 100644 index 0000000..5568122 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-access.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-circular.png b/content/posts/dsa/linked-lists/linked-list-circular.png new file mode 100644 index 0000000..38cda31 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-circular.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-cover.png b/content/posts/dsa/linked-lists/linked-list-cover.png new file mode 100644 index 0000000..be50316 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-cover.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-delete-end.png b/content/posts/dsa/linked-lists/linked-list-delete-end.png new file mode 100644 index 0000000..7d5509c Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-delete-end.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-delete-index.png b/content/posts/dsa/linked-lists/linked-list-delete-index.png new file mode 100644 index 0000000..a06604a Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-delete-index.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-delete-start.png b/content/posts/dsa/linked-lists/linked-list-delete-start.png new file mode 100644 index 0000000..2bfb035 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-delete-start.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-double.png b/content/posts/dsa/linked-lists/linked-list-double.png new file mode 100644 index 0000000..63e82ca Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-double.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-insert-end.png b/content/posts/dsa/linked-lists/linked-list-insert-end.png new file mode 100644 index 0000000..8187025 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-insert-end.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-insert-index.png b/content/posts/dsa/linked-lists/linked-list-insert-index.png new file mode 100644 index 0000000..b142d87 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-insert-index.png differ diff --git a/content/posts/dsa/linked-lists/linked-list-insert-start.png b/content/posts/dsa/linked-lists/linked-list-insert-start.png new file mode 100644 index 0000000..d809d98 Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list-insert-start.png differ diff --git a/content/posts/dsa/linked-lists/linked-list.png b/content/posts/dsa/linked-lists/linked-list.png new file mode 100644 index 0000000..5b1232d Binary files /dev/null and b/content/posts/dsa/linked-lists/linked-list.png differ