|
| 1 | +# Definition for a Node. |
| 2 | +class Node: |
| 3 | + def __init__(self, val=None, next=None): |
| 4 | + self.val = val |
| 5 | + self.next = next |
| 6 | + |
| 7 | + |
| 8 | +class Solution: |
| 9 | + # These are the cases: |
| 10 | + # 1. No nodes. (insert head) |
| 11 | + # 2. At least two different nodes. |
| 12 | + # - curr.val <= insertVal <= curr.next.val (insert to curr.next) |
| 13 | + # - curr.val > curr.next.val (insert to curr.next) |
| 14 | + # 3. All nodes same. (insert to head.next) |
| 15 | + # |
| 16 | + # Time Complexity: O(n), Two Pass. |
| 17 | + # Space Complexity: O(1). |
| 18 | + def insert(self, head: "Node", insertVal: int) -> "Node": |
| 19 | + if head is None: |
| 20 | + head = Node(val=insertVal) |
| 21 | + head.next = head |
| 22 | + return head |
| 23 | + |
| 24 | + curr = head |
| 25 | + while True: |
| 26 | + if curr.val <= insertVal <= curr.next.val: |
| 27 | + curr.next = Node(val=insertVal, next=curr.next) |
| 28 | + return head |
| 29 | + curr = curr.next |
| 30 | + if curr == head: |
| 31 | + break |
| 32 | + |
| 33 | + curr = head |
| 34 | + while True: |
| 35 | + if curr.val > curr.next.val: |
| 36 | + curr.next = Node(val=insertVal, next=curr.next) |
| 37 | + return head |
| 38 | + curr = curr.next |
| 39 | + if curr == head: |
| 40 | + break |
| 41 | + |
| 42 | + head.next = Node(val=insertVal, next=head.next) |
| 43 | + return head |
| 44 | + |
| 45 | + |
| 46 | +class OfficialSolution: |
| 47 | + def insert(self, head: Node, insertVal: int) -> Node: |
| 48 | + """ |
| 49 | + == Approach 1: Two-Pointers Iteration == |
| 50 | +
|
| 51 | + == Intuition == |
| 52 | + As simple as the problem might seem to be, it is actually not trivial to write |
| 53 | + a solution that covers all cases. |
| 54 | + Often the case for the problems with linked list, one could apply the approach |
| 55 | + of Two-Pointers Iteration, where one uses two pointers as surrogate to traverse |
| 56 | + the linked list. |
| 57 | + One of the reasons of having two pointers rather than one is that in singly |
| 58 | + linked list one does not have a reference to the precedent node, therefore we |
| 59 | + keep an additional pointer which points to the precedent node. |
| 60 | + For this problem, we iterate through the cyclic list using two pointers, namely |
| 61 | + prev and curr. When we find a suitable place to insert the new value, we insert |
| 62 | + it between the prev and curr nodes. |
| 63 | +
|
| 64 | + == Algorithm == |
| 65 | + First of all, let us define the skeleton of two pointers iteration algorithm as |
| 66 | + follows: |
| 67 | + - As we mentioned in the intuition, we loop over the linked list with two |
| 68 | + pointers (i.e. prev and curr) step by step. The termination condition of |
| 69 | + the loop is that we get back to the starting point of the two pointers. |
| 70 | + (i.e. prev == head). |
| 71 | + - During the loop, at each step, we check if the current place bounded by |
| 72 | + the two pointers is the right place to insert the new value. |
| 73 | + - If not, we move both pointers one step forwards. |
| 74 | +
|
| 75 | + Now the tricky part of this problem is to sort out different cases that our |
| 76 | + algorithm should deal with within the loop, and then design a concise logic to |
| 77 | + handle them sound and properly. Here we break it down into three general cases. |
| 78 | +
|
| 79 | + Case 1. The value of new node sits between the minimal and maximal values of |
| 80 | + the current list. As a result, it should be inserted within the list. |
| 81 | + The condition is to find the place that meets the constraint of: |
| 82 | + prev.val <= insertVal <= curr.val. |
| 83 | +
|
| 84 | + Case 2. The value of new node goes beyond the minimal and maximal values of the |
| 85 | + current list, either less than the minimal value or greater than the maximal |
| 86 | + value. In either case, the new node should be added right after the tail node ( |
| 87 | + i.e. the node with the maximal value of the list). |
| 88 | + Firstly, we should locate the position of the tail node, by finding a descending |
| 89 | + order between the adjacent, i.e. the condition of prev.val > curr.val, since the |
| 90 | + nodes are sorted in ascending order, the tail node would have the greatest value |
| 91 | + of all nodes. |
| 92 | +
|
| 93 | + Case 3. Finally, there is one case that does not fall into any of the above two |
| 94 | + cases. This is the case where the list contains uniform values. |
| 95 | + Though not explicitly stated in the problem description, our sorted list can |
| 96 | + contain some duplicate values. And in the extreme case, the entire list has only |
| 97 | + one single unique value. |
| 98 | +
|
| 99 | + The above three cases cover the scenarios within and after our iteration loop. |
| 100 | + There is however one minor corner case we still need to deal with, where we have |
| 101 | + an empty list. This, we could easily handle before the loop. |
| 102 | +
|
| 103 | + == Complexity Analysis == |
| 104 | + - Time Complexity: O(n), where N is the size of the list. In the worst case, we |
| 105 | + would iterate through the entire list. |
| 106 | + - Space Complexity: O(1). It is a constant space solution. |
| 107 | +
|
| 108 | + == Own Comments == |
| 109 | + It is exactly similar to my solution, with |
| 110 | + insertVal >= prev.val or insertVal <= curr.val |
| 111 | + making it a one pass solution. |
| 112 | + """ |
| 113 | + if head is None: |
| 114 | + newNode = Node(val=insertVal, next=None) |
| 115 | + newNode.next = newNode |
| 116 | + return newNode |
| 117 | + |
| 118 | + prev, curr = head, head.next |
| 119 | + toInsert = False |
| 120 | + |
| 121 | + while True: |
| 122 | + if prev.val <= insertVal <= curr.val: |
| 123 | + # Case #1 |
| 124 | + toInsert = True |
| 125 | + elif prev.val > curr.val: |
| 126 | + # Case #2. Where we locate the tail element |
| 127 | + # 'prev' points to the tail, i.e. the largest element |
| 128 | + if insertVal >= prev.val or insertVal <= curr.val: |
| 129 | + toInsert = True |
| 130 | + |
| 131 | + if toInsert: |
| 132 | + prev.next = Node(val=insertVal, next=curr) |
| 133 | + return head |
| 134 | + |
| 135 | + prev, curr = curr, curr.next |
| 136 | + # loop condition |
| 137 | + if prev == head: |
| 138 | + break |
| 139 | + |
| 140 | + # Case #3. |
| 141 | + # did not insert the node in the loop |
| 142 | + prev.next = Node(val=insertVal, next=curr) |
| 143 | + return head |
0 commit comments