Skip to content

Commit da695a6

Browse files
authored
Merge branch 'master' into reverse_stack_improve
2 parents d066635 + 82dee61 commit da695a6

File tree

5 files changed

+259
-41
lines changed

5 files changed

+259
-41
lines changed

DIRECTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@
201201
* [PriorityQueues](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/PriorityQueues.java)
202202
* [Queue](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/Queue.java)
203203
* [QueueByTwoStacks](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/QueueByTwoStacks.java)
204+
* [TokenBucket](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/queues/TokenBucket.java)
204205
* stacks
205206
* [NodeStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/NodeStack.java)
206207
* [ReverseStack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/stacks/ReverseStack.java)
@@ -865,6 +866,7 @@
865866
* [PriorityQueuesTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/PriorityQueuesTest.java)
866867
* [QueueByTwoStacksTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueByTwoStacksTest.java)
867868
* [QueueTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/QueueTest.java)
869+
* [TokenBucketTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/queues/TokenBucketTest.java)
868870
* stacks
869871
* [NodeStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/NodeStackTest.java)
870872
* [ReverseStackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/stacks/ReverseStackTest.java)

src/main/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedLists.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,43 @@
11
package com.thealgorithms.datastructures.lists;
22

33
/**
4-
* Rotate a list
5-
* @author Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi)
4+
* The RotateSinglyLinkedLists class provides a method to rotate a singly linked list
5+
* to the right by a specified number of positions.
6+
* <p>
7+
* In a right rotation by `k` steps, each node in the list moves `k` positions to the right.
8+
* Nodes that are rotated off the end of the list are placed back at the beginning.
9+
* </p>
10+
* <p>
11+
* Example:
12+
* Given linked list: 1 -> 2 -> 3 -> 4 -> 5 and k = 2, the output will be:
13+
* 4 -> 5 -> 1 -> 2 -> 3.
14+
* </p>
15+
* <p>
16+
* Edge Cases:
17+
* <ul>
18+
* <li>If the list is empty, returns null.</li>
19+
* <li>If `k` is 0 or a multiple of the list length, the list remains unchanged.</li>
20+
* </ul>
21+
* </p>
22+
* <p>
23+
* Complexity:
24+
* <ul>
25+
* <li>Time Complexity: O(n), where n is the number of nodes in the linked list.</li>
26+
* <li>Space Complexity: O(1), as we only use a constant amount of additional space.</li>
27+
* </ul>
28+
* </p>
29+
*
30+
* Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi)
631
*/
7-
832
public class RotateSinglyLinkedLists {
33+
34+
/**
35+
* Rotates a singly linked list to the right by `k` positions.
36+
*
37+
* @param head The head node of the singly linked list.
38+
* @param k The number of positions to rotate the list to the right.
39+
* @return The head of the rotated linked list.
40+
*/
941
public Node rotateRight(Node head, int k) {
1042
if (head == null || head.next == null || k == 0) {
1143
return head;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.thealgorithms.datastructures.queues;
2+
3+
import java.util.concurrent.TimeUnit;
4+
5+
/**
6+
* TokenBucket implements a token bucket rate limiter algorithm.
7+
* This class is used to control the rate of requests in a distributed system.
8+
* It allows a certain number of requests (tokens) to be processed in a time frame,
9+
* based on the defined refill rate.
10+
*
11+
* Applications: Computer networks, API rate limiting, distributed systems, etc.
12+
*
13+
* @author Hardvan
14+
*/
15+
public final class TokenBucket {
16+
private final int maxTokens;
17+
private final int refillRate; // tokens per second
18+
private int tokens;
19+
private long lastRefill; // Timestamp in nanoseconds
20+
21+
/**
22+
* Constructs a TokenBucket instance.
23+
*
24+
* @param maxTokens Maximum number of tokens the bucket can hold.
25+
* @param refillRate The rate at which tokens are refilled (tokens per second).
26+
*/
27+
public TokenBucket(int maxTokens, int refillRate) {
28+
this.maxTokens = maxTokens;
29+
this.refillRate = refillRate;
30+
this.tokens = maxTokens;
31+
this.lastRefill = System.nanoTime();
32+
}
33+
34+
/**
35+
* Attempts to allow a request based on the available tokens.
36+
* If a token is available, it decrements the token count and allows the request.
37+
* Otherwise, the request is denied.
38+
*
39+
* @return true if the request is allowed, false if the request is denied.
40+
*/
41+
public synchronized boolean allowRequest() {
42+
refillTokens();
43+
if (tokens > 0) {
44+
tokens--;
45+
return true;
46+
}
47+
return false;
48+
}
49+
50+
/**
51+
* Refills the tokens based on the time elapsed since the last refill.
52+
* The number of tokens to be added is calculated based on the elapsed time
53+
* and the refill rate, ensuring the total does not exceed maxTokens.
54+
*/
55+
private void refillTokens() {
56+
long now = System.nanoTime();
57+
long tokensToAdd = (now - lastRefill) / TimeUnit.SECONDS.toNanos(1) * refillRate;
58+
tokens = Math.min(maxTokens, tokens + (int) tokensToAdd);
59+
lastRefill = now;
60+
}
61+
}

src/test/java/com/thealgorithms/datastructures/lists/RotateSinglyLinkedListsTest.java

Lines changed: 71 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,67 +6,100 @@
66
import org.junit.jupiter.api.Test;
77

88
/**
9-
* Test cases for RotateSinglyLinkedLists
9+
* Test cases for RotateSinglyLinkedLists.
1010
* Author: Bama Charan Chhandogi (https://github.com/BamaCharanChhandogi)
1111
*/
1212
public class RotateSinglyLinkedListsTest {
1313

14+
private final RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists();
15+
16+
// Helper method to create a linked list from an array of values
17+
private Node createLinkedList(int[] values) {
18+
if (values.length == 0) {
19+
return null;
20+
}
21+
22+
Node head = new Node(values[0]);
23+
Node current = head;
24+
for (int i = 1; i < values.length; i++) {
25+
current.next = new Node(values[i]);
26+
current = current.next;
27+
}
28+
return head;
29+
}
30+
31+
// Helper method to convert a linked list to a string for easy comparison
32+
private String linkedListToString(Node head) {
33+
StringBuilder sb = new StringBuilder();
34+
Node current = head;
35+
while (current != null) {
36+
sb.append(current.value);
37+
if (current.next != null) {
38+
sb.append(" -> ");
39+
}
40+
current = current.next;
41+
}
42+
return sb.toString();
43+
}
44+
1445
@Test
1546
public void testRotateRightEmptyList() {
16-
RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists();
17-
18-
// Test case: Rotate an empty list
47+
// Rotate an empty list
1948
assertNull(rotator.rotateRight(null, 2));
2049
}
2150

2251
@Test
2352
public void testRotateRightSingleNodeList() {
24-
RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists();
25-
26-
// Test case: Rotate a list with one element
53+
// Rotate a list with a single element
2754
Node singleNode = new Node(5);
2855
Node rotatedSingleNode = rotator.rotateRight(singleNode, 3);
29-
assertEquals(5, rotatedSingleNode.value);
30-
assertNull(rotatedSingleNode.next);
56+
assertEquals("5", linkedListToString(rotatedSingleNode));
3157
}
3258

3359
@Test
3460
public void testRotateRightMultipleElementsList() {
35-
RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists();
61+
// Rotate a list with multiple elements (rotate by 2)
62+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5});
63+
Node rotated = rotator.rotateRight(head, 2);
64+
assertEquals("4 -> 5 -> 1 -> 2 -> 3", linkedListToString(rotated));
65+
}
3666

37-
// Test case: Rotate a list with multiple elements (Rotate by 2)
38-
Node head = new Node(1);
39-
head.next = new Node(2);
40-
head.next.next = new Node(3);
41-
head.next.next.next = new Node(4);
42-
head.next.next.next.next = new Node(5);
67+
@Test
68+
public void testRotateRightFullRotation() {
69+
// Rotate by more than the length of the list
70+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5});
71+
Node rotated = rotator.rotateRight(head, 7);
72+
assertEquals("4 -> 5 -> 1 -> 2 -> 3", linkedListToString(rotated));
73+
}
4374

44-
Node rotated1 = rotator.rotateRight(head, 2);
45-
assertEquals(4, rotated1.value);
46-
assertEquals(5, rotated1.next.value);
47-
assertEquals(1, rotated1.next.next.value);
48-
assertEquals(2, rotated1.next.next.next.value);
49-
assertEquals(3, rotated1.next.next.next.next.value);
50-
assertNull(rotated1.next.next.next.next.next);
75+
@Test
76+
public void testRotateRightZeroRotation() {
77+
// Rotate a list by k = 0 (no rotation)
78+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5});
79+
Node rotated = rotator.rotateRight(head, 0);
80+
assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated));
5181
}
5282

5383
@Test
54-
public void testRotateRightFullRotation() {
55-
RotateSinglyLinkedLists rotator = new RotateSinglyLinkedLists();
84+
public void testRotateRightByListLength() {
85+
// Rotate a list by k equal to list length (no change)
86+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5});
87+
Node rotated = rotator.rotateRight(head, 5);
88+
assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated));
89+
}
5690

57-
// Test case: Rotate a list with multiple elements (Full rotation)
58-
Node head = new Node(1);
59-
head.next = new Node(2);
60-
head.next.next = new Node(3);
61-
head.next.next.next = new Node(4);
62-
head.next.next.next.next = new Node(5);
91+
@Test
92+
public void testRotateRightByMultipleOfListLength() {
93+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5});
94+
Node rotated = rotator.rotateRight(head, 10); // k = 2 * list length
95+
assertEquals("1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated));
96+
}
6397

64-
Node rotated3 = rotator.rotateRight(head, 7);
65-
assertEquals(4, rotated3.value);
66-
assertEquals(5, rotated3.next.value);
67-
assertEquals(1, rotated3.next.next.value);
68-
assertEquals(2, rotated3.next.next.next.value);
69-
assertEquals(3, rotated3.next.next.next.next.value);
70-
assertNull(rotated3.next.next.next.next.next);
98+
@Test
99+
public void testRotateRightLongerList() {
100+
// Rotate a longer list by a smaller k
101+
Node head = createLinkedList(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9});
102+
Node rotated = rotator.rotateRight(head, 4);
103+
assertEquals("6 -> 7 -> 8 -> 9 -> 1 -> 2 -> 3 -> 4 -> 5", linkedListToString(rotated));
71104
}
72105
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.thealgorithms.datastructures.queues;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import org.junit.jupiter.api.Test;
7+
8+
public class TokenBucketTest {
9+
10+
@Test
11+
public void testRateLimiter() throws InterruptedException {
12+
TokenBucket bucket = new TokenBucket(5, 1);
13+
for (int i = 0; i < 5; i++) {
14+
assertTrue(bucket.allowRequest());
15+
}
16+
assertFalse(bucket.allowRequest());
17+
Thread.sleep(1000);
18+
assertTrue(bucket.allowRequest());
19+
}
20+
21+
@Test
22+
public void testRateLimiterWithExceedingRequests() throws InterruptedException {
23+
TokenBucket bucket = new TokenBucket(3, 1);
24+
25+
for (int i = 0; i < 3; i++) {
26+
assertTrue(bucket.allowRequest());
27+
}
28+
assertFalse(bucket.allowRequest());
29+
30+
Thread.sleep(1000);
31+
assertTrue(bucket.allowRequest());
32+
assertFalse(bucket.allowRequest());
33+
}
34+
35+
@Test
36+
public void testRateLimiterMultipleRefills() throws InterruptedException {
37+
TokenBucket bucket = new TokenBucket(2, 1);
38+
39+
assertTrue(bucket.allowRequest());
40+
assertTrue(bucket.allowRequest());
41+
assertFalse(bucket.allowRequest());
42+
43+
Thread.sleep(1000);
44+
assertTrue(bucket.allowRequest());
45+
46+
Thread.sleep(1000);
47+
assertTrue(bucket.allowRequest());
48+
assertFalse(bucket.allowRequest());
49+
}
50+
51+
@Test
52+
public void testRateLimiterEmptyBucket() {
53+
TokenBucket bucket = new TokenBucket(0, 1);
54+
55+
assertFalse(bucket.allowRequest());
56+
}
57+
58+
@Test
59+
public void testRateLimiterWithHighRefillRate() throws InterruptedException {
60+
TokenBucket bucket = new TokenBucket(5, 10);
61+
62+
for (int i = 0; i < 5; i++) {
63+
assertTrue(bucket.allowRequest());
64+
}
65+
66+
assertFalse(bucket.allowRequest());
67+
68+
Thread.sleep(1000);
69+
70+
for (int i = 0; i < 5; i++) {
71+
assertTrue(bucket.allowRequest());
72+
}
73+
}
74+
75+
@Test
76+
public void testRateLimiterWithSlowRequests() throws InterruptedException {
77+
TokenBucket bucket = new TokenBucket(5, 1);
78+
79+
for (int i = 0; i < 5; i++) {
80+
assertTrue(bucket.allowRequest());
81+
}
82+
83+
Thread.sleep(1000);
84+
assertTrue(bucket.allowRequest());
85+
86+
Thread.sleep(2000);
87+
assertTrue(bucket.allowRequest());
88+
assertTrue(bucket.allowRequest());
89+
}
90+
}

0 commit comments

Comments
 (0)