---
# System prepended metadata

title: Design Singliy Linked List
tags: [Leetcode, Python]

---

# Design Singly Linked List

Design a Singly Linked List class.

Your LinkedList class should support the following operations:

* LinkedList() will initialize an empty linked list.
* int get(int i) will return the value of the ith node (0-indexed). If the index is out of bounds, return -1.
* void insertHead(int val) will insert a node with val at the head of the list.
* void insertTail(int val) will insert a node with val at the tail of the list.
* int remove(int i) will remove the ith node (0-indexed). If the index is out of bounds, return false, otherwise return true.
* int[] getValues() return an array of all the values in the linked list, ordered from head to tail.

Example 1:
> Input: 
> ["insertHead", 1, "insertTail", 2, "insertHead", 0, "remove", 1, "getValues"]
> 
> Output:
> [null, null, null, true, [0, 2]]


Example 2:
> Input:
> ["insertHead", 1, "insertHead", 2, "get", 5]
> 
> Output:
> [null, null, -1]

**Note:**

* The index int i provided to get(int i) and remove(int i) is guranteed to be greater than or equal to 0.



---

## 1. Create a ListNode

```python
class ListNode:
    def __init__(self, val):
        self.val = val
        self.next = None
```

## 2. Create a Singly Linked List

Note: initialize the linked list with a **dummy node(sentinel node)** will make edge cases easier

```python
class SinglyLinkedList:
    
    def __init__ (self):
        self.head = ListNode(-1) # sentinel node
        self.tail = self.head
```

## 3. get i-th node

```python
    def get(self, index: int) -> int:
        curr = self.head.next
        i = 0
        # iteration
        while curr:
            # found
            if i == index:
                return curr.val
            
            # next node and increment i
            i += 1
            curr = curr.next
        
        # not found
        return -1
        
```
Steps:
1. create a pointer(curr), and points to the head.next
2. initiate variable i = 0
3. iteration through the list (standard start: **while curr:**)
4. if i == index, then the node is found. Return the value of the node.
5. iterate to the next node and increment i by 1
6. return -1 if not found


## 4. insertHead
```python
    def insertHead(self, val: int) -> None:
        new_node = ListNode(val)
        new_node.next = self.head.next
        self.head.next = new_node
        # handle self.tail
        # if list is empty before insertion
        if not new_node.next:
            self.tail = new_node
        
```
Steps:
1. create a new node
2. point new node's next to the head.next
3. move head to point to new node
4. handle edge case: if the list is empty before insertion, then new node should be tail. Therefore, if new node's next points to null, it means the node is the first node. Then, move the tail to point to the new node.


## 5. insertTail
```python
    def insertTail(self, val: int) -> None:
        self.tail.next = ListNode(val)
        self.tail = self.tail.next
```
easy, not explanation needed


## 6. remove
**This one is tricky, be careful**
```python
    def remove(self, index: int) -> bool:
        curr = self.head
        i = 0
        
        # start the iteration
        while i < index and curr:
            i += 1
            curr = curr.next
            
        if curr and curr.next:
            # if target(curr.next) is the last node
            if curr.next == self.tail:
                # set tail to curr(previous node)
                self.tail = curr
            
            # remove the node
            curr.next = curr.next.next
            
            return True
        
        return False # list out of bounds

```
Steps:
1. create a pointer that points to the previous node of every node (i.e. first point at the dummy(head))
2. initiate variable i = 0
3. start iteration. **Condition: i < index and curr exist**. The condition "i < index" will ensure curr pointer stop at the previous node before our target node.
4. execute iteration and increment i by 1
5. **check curr and curr.next exist**
6. **handle tail pointer: if target node is the tail**, then curr(the previous node) should be the new tail
**7. remove the node: curr.next = curr.next.next (should memorize this)**
8. return

## get values
```python
    def getValues(self) -> List[int]:
        result = []
        curr = self.head.next
        while curr:
            result.append(curr.val)
            curr = curr.next
            
        return result
```
easy, no explanation