Skip to content

Commit

Permalink
Added swift
Browse files Browse the repository at this point in the history
  • Loading branch information
javadev committed Jun 24, 2024
1 parent 29f0ffc commit ca2f02e
Show file tree
Hide file tree
Showing 82 changed files with 5,237 additions and 315 deletions.
614 changes: 307 additions & 307 deletions README.md

Large diffs are not rendered by default.

66 changes: 65 additions & 1 deletion src/main/python/g0101_0200/s0142_linked_list_cycle_ii/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,68 @@ There is a cycle in a linked list if there is some node in the list that can be
* <code>-10<sup>5</sup> <= Node.val <= 10<sup>5</sup></code>
* `pos` is `-1` or a **valid index** in the linked-list.

**Follow up:** Can you solve it using `O(1)` (i.e. constant) memory?
**Follow up:** Can you solve it using `O(1)` (i.e. constant) memory?

To solve the problem, we can use Floyd's Tortoise and Hare algorithm, also known as the Floyd's Cycle Detection algorithm. This algorithm involves two pointers, one moving at twice the speed of the other. If there's a cycle in the linked list, these two pointers will eventually meet. Once they meet, we reset one pointer to the head of the list and move both pointers at the same speed. The point where they meet again will be the start of the cycle.

Here's how we can implement this algorithm in Python using a `Solution` class:

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

class Solution:
def detectCycle(self, head: ListNode) -> ListNode:
# Function to detect cycle and return the node where cycle starts

# Step 1: Initialize slow and fast pointers
slow = fast = head

# Step 2: Find the meeting point of the two pointers
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break

# If there's no cycle, return None
if not fast or not fast.next:
return None

# Step 3: Reset one pointer to the head and move both pointers at the same speed
slow = head
while slow != fast:
slow = slow.next
fast = fast.next

# Step 4: Return the node where the two pointers meet again (start of cycle)
return slow

# Example usage:
# Create linked list nodes
node1 = ListNode(3)
node2 = ListNode(2)
node3 = ListNode(0)
node4 = ListNode(-4)

# Connect nodes to form a cycle
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node2 # This creates a cycle

# Create an instance of Solution class
solution = Solution()

# Call detectCycle method to find the start of the cycle
cycle_start = solution.detectCycle(node1)

if cycle_start:
print("Tail connects to node index:", cycle_start.val)
else:
print("No cycle")
```

This solution has a time complexity of O(n) and uses O(1) extra space.
102 changes: 102 additions & 0 deletions src/main/python/g0101_0200/s0146_lru_cache/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,105 @@ The functions `get` and `put` must each run in `O(1)` average time complexity.
* <code>0 <= key <= 10<sup>4</sup></code>
* <code>0 <= value <= 10<sup>5</sup></code>
* At most 2<code> * 10<sup>5</sup></code> calls will be made to `get` and `put`.

To solve the problem and implement the LRU Cache, we can use a combination of a dictionary (hash map) and a doubly linked list. This approach allows us to achieve O(1) time complexity for both the `get` and `put` operations.

### Steps:

1. **Define a Doubly Linked List Node**:
- Define a class for the doubly linked list node with attributes `key`, `value`, `prev`, and `next`.

2. **Initialize the LRU Cache**:
- Initialize the LRU cache with a dictionary to store key-value pairs and two dummy nodes for the head and tail of the doubly linked list.
- Set `capacity` to the provided capacity.
- Initialize a `size` variable to keep track of the number of elements in the cache.

3. **Implement `get` Operation**:
- If the key exists in the cache, move the corresponding node to the front of the doubly linked list (indicating it was recently used) and return its value.
- If the key does not exist, return -1.

4. **Implement `put` Operation**:
- If the key exists in the cache, update its value and move the corresponding node to the front of the doubly linked list.
- If the key does not exist:
- If the cache is full (`size` equals `capacity`), remove the least recently used node (tail node) from the doubly linked list and the dictionary.
- Create a new node with the provided key and value, add it to the front of the doubly linked list, and insert the key-value pair into the dictionary.
- Update the `size` accordingly.

### Implementation:

```python
class LRUCache:
class ListNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None

def __init__(self, capacity: int):
self.capacity = capacity
self.cache = {}
self.head = self.ListNode()
self.tail = self.ListNode()
self.head.next = self.tail
self.tail.prev = self.head
self.size = 0

def _add_node(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node

def _remove_node(self, node):
prev_node = node.prev
next_node = node.next
prev_node.next = next_node
next_node.prev = prev_node

def _move_to_front(self, node):
self._remove_node(node)
self._add_node(node)

def get(self, key: int) -> int:
if key in self.cache:
node = self.cache[key]
self._move_to_front(node)
return node.value
else:
return -1

def put(self, key: int, value: int) -> None:
if key in self.cache:
node = self.cache[key]
node.value = value
self._move_to_front(node)
else:
if self.size == self.capacity:
del self.cache[self.tail.prev.key]
self._remove_node(self.tail.prev)
self.size -= 1
new_node = self.ListNode(key, value)
self._add_node(new_node)
self.cache[key] = new_node
self.size += 1
```

### Explanation:

1. **Define a Doubly Linked List Node**:
- We define a nested class `ListNode` to represent nodes in the doubly linked list. Each node contains `key`, `value`, `prev`, and `next` attributes.

2. **Initialize the LRU Cache**:
- In the `__init__` method, we initialize the LRU cache with the provided `capacity`, an empty dictionary `cache` to store key-value pairs, and two dummy nodes `head` and `tail` for the head and tail of the doubly linked list. We set `head.next` to `tail` and `tail.prev` to `head` to link them together. We also initialize `size` to 0.

3. **Implement `get` Operation**:
- In the `get` method, if the key exists in the cache, we move the corresponding node to the front of the doubly linked list using the `_move_to_front` method and return its value. If the key does not exist, we return -1.

4. **Implement `put` Operation**:
- In the `put` method, if the key exists in the cache, we update its value and move the corresponding node to the front of the doubly linked list. If the key does not exist:
- If the cache is full, we remove the least recently used node (tail node) from the doubly linked list and the dictionary.
- We create a new node with the provided key and value, add it to the front of the doubly linked list, and insert the key-value pair into the dictionary.
- We update the `size` accordingly.

This implementation ensures that both `get` and `put` operations run in O(1) average time complexity.
95 changes: 94 additions & 1 deletion src/main/python/g0101_0200/s0148_sort_list/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,97 @@ Given the `head` of a linked list, return _the list after sorting it in **ascend
* The number of nodes in the list is in the range <code>[0, 5 * 10<sup>4</sup>]</code>.
* <code>-10<sup>5</sup> <= Node.val <= 10<sup>5</sup></code>

**Follow up:** Can you sort the linked list in `O(n logn)` time and `O(1)` memory (i.e. constant space)?
**Follow up:** Can you sort the linked list in `O(n logn)` time and `O(1)` memory (i.e. constant space)?

To solve the problem of sorting a linked list, you can use the merge sort algorithm, which is suitable for linked lists because it provides an O(n log n) time complexity. This approach can be implemented recursively and achieves the required efficiency.

Here are the detailed steps and the corresponding implementation using the `Solution` class:

### Steps:

1. **Base Case**:
- If the linked list is empty or has only one node, it is already sorted. Return the head.

2. **Split the List**:
- Use the fast and slow pointer technique to find the middle of the linked list. This will help to split the linked list into two halves.
- `slow` moves one step at a time, while `fast` moves two steps at a time.
- When `fast` reaches the end, `slow` will be at the middle point of the list.

3. **Sort Each Half**:
- Recursively sort the left half and the right half of the list.

4. **Merge the Sorted Halves**:
- Merge the two sorted halves into a single sorted list.

### Implementation:

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

class Solution:
def sortList(self, head: ListNode) -> ListNode:
if not head or not head.next:
return head

# Step 2: Split the list into two halves
mid = self.getMid(head)
left = head
right = mid.next
mid.next = None

# Step 3: Sort each half
left = self.sortList(left)
right = self.sortList(right)

# Step 4: Merge the sorted halves
return self.merge(left, right)

def getMid(self, head: ListNode) -> ListNode:
slow = head
fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
return slow

def merge(self, list1: ListNode, list2: ListNode) -> ListNode:
dummy = ListNode()
tail = dummy

while list1 and list2:
if list1.val < list2.val:
tail.next = list1
list1 = list1.next
else:
tail.next = list2
list2 = list2.next
tail = tail.next

if list1:
tail.next = list1
if list2:
tail.next = list2

return dummy.next
```

### Explanation:

1. **Base Case**:
- The function `sortList` checks if the list is empty or has a single node, in which case it returns the head as it is already sorted.

2. **Split the List**:
- The `getMid` function finds the middle of the list using the fast and slow pointer technique.
- The list is then split into two halves: `left` starting from the head to the middle, and `right` starting from the node after the middle.

3. **Sort Each Half**:
- The `sortList` function is called recursively on both halves to sort them.

4. **Merge the Sorted Halves**:
- The `merge` function merges the two sorted halves into a single sorted linked list.
- A dummy node is used to simplify the merging process, and a `tail` pointer is used to build the new sorted list.

This approach ensures that the linked list is sorted in O(n log n) time complexity, which is optimal for this problem. The space complexity is O(log n) due to the recursion stack.
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,69 @@ A **subarray** is a contiguous subsequence of the array.

* <code>1 <= nums.length <= 2 * 10<sup>4</sup></code>
* `-10 <= nums[i] <= 10`
* The product of any prefix or suffix of `nums` is **guaranteed** to fit in a **32-bit** integer.
* The product of any prefix or suffix of `nums` is **guaranteed** to fit in a **32-bit** integer.

To solve the problem of finding the maximum product subarray, you can use a dynamic programming approach to keep track of the maximum and minimum products at each position in the array. This approach works because the product of two negative numbers can be positive, and thus the minimum product can become the maximum if a negative number is encountered.

Here are the detailed steps and the corresponding implementation in the `Solution` class:

### Steps:

1. **Initialization**:
- Check if the input array `nums` is empty. If it is, return 0.
- Initialize three variables:
- `max_product` to keep track of the maximum product found so far.
- `current_max` to keep track of the maximum product ending at the current position.
- `current_min` to keep track of the minimum product ending at the current position.

2. **Iterate through the Array**:
- Loop through each element in the array starting from the first element.
- For each element, calculate the potential new values for `current_max` and `current_min` considering the current element itself, the product of `current_max` with the current element, and the product of `current_min` with the current element.
- Update `current_max` to be the maximum of these values.
- Update `current_min` to be the minimum of these values.
- Update `max_product` to be the maximum of `max_product` and `current_max`.

3. **Return Result**:
- After the loop, `max_product` will hold the maximum product of any subarray within `nums`.

### Implementation:

```python
class Solution:
def maxProduct(self, nums: List[int]) -> int:
if not nums:
return 0

max_product = nums[0]
current_max = nums[0]
current_min = nums[0]

for num in nums[1:]:
if num < 0:
current_max, current_min = current_min, current_max

current_max = max(num, current_max * num)
current_min = min(num, current_min * num)

max_product = max(max_product, current_max)

return max_product
```

### Explanation:

1. **Initialization**:
- `max_product` is initialized to the first element of the array because the maximum product subarray must include at least one element.
- `current_max` and `current_min` are also initialized to the first element.

2. **Iterate through the Array**:
- For each element in `nums` (starting from the second element):
- If the current element is negative, swap `current_max` and `current_min` because multiplying by a negative number flips the maximum and minimum.
- Update `current_max` to be the maximum of the current element or the product of `current_max` with the current element.
- Update `current_min` to be the minimum of the current element or the product of `current_min` with the current element.
- Update `max_product` to be the maximum of `max_product` and `current_max`.

3. **Return Result**:
- The `max_product` variable now contains the maximum product of any contiguous subarray.

This solution efficiently finds the maximum product subarray in O(n) time complexity with O(1) additional space complexity.
Loading

0 comments on commit ca2f02e

Please sign in to comment.