Skip to content

Commit d98eb65

Browse files
JRS296NEHA-AMIN
authored andcommitted
Merge branch 'master' into feature/lru-cache
2 parents 0711fdf + a7250c7 commit d98eb65

File tree

5 files changed

+311
-0
lines changed

5 files changed

+311
-0
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# 31. Next Permutation
2+
3+
**Difficulty:** *Medium*
4+
**Category:** *Arrays, Permutations, Greedy*
5+
**Leetcode Link:** [Problem Link](https://leetcode.com/problems/next-permutation/)
6+
7+
---
8+
9+
## 📝 Introduction
10+
11+
*Given an array Arr[] of integers, rearrange the numbers into the lexicographically next greater permutation of numbers.
12+
If such an arrangement is not possible, rearrange it to the lowest possible order (i.e., sorted in ascending order).*
13+
14+
*Constraints typically include:<br>
15+
- All integers may be distinct or contain duplicates.<br>
16+
- The transformation must be done in-place with only constant extra memory.*
17+
18+
---
19+
20+
## 💡 Approach & Key Insights
21+
22+
*There are three main approaches to solve this problem:<br>
23+
- Brute Force: Generate all permutations and pick the next.<br>
24+
- In-Built Function: Use next_permutation() if language provides it.<br>
25+
- Optimal: Use the standard algorithm to compute the next permutation by identifying a break point, swapping, and reversing.*
26+
27+
---
28+
29+
## 🛠️ Breakdown of Approaches
30+
31+
### 1️⃣ Brute Force / Naive Approach
32+
33+
- **Explanation:** *Generate all permutations of the array, sort them, and locate the input permutation. Return the next one in sequence. If not found or if it's the last permutation, return the first (sorted array).*
34+
- **Time Complexity:** *O(N! × N) – N! permutations each of length N.*
35+
- **Space Complexity:** *O(1) – ignoring recursion stack used for generating permutations.*
36+
- **Example/Dry Run:**
37+
38+
```plaintext
39+
Input: [1, 2, 3]
40+
All permutations: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
41+
Current permutation: [1,2,3]
42+
Next permutation: [1,3,2]
43+
```
44+
45+
### 2️⃣ Language Built-in (C++ std::next_permutation)
46+
47+
- **Explanation:** *C++ provides next_permutation() which directly modifies the array to the next permutation.*
48+
- **Time Complexity:** *O(N) – internally optimized.*
49+
- **Space Complexity:** *O(1).*
50+
- **Example:**
51+
52+
```cpp
53+
std::vector<int> nums = {1, 2, 3};
54+
std::next_permutation(nums.begin(), nums.end());
55+
// nums is now [1, 3, 2]
56+
```
57+
58+
### 3️⃣ Optimal Approach
59+
60+
- **Explanation:**
61+
1. Find the break-point: The first index i from the back where arr[i] < arr[i+1].
62+
2. If no such index exists, reverse the whole array (it was the last permutation).
63+
3. Else, find the smallest number greater than arr[i] from the right half, swap it with arr[i].
64+
4. Reverse the subarray from i+1 to end.
65+
66+
- **Time Complexity:** *O(3N) = O(N) – each of the three steps (finding break-point, finding next greater, reversing) takes O(N).*
67+
- **Space Complexity:** *O(1) – all operations are done in-place.*
68+
- **Example/Dry Run:**
69+
70+
```plaintext
71+
Input: [1, 2, 3]
72+
73+
Step 1: Find break-point: 2 < 3 at index 1 → i = 1
74+
Step 2: Find element just greater than arr[i]: 3 at index 2
75+
Step 3: Swap 2 and 3 → [1, 3, 2]
76+
Step 4: Reverse from i+1 = 2 to end → Already sorted
77+
78+
Output: [1, 3, 2]
79+
```
80+
81+
---
82+
83+
## 📊 Complexity Analysis
84+
85+
| Approach | Time Complexity | Space Complexity |
86+
| -------------- | ----------------- | ---------------- |
87+
| Brute Force | O(N! × N) | O(1) |
88+
| In-Built | O(N) | O(1) |
89+
| Optimal | O(N) | O(1) |
90+
91+
---
92+
93+
## 📉 Optimization Ideas
94+
95+
*The optimal solution is already efficient for all constraints.
96+
You may consider early exits in case the array is already in decreasing order to avoid redundant operations.*
97+
98+
---
99+
100+
## 📌 Example Walkthroughs & Dry Runs
101+
102+
```plaintext
103+
Example 1:
104+
Input: [1, 3, 2]
105+
Break-point: index 0 (1 < 3)
106+
Swap with next greater: swap 1 and 2 → [2, 3, 1]
107+
Reverse after index 0 → [2, 1, 3]
108+
Output: [2, 1, 3]
109+
110+
Example 2:
111+
Input: [3, 2, 1]
112+
Break-point: None (fully decreasing)
113+
Reverse entire array → [1, 2, 3]
114+
Output: [1, 2, 3]
115+
```
116+
117+
---
118+
119+
## 🔗 Additional Resources
120+
121+
- [C++ next_permutation documentation](https://en.cppreference.com/w/cpp/algorithm/next_permutation)
122+
123+
---
124+
125+
Author: Neha Amin <br>
126+
Date: 19/07/2025
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class Solution {
2+
public:
3+
void nextPermutation(vector<int>& nums) {
4+
int n = nums.size();
5+
int ind = -1;
6+
// Step 1: Find the break point
7+
for (int i = n - 2; i >= 0; i--) {
8+
if (nums[i] < nums[i + 1]) {
9+
ind = i;
10+
break;
11+
}
12+
}
13+
// If break point does not exist, reverse the whole array
14+
if (ind == -1) {
15+
reverse(nums.begin(), nums.end());
16+
return;
17+
}
18+
// Step 2: Find element just greater than nums[ind] and swap
19+
for (int i = n - 1; i > ind; i--) {
20+
if (nums[i] > nums[ind]) {
21+
swap(nums[i], nums[ind]);
22+
break;
23+
}
24+
}
25+
// Step 3: Reverse the right half
26+
reverse(nums.begin() + ind + 1, nums.end());
27+
}
28+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class Solution:
2+
def nextPermutation(self, nums: List[int]) -> None:
3+
"""
4+
Do not return anything, modify nums in-place instead.
5+
"""
6+
n = len(nums)
7+
ind = -1
8+
# Step 1: Find the break point
9+
for i in range(n-2, -1, -1):
10+
if nums[i] < nums[i + 1]:
11+
ind = i
12+
break
13+
# If break point does not exist, reverse the whole array
14+
if ind == -1:
15+
nums.reverse()
16+
return
17+
# Step 2: Find the next greater element and swap
18+
for i in range(n - 1, ind, -1):
19+
if nums[i] > nums[ind]:
20+
nums[i], nums[ind] = nums[ind], nums[i]
21+
break
22+
# Step 3: Reverse the right half
23+
nums[ind+1:] = reversed(nums[ind+1:])
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
class LRUCache {
2+
class Node {
3+
int key;
4+
int val;
5+
Node next;
6+
Node prev;
7+
8+
Node(int key, int val) {
9+
this.key = key;
10+
this.val = val;
11+
this.next = null;
12+
this.prev = null;
13+
}
14+
}
15+
16+
Node head = new Node(-1, -1);
17+
Node tail = new Node(-1, -1);
18+
int cap;
19+
HashMap<Integer, Node> cache;
20+
21+
public LRUCache(int capacity) {
22+
cap = capacity;
23+
cache = new HashMap<>();
24+
head.next = tail;
25+
tail.prev = head;
26+
}
27+
28+
private void addNode(Node newNode) {
29+
Node temp = head.next;
30+
newNode.next = temp;
31+
newNode.prev = head;
32+
head.next = newNode;
33+
temp.prev = newNode;
34+
}
35+
36+
private void deleteNode(Node delNode) {
37+
Node delPrev = delNode.prev;
38+
Node delNext = delNode.next;
39+
delPrev.next = delNext;
40+
delNext.prev = delPrev;
41+
}
42+
43+
public int get(int key) {
44+
if (cache.containsKey(key)) {
45+
Node resNode = cache.get(key);
46+
int result = resNode.val;
47+
cache.remove(key);
48+
deleteNode(resNode);
49+
addNode(resNode);
50+
cache.put(key, head.next);
51+
return result;
52+
}
53+
return -1;
54+
}
55+
56+
public void put(int key, int value) {
57+
if (cache.containsKey(key)) {
58+
Node existingNode = cache.get(key);
59+
cache.remove(key);
60+
deleteNode(existingNode);
61+
}
62+
if (cache.size() == cap) {
63+
Node lruNode = tail.prev;
64+
cache.remove(lruNode.key);
65+
deleteNode(lruNode);
66+
}
67+
addNode(new Node(key, value));
68+
cache.put(key, head.next);
69+
}
70+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
class LRUCache:
2+
class Node:
3+
def __init__(self, key=0, val=0):
4+
self.key = key
5+
self.val = val
6+
self.next = None
7+
self.prev = None
8+
9+
def __init__(self, capacity: int):
10+
self.cap = capacity
11+
self.cache = {} # key -> node
12+
13+
# Create dummy head and tail nodes
14+
self.head = self.Node(-1, -1)
15+
self.tail = self.Node(-1, -1)
16+
self.head.next = self.tail
17+
self.tail.prev = self.head
18+
19+
def add_node(self, new_node):
20+
"""Add node right after head"""
21+
temp = self.head.next
22+
new_node.next = temp
23+
new_node.prev = self.head
24+
self.head.next = new_node
25+
temp.prev = new_node
26+
27+
def delete_node(self, del_node):
28+
"""Remove node from the list"""
29+
del_prev = del_node.prev
30+
del_next = del_node.next
31+
del_prev.next = del_next
32+
del_next.prev = del_prev
33+
34+
def get(self, key: int) -> int:
35+
if key in self.cache:
36+
res_node = self.cache[key]
37+
result = res_node.val
38+
39+
# Move to head (mark as recently used)
40+
del self.cache[key]
41+
self.delete_node(res_node)
42+
self.add_node(res_node)
43+
self.cache[key] = self.head.next
44+
45+
return result
46+
return -1
47+
48+
def put(self, key: int, value: int) -> None:
49+
if key in self.cache:
50+
# Update existing key
51+
existing_node = self.cache[key]
52+
del self.cache[key]
53+
self.delete_node(existing_node)
54+
55+
if len(self.cache) == self.cap:
56+
# Remove LRU (tail.prev)
57+
lru_node = self.tail.prev
58+
del self.cache[lru_node.key]
59+
self.delete_node(lru_node)
60+
61+
# Add new node
62+
new_node = self.Node(key, value)
63+
self.add_node(new_node)
64+
self.cache[key] = self.head.next

0 commit comments

Comments
 (0)