-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add algorithms for 2Sum, 3Sum, Kadane's Algorithm, and linked list pr…
…oblems
- Loading branch information
1 parent
18da83a
commit 08d2300
Showing
10 changed files
with
400 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// twoSum.js | ||
|
||
/** | ||
* | ||
* Time Complexity: O(n) — We traverse the list once while storing elements in a hash map. | ||
Space Complexity: O(n) — In the worst case, we store all elements in the hash map. | ||
* Finds indices of the two numbers such that they add up to a specific target. | ||
* @param {number[]} nums - An array of numbers. | ||
* @param {number} target - The target sum. | ||
* @returns {number[]|null} - Indices of the two numbers or null if not found. | ||
*/ | ||
export function twoSum(nums, target) { | ||
const map = new Map(); | ||
|
||
for (let i = 0; i < nums.length; i++) { | ||
const complement = target - nums[i]; | ||
if (map.has(complement)) { | ||
return [map.get(complement), i]; | ||
} | ||
map.set(nums[i], i); | ||
} | ||
|
||
return null; // No two sum solution | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// threeSum.js | ||
|
||
/** | ||
* Time Complexity: O(n^2) — The outer loop runs in O(n), and the inner two-pointer approach runs in O(n). | ||
Space Complexity: O(1) — The output list is the only extra space used (not counting input). | ||
* | ||
* | ||
* Finds all unique triplets in the array that sum up to zero. | ||
* @param {number[]} nums - An array of numbers. | ||
* @returns {number[][]} - A list of unique triplets. | ||
*/ | ||
export function threeSum(nums) { | ||
const result = []; | ||
nums.sort((a, b) => a - b); // Sort the array | ||
|
||
for (let i = 0; i < nums.length - 2; i++) { | ||
// Skip duplicate values | ||
if (i > 0 && nums[i] === nums[i - 1]) continue; | ||
|
||
let left = i + 1; | ||
let right = nums.length - 1; | ||
|
||
while (left < right) { | ||
const sum = nums[i] + nums[left] + nums[right]; | ||
|
||
if (sum === 0) { | ||
result.push([nums[i], nums[left], nums[right]]); | ||
while (left < right && nums[left] === nums[left + 1]) left++; // Skip duplicates | ||
while (left < right && nums[right] === nums[right - 1]) right--; // Skip duplicates | ||
left++; | ||
right--; | ||
} else if (sum < 0) { | ||
left++; | ||
} else { | ||
right--; | ||
} | ||
} | ||
} | ||
|
||
return result; // Return the list of triplets | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
// kadanesAlgorithm.js | ||
|
||
/** | ||
* | ||
* | ||
* Time Complexity: O(n) — The algorithm processes each element of the array once. | ||
Space Complexity: O(1) — Only a constant amount of additional space is used. | ||
* Finds the maximum sum of a contiguous subarray using Kadane's Algorithm. | ||
* @param {number[]} nums - An array of numbers. | ||
* @returns {number} - The maximum sum of the contiguous subarray. | ||
*/ | ||
export function maxSubArray(nums) { | ||
let maxSoFar = nums[0]; | ||
let maxEndingHere = nums[0]; | ||
|
||
for (let i = 1; i < nums.length; i++) { | ||
maxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]); | ||
maxSoFar = Math.max(maxSoFar, maxEndingHere); | ||
} | ||
|
||
return maxSoFar; // Return the maximum sum | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// test.js | ||
|
||
import { twoSum } from '../2Sum'; | ||
|
||
|
||
|
||
function testTwoSum() { | ||
// Test Case 1: Basic case | ||
let nums = [2, 7, 11, 15]; | ||
let target = 9; | ||
let result = twoSum(nums, target); | ||
console.assert(JSON.stringify(result) === JSON.stringify([0, 1]), `Expected [0, 1], got ${result}`); | ||
|
||
// Test Case 2: No solution | ||
nums = [1, 2, 3]; | ||
target = 6; | ||
result = twoSum(nums, target); | ||
console.assert(result === null, `Expected null, got ${result}`); | ||
|
||
// Test Case 3: Duplicate values | ||
nums = [3, 2, 4]; | ||
target = 6; | ||
result = twoSum(nums, target); | ||
console.assert(JSON.stringify(result) === JSON.stringify([1, 2]), `Expected [1, 2], got ${result}`); | ||
|
||
// Test Case 4: Negative numbers | ||
nums = [-3, 4, 3, 90]; | ||
target = 0; | ||
result = twoSum(nums, target); | ||
console.assert(JSON.stringify(result) === JSON.stringify([0, 2]), `Expected [0, 2], got ${result}`); | ||
|
||
console.log("All 2-Sum tests passed!"); | ||
} | ||
testTwoSum(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// test.js | ||
|
||
|
||
import { threeSum } from '../3sum'; | ||
function testThreeSum() { | ||
// Test Case 1: Basic case | ||
let nums = [-1, 0, 1, 2, -1, -4]; | ||
let result = threeSum(nums); | ||
console.assert(JSON.stringify(result) === JSON.stringify([[-1, -1, 2], [-1, 0, 1]]), `Expected [[-1, -1, 2], [-1, 0, 1]], got ${JSON.stringify(result)}`); | ||
|
||
// Test Case 2: No triplet | ||
nums = [1, 2, 3]; | ||
result = threeSum(nums); | ||
console.assert(JSON.stringify(result) === JSON.stringify([]), `Expected [], got ${JSON.stringify(result)}`); | ||
|
||
// Test Case 3: Duplicate triplets | ||
nums = [-2, 0, 0, 2, 2]; | ||
result = threeSum(nums); | ||
console.assert(JSON.stringify(result) === JSON.stringify([[-2, 0, 2]]), `Expected [[-2, 0, 2]], got ${JSON.stringify(result)}`); | ||
|
||
// Test Case 4: All negative numbers | ||
nums = [-1, -2, -3, -4]; | ||
result = threeSum(nums); | ||
console.assert(JSON.stringify(result) === JSON.stringify([]), `Expected [], got ${JSON.stringify(result)}`); | ||
|
||
console.log("All 3-Sum tests passed!"); | ||
} | ||
|
||
// Run the tests | ||
testThreeSum(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// testKadanesAlgorithm.js | ||
|
||
import { maxSubArray } from '../KandanesAlgo.js'; | ||
|
||
function runKadaneTests() { | ||
// Test Case 1: Basic case | ||
let nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]; | ||
let result = maxSubArray(nums); | ||
console.assert(result === 6, `Expected 6, got ${result}`); | ||
|
||
// Test Case 2: All negative numbers | ||
nums = [-1, -2, -3, -4]; | ||
result = maxSubArray(nums); | ||
console.assert(result === -1, `Expected -1, got ${result}`); | ||
|
||
// Test Case 3: All positive numbers | ||
nums = [1, 2, 3, 4]; | ||
result = maxSubArray(nums); | ||
console.assert(result === 10, `Expected 10, got ${result}`); | ||
|
||
// Test Case 4: Mixed numbers with zero | ||
nums = [0, -1, 2, 3, -2, 5, -3]; | ||
result = maxSubArray(nums); | ||
console.assert(result === 8, `Expected 8, got ${result}`); | ||
|
||
// Test Case 5: Single element | ||
nums = [-5]; | ||
result = maxSubArray(nums); | ||
console.assert(result === -5, `Expected -5, got ${result}`); | ||
|
||
console.log("All Kadane's Algorithm tests passed!"); | ||
} | ||
|
||
// Run the tests | ||
runKadaneTests(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
|
||
|
||
/** | ||
* Calculates the intersection point of two lines | ||
* defined by the points (x1, y1) to (x2, y2) and (x3, y3) to (x4, y4). | ||
* | ||
* @param {number} x1 - x-coordinate of the first point of the first line | ||
* @param {number} y1 - y-coordinate of the first point of the first line | ||
* @param {number} x2 - x-coordinate of the second point of the first line | ||
* @param {number} y2 - y-coordinate of the second point of the first line | ||
* @param {number} x3 - x-coordinate of the first point of the second line | ||
* @param {number} y3 - y-coordinate of the first point of the second line | ||
* @param {number} x4 - x-coordinate of the second point of the second line | ||
* @param {number} y4 - y-coordinate of the second point of the second line | ||
* @returns {Object|null} The intersection point { x, y } or null if no intersection | ||
*/ | ||
|
||
export function findIntersection(x1, y1, x2, y2, x3, y3, x4, y4) { | ||
const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); | ||
|
||
// Lines are parallel if denom is zero | ||
if (denom === 0) { | ||
return null; // No intersection | ||
} | ||
|
||
const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom; | ||
const ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom; | ||
|
||
// Check if intersection is within the segments | ||
if (ua < 0 || ua > 1 || ub < 0 || ub > 1) { | ||
return null; // Intersection not within the segments | ||
} | ||
|
||
// Calculate the intersection point | ||
const intersectionX = x1 + ua * (x2 - x1); | ||
const intersectionY = y1 + ua * (y2 - y1); | ||
|
||
return { x: intersectionX, y: intersectionY }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// slowFast.js | ||
|
||
// Definition for singly-linked list node | ||
class ListNode { | ||
constructor(value) { | ||
this.value = value; | ||
this.next = null; | ||
} | ||
} | ||
|
||
/** | ||
* Detects if a linked list has a cycle. | ||
* @param {ListNode} head - The head of the linked list. | ||
* @returns {boolean} - True if there's a cycle, false otherwise. | ||
*/ | ||
export function hasCycle(head) { | ||
let slow = head; | ||
let fast = head; | ||
|
||
while (fast && fast.next) { | ||
slow = slow.next; // Move slow pointer one step | ||
fast = fast.next.next; // Move fast pointer two steps | ||
|
||
if (slow === fast) { | ||
return true; // Cycle detected | ||
} | ||
} | ||
return false; // No cycle | ||
} | ||
|
||
/** | ||
* Finds the middle element of a linked list. | ||
* @param {ListNode} head - The head of the linked list. | ||
* @returns {ListNode|null} - The middle node or null if the list is empty. | ||
*/ | ||
export function findMiddle(head) { | ||
if (!head) return null; | ||
|
||
let slow = head; | ||
let fast = head; | ||
|
||
while (fast && fast.next) { | ||
slow = slow.next; // Move slow pointer one step | ||
fast = fast.next.next; // Move fast pointer two steps | ||
} | ||
return slow; // Slow pointer is at the middle | ||
} | ||
|
||
/** | ||
* Detects the start of the cycle in a linked list. | ||
* @param {ListNode} head - The head of the linked list. | ||
* @returns {ListNode|null} - The node where the cycle starts or null if there is no cycle. | ||
*/ | ||
export function detectCycle(head) { | ||
let slow = head; | ||
let fast = head; | ||
|
||
// First phase: determine if a cycle exists | ||
while (fast && fast.next) { | ||
slow = slow.next; | ||
fast = fast.next.next; | ||
|
||
if (slow === fast) { | ||
// Cycle detected | ||
let entry = head; | ||
while (entry !== slow) { | ||
entry = entry.next; // Move entry pointer | ||
slow = slow.next; // Move slow pointer | ||
} | ||
return entry; // Start of the cycle | ||
} | ||
} | ||
return null; // No cycle | ||
} | ||
|
||
// Exporting the ListNode class for testing | ||
export { ListNode }; |
44 changes: 44 additions & 0 deletions
44
Data-Structures/Linked-List/test/FindIntersectionpoint.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
|
||
import { findIntersection } from '../FindIntersectionPoint'; | ||
|
||
function runTests() { | ||
const tests = [ | ||
{ | ||
desc: 'Intersecting lines', | ||
input: [0, 0, 2, 2, 0, 2, 2, 0], | ||
expected: { x: 1, y: 1 } | ||
}, | ||
{ | ||
desc: 'Parallel lines (no intersection)', | ||
input: [0, 0, 2, 2, 0, 1, 2, 3], | ||
expected: null | ||
}, | ||
{ | ||
desc: 'Overlap lines (fully overlapping)', | ||
input: [0, 0, 2, 2, 1, 1, 3, 3], | ||
expected: { x: 1, y: 1 } | ||
}, | ||
{ | ||
desc: 'Non-intersecting lines (far apart)', | ||
input: [0, 0, 1, 1, 2, 2, 3, 3], | ||
expected: null | ||
}, | ||
{ | ||
desc: 'Intersecting at an endpoint', | ||
input: [0, 0, 2, 2, 2, 0, 0, 2], | ||
expected: { x: 2, y: 2 } | ||
} | ||
]; | ||
|
||
tests.forEach(({ desc, input, expected }, index) => { | ||
const result = findIntersection(...input); | ||
const isPassed = JSON.stringify(result) === JSON.stringify(expected); | ||
console.log(`Test ${index + 1}: ${desc} - ${isPassed ? 'Passed' : 'Failed'}`); | ||
if (!isPassed) { | ||
console.log(` Expected: ${JSON.stringify(expected)}, Got: ${JSON.stringify(result)}`); | ||
} | ||
}); | ||
} | ||
|
||
// Run the tests | ||
runTests(); |
Oops, something went wrong.