Arey bacho! Jaldi se class mein aa jao aur dhyan whiteboard par lagao.
Pichle chapter mein humne seekha ki Complexity Analysis kya hoti hai aur code ki performance ko scale par kaise evaluate kiya jata hai. Aaj hum DSA ke sabse pehle, sabse fundamental, aur real-world development mein sabse zyada use hone wale data structure ko disassemble karenge—Arrays!
Array ek linear data structure hai jo multiple values ko ek single variable ke under contiguous (ek ke baad ek consecutive) memory locations mein store karta hai.
Chalo ek real-life scenario socho. Manlo tum ek teacher ho aur tumhein apni class ke 5 students ke marks store karne hain.
- Rasta 1: Tum 5 alag-alag variables banao:
let marks1 = 97,let marks2 = 82,let marks3 = 76...- Problem: Agar tumhari class mein 1000 bacche ho gaye, toh kya tum 1000 variables manually declare karoge? Bilkul nahi! It will become a maintenance nightmare.
- Rasta 2: Tum ek single container banao jise tum naam de do
marksaur usme saare students ke marks ko ek consecutive line mein arrange kar do. Yahi container humara Array hai!
┌─────────────────────────────────────────────────────────────────────────┐
│ ARRAY CONCEPTUAL MEMORY LAYOUT │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Index: 0 1 2 3 4 │
│ ┌───────┬───────┬───────┬───────┬───────┐ │
│ Value: │ 97 │ 82 │ 76 │ 65 │ 89 │ │
│ └───────┴───────┴───────┴───────┴───────┘ │
│ Address: 1000 1004 1008 1012 1016 │
│ (Assuming each integer takes 4 bytes of memory) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
- Element: Array ke andar store hone wali har individual value ko hum element kehte hain (jaise
97ya82). - Index: Array ke har ek box (compartment) ka ek numeric address hota hai, jise hum Index kehte hain. JavaScript mein index hamesha
0se shuru hota hai (0-based indexing). - Length: Array ke andar total kitne elements present hain, us count ko hum array ki
lengthkehte hain (Is case mein length is5). - Base Address: Array ke sabse pehle element (index 0) ka jo memory address hota hai, use base address kehte hain (jaise upar diagram mein
1000).
Bohot se bache sochte hain, "Sir, counting 1 se kyun nahi shuru hoti?" Iske peeche ek simple pointer calculation hai.
Memory level par jab hum kisi element ka address nikalte hain, toh computer ek offset formula use karta hai: \[Address of Index i = Base Address + (i × Size of one element)\]
Chalo upar wale array se index 2 par rakhe element ka address nikalte hain:
\[Address = 1000 + (2 × 4 bytes) = 1008\]
Computer ko bina kisi loop ke directly memory address mil gaya! Kyunki pehle element ke liye offset 0 hai, isiliye index 0 se start hota hai taaki address direct Base Address ke barabar aaye:
\[Address of Index 0 = 1000 + (0 × 4) = 1000\]
Interviews mein yeh question direct pucha jata hai. Dono ke beech ka conceptual difference dhyan se samajh lo:
| Feature | Traditional/Fixed Array (C++ / Java) | JavaScript Array |
|---|---|---|
| Sizing | Static length: Ek baar array declare kar diya (jaise size 5), toh memory lock ho jati hai. Use runtime par change nahi kar sakte. | Dynamic length: Iska size runtime par dynamically grow ya shrink ho sakta hai. |
| Data Types | Homogeneous: Saare elements ka data type exact same hona chahiye (e.g., pure integers). | Heterogeneous: Ek hi array mein tum numbers, strings, booleans, nested arrays aur objects mix karke store kar sakte ho. |
| Memory Allocation | Hamesha strictly Contiguous (continuous) blocks mein store hote hain. | Engine level par iske do states hote hain: Packed ya Holey. |
V8 engine JavaScript arrays ko optimize karne ke liye unhe do layouts mein store karta hai:
- Packed Arrays (Contiguous): Agar tum array ko sequentially fill karte ho bina beech mein gaps chode (e.g.
const arr =), toh engine ise contiguous block mein store karta hai. Is layout mein element access directly O(1) pointer arithmetic se hota hai aur performant hota hai. - Holey Arrays (Sparse): Agar tum indices ko skip kar dete ho (e.g., index
0par10set kiya aur directly index100par90set kiya), toh beech mein "holes" (undefined empty spaces) ban jate hain. Engine contiguous memory allocation chodh kar ise ek dictionary-like associative hash map mein convert kar deta hai. Isse element access slow ho jata hai kyunki engine ko prototype chain lookups perform karne padte hain.
DSA mein strong hone ke liye, hum built-in methods ke piche ka raw manual logic seekhengay. Chalo har operation ko dhang se dekhte hain:
┌─────────────────────────────────────────────────────────────────────────┐
│ ARRAY OPERATION COMPLEXITIES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Access: O(1) [Contiguous offset index lookup] │
│ Traversal:O(n) [Linearly visiting all N elements] │
│ Search: O(n) [Sequentially checking items] │
│ Update: O(1) [Direct index write] │
│ Insert: O(n) [Shifting elements to make space] │
│ Delete: O(n) [Shifting elements to fill gap] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Direct pointer arithmetic offset calculations ke zariye index target check kiya jata hai, isiliye array size chahe 10 lakh ho, element access hamesha constant time leta hai.
const arr =;
const val = arr; // O(1) TimeArray ke pehle element se shuru karke aakhri element tak ek-ek karke visit karna traversal hai.
function traverse(arr) {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]); // visiting every element
}
}- Why O(n)? Agar array mein n elements hain, toh loop exactly n baar chalega.
Array par iterate karne ke multiple tarike hote hain. DSA point of view se samjho ki kab kaunsa rasta choose karna hai:
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}- Kab use karein? Jab humein array ka index modify karna ho, reverse direction mein iterate karna ho (
i--), ya dynamically multiple steps jump karne hon (jaisei += 2).
let i = 0;
while (i < arr.length) {
console.log(arr[i]);
i++;
}- Kab use karein? Jab loop ka termination condition strictly numeric boundary par nahi, balki dynamic states par depend kare (jaise pointers tab tak move karne hain jab tak left index pointer right pointer se chota ho).
for (const val of arr) {
console.log(val);
}- Kab use karein? Jab humein index se koi lena-dena na ho aur bas array ki values ko cleanly read karna ho.
Linear Search (sequential search) sabse basic searching algorithm hai.
Manlo humein array mein ek target value dhoondhni hai. Hum index 0 se start karenge aur array ke end tak ek-ek element ko check karte jayenge. Agar matching value mil gayi, toh uska index return kar denge, warna array finish hone par -1 return kar denge.
Target = 30
Array: [ 10 │ 25 │ 30 │ 12 ]
0 1 2 3
Step 1: Check Index 0 -> 10 !== 30 (Aage badho)
Step 2: Check Index 1 -> 25 !== 30 (Aage badho)
Step 3: Check Index 2 -> 30 === 30 (Match Found! Return Index 2)
function linearSearch(arr, target) {
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
return i; // Target index mil gaya
}
}
return -1; // Pura array check kar liya, target nahi mila
}let i = 0: Array ke starting pointer0se iteration initialize hoti hai.i < arr.length: Hum tab tak chalte hain jab tak indices valid bounds ke andar hain.if (arr[i] === target): Har index par checked elements ko strict evaluation===se target value se match karte hain.return i: Milte hi processing terminate karke index return kar dete hain.return -1: Agar control loop se bahar aa gaya, iska matlab value nahi thi.
- Best Case: Target index
0par hi mil jaye. operations =1→ O(1) Time. - Worst Case: Target aakhri index par ho ya array mein present na ho. pooray n elements scan karne padenge → O(n) Time.
- Average Case: Target array ke beech mein kahin mile (around n/2 steps). constants drop karne par → O(n) Time.
- Space Complexity: O(1) Auxiliary Space kyunki hum koi extra helper memory structures scale nahi kar rahe.
Array contiguous block of memory hota hai, isiliye shifts compulsory hain jab hum elements starting ya middle mein manipulate karte hain.
Manlo humare paas array hai `` aur humein index 1 par value `99` insert karni hai.
Initial Array:
Index: 0 1 2 3 4
┌──────┬──────┬──────┬──────┬──────┐
│ 10 │ 20 │ 30 │ 40 │ │ (Vacancy at end)
└──────┴──────┴──────┴──────┴──────┘
Step 1: Piche se shuru karke index 1 tak elements ko right shift karenge
ko index 4 par copy kiya:
[ 10 │ 20 │ 30 │ 40 │ 40 ]
ko index 3 par copy kiya:
[ 10 │ 20 │ 30 │ 30 │ 40 ]
ko index 2 par copy kiya:
[ 10 │ 20 │ 20 │ 30 │ 40 ]
Step 2: Ab index 1 free ho chuka hai, wahan target 99 overwrite karenge:
[ 10 │ 99 │ 20 │ 30 │ 40 ] <-- Insertion Complete!
function insertAtIndex(arr, index, value) {
const n = arr.length;
// Right shift loops back
for (let i = n; i > index; i--) {
arr[i] = arr[i - 1];
}
arr[index] = value; // write on target vacancy
return arr;
}- Beginning insertion complexity: O(n) kyunki pooray n elements ko 1 step right shift karna padta hai.
- Middle insertion complexity: O(n) kyunki targeted location ke aage ke elements ko shift overhead face karna padta hai.
- End insertion complexity: O(1) (amortized) kyunki piche direct empty slot par overwrite hota hai, no shifting required.
Manlo humare paas array hai `` aur humein index 1 se element delete karna hai.
Initial Array:
Index: 0 1 2 3 4
┌──────┬──────┬──────┬──────┬──────┐
│ 10 │ 99 │ 20 │ 30 │ 40 │
└──────┴──────┴──────┴──────┴──────┘
Step 1: Index 2 se shuru karke elements ko 1 step left shift karenge
ko index 1 par copy kiya:
[ 10 │ 20 │ 20 │ 30 │ 40 ]
ko index 2 par copy kiya:
[ 10 │ 20 │ 30 │ 30 │ 40 ]
ko index 3 par copy kiya:
[ 10 │ 20 │ 30 │ 40 │ 40 ]
Step 2: Array ka duplicate tail element pop / truncate karenge size reduce karke:
[ 10 │ 20 │ 30 │ 40 ] <-- Deletion Complete!
function deleteAtIndex(arr, index) {
const n = arr.length;
if (index < 0 || index >= n) return arr;
// Left shifting
for (let i = index; i < n - 1; i++) {
arr[i] = arr[i + 1];
}
arr.length = n - 1; // Direct resizing truncation
return arr;
}- Beginning deletion complexity: O(n) because all elements must shift left to fill index 0.
- Middle deletion complexity: O(n) due to partial shifting of suffix elements.
- End deletion complexity: O(1) hamesha, directly tail element truncate hota hai, no shifts.
Let's dissect high-utility JS array built-ins along with their internal operational characteristics:
const fruits = ['apple', 'banana', 'cherry'];- Purpose: Array ke very end par elements append karta hai.
- Does it mutate? Yes, mutates original array.
- Time Complexity: O(1) (amortized). No shifts needed.
- Example:
fruits.push('date'); // ['apple', 'banana', 'cherry', 'date']
- Purpose: Array ke end se elements remove aur return karta hai.
- Does it mutate? Yes, mutates original array.
- Time Complexity: O(1).
- Example:
fruits.pop(); // returns 'date'
- Purpose: Array ke starting position (index 0) par element insert karta hai.
- Does it mutate? Yes, mutates original array.
- Time Complexity: O(n). Shifts entire elements rightwards.
- Example:
fruits.unshift('apricot'); // ['apricot', 'apple', 'banana', 'cherry']
- Purpose: Array ke index 0 se element remove aur return karta hai.
- Does it mutate? Yes, mutates original array.
- Time Complexity: O(n). Shifts entire elements leftwards.
- Example:
fruits.shift(); // returns 'apricot'
- Purpose: Multi-purpose tool jo elements add, remove ya replace karta hai target ranges par.
- Does it mutate? Yes, mutates original array directly.
- Time Complexity: O(n). Shifts internal trailing element chains.
- Example:
fruits.splice(1, 1, 'mango'); // replaces index 1 'banana' with 'mango'
- Purpose: Array ke specific chunk range
[start, end)ko sub-array copy return karta hai. - Does it mutate? No! Returns a shallow copy, leaving original intact.
- Time Complexity: O(k) where k = end - start.
- Example:
const subset = fruits.slice(1, 3); // returns ['mango', 'cherry']
- Purpose: Multiple arrays ko link/merge karke ek new array block return karta hai.
- Does it mutate? No! Returns a brand new array.
- Time Complexity: O(n + m) where n, m are lengths of the arrays being merged.
- Example:
const combined = fruits.concat(['grape', 'kiwi']);
- Purpose: Check karta hai ki array mein value hai ya nahi (returns boolean).
- Does it mutate? No.
- Time Complexity: O(n). Performs manual linear scan underneath.
- Example:
fruits.includes('apple'); // true
- Purpose: Target element ka pehla valid matching index deta hai, warna
-1. - Does it mutate? No.
- Time Complexity: O(n). Performs linear traversal.
- Example:
fruits.indexOf('mango'); // 1
Whiteboard par dhyan do, yeh table dimaag mein lock kar lo:
| Operation | Position | Time Complexity | Real Shifting Reason |
|---|---|---|---|
| Access | Anywhere | O(1) | Direct index math pointer offset jump. |
| Search | Anywhere | O(n) | Linear trace scan starting from index 0. |
| Update | Anywhere | O(1) | Direct write to continuous indexed slot. |
| Insertion | Beginning | O(n) | Must shift all n elements to the right. |
| Insertion | Middle | O(n) | Must shift elements after the target index. |
| Insertion | End | O(1) (amortized) | Direct append to final contiguous block. |
| Deletion | Beginning | O(n) | Must shift all remaining elements left. |
| Deletion | Middle | O(n) | Shift subsequent elements to close the gap. |
| Deletion | End | O(1) | Direct tail truncation, no shifts needed. |
JavaScript mein memory pointer variables reference semantics follow karte hain, jo data structures mein bohot crucial hain.
JavaScript mein primitive types (numbers, strings) pass-by-value hote hain. Lekin Objects aur Arrays pass-by-reference hote hain.
let arrayA =;
let arrayB = arrayA; // reference pointer copied
arrayB.push(4);
console.log(arrayA); // Output: <-- arrayA bhi change ho gaya!Kyunki arrayA aur arrayB dono memory mein same identical stack space address block ko point kar rahe hain.
Bachon ko aksar confusion hoti hai: "Sir, const array ko hum push karke kaise change kar sakte hain?"
const myArr =; // address reference lock ho gaya
myArr.push(4); // ALLOWED! Elements mutate hue, reference address change nahi hua
myArr =; // ERROR! Reassignment is strictly blocked in const variables- Shallow Copy: Pehle level ke properties ko copy karta hai.
Agar hum first level array change karein, toh normal copy hoga. Par nested level properties reference pointers duplicate copy karti hain:
let original = [,]; let shallowCopy = [...original]; // spread operator
shallowCopy.push(99); console.log(original); // <-- Nested element main change reflect ho gaya!
- Deep Copy: Har level of nested elements ko brand new arrays allocations banta hai.
let deepCopy = JSON.parse(JSON.stringify(original));
Chalo standard DSA problems solve karte hain direct manual loops lagakar.
- Understand: Humein ek array diya hai aur humein saare elements ka total sum return karna hai.
- Approach: Ek cumulative variable
suminitialized zero rakhenge. Pooray array par sequence loop trace chalakar har value add karte jayenge.
function getSum(arr) {
let sum = 0; // sum state
for (let i = 0; i < arr.length; i++) {
sum += arr[i]; // adding sequentially
}
return sum;
}- Line-by-Line:
let sum = 0: Sum tracker variable setup kiya.for (let i = 0; i < arr.length; i++): Indices start0se length tak scan chalta hai.sum += arr[i]: Cumulative math addition.
- Dry Run (
arr =):sum = 0i = 0→sum = 0 + 10 = 10i = 1→sum = 10 + 20 = 30i = 2→sum = 30 + 30 = 60- Returns
60.
- Complexity: O(n) Time kyunki complete traversal execute ho raha hai. O(1) Auxiliary Space variables storage ke liye.
- Edge Cases: Empty array
[]→ check constraints, return0.
- Understand: Array mein se sabse bada (Max) aur sabse chota (Min) elements nikaalna hai.
- Approach: Hum pehle element (
arr) ko hi initialmaxaurminmaan lenge. Phir loop chalakar check karenge ki kya koi value unse badi ya choti hai.
function findMinMax(arr) {
if (arr.length === 0) return { min: null, max: null };
let min = arr;
let max = arr;
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i]; // update max
}
if (arr[i] < min) {
min = arr[i]; // update min
}
}
return { min, max };
}- Dry Run (
arr =):min = 5,max = 5i = 1(Value = 12) →12 > max(5)→max = 12.i = 2(Value = 3) →3 < min(5)→min = 3.i = 3(Value = 45) →45 > max(12)→max = 45.- Returns
{ min: 3, max: 45 }.
- Complexity: O(n) Time, O(1) Space.
- Edge Cases: Single size array `` → Returns
{min: 10, max: 10}.
- Understand: Array ke elements ko reverse order mein place karna hai.
- Approach: Two pointer approach use karke start index pointer
0se aur end indexn-1se set karke elements swap karte huye meet karaenge center par.
function reverseArray(arr) {
let start = 0;
let end = arr.length - 1;
while (start < end) {
// Swapping values manually
let temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
return arr;
}- Dry Run (
arr =):start = 0,end = 3.- Swap
arr&arr: Array → ``. start = 1,end = 2.- Swap
arr&arr: Array → ``. start = 2,end = 1(Loop breaks). Correct!
- Complexity: O(n) Time. O(1) Auxiliary Space kyunki humne arrays ke elements in-place change kiye.
- Understand: Check karna hai ki har element apne agle element se chota ya barabar hai (
arr[i] <= arr[i+1]). - Approach: Adjacent linear pairs check karenge. Agar kisi bhi point par standard relation coordinate fail ho
arr[i] > arr[i+1], instantly returnfalse.
function isSorted(arr) {
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false; // sorted order broken
}
}
return true;
}- Time Complexity: O(n). O(1) Space.
- Edge Cases: Single element array ya empty array hamesha sorted hote hain.
- Index vs. Value Confusion:
Loop chalate waqt log
i(index pointer) aurarr[i](index ke andar ki actual value) ke beech confuse ho jate hain. Hamesha evaluate karo: key positions checkingihai ya element index valuearr[i]. - Off-by-One Out-of-Bounds:
Array ka size
lengthhota hai par indices sirflength-1tak access ho sakte hain.for(let i=0; i <= arr.length; i++)likhne par last step pararr[length]access hoga joundefinedreturn dega aur code fail ho jayega! Use< arr.lengthalways. - Modifying Array while Traversing:
Loop traverse chalne ke dauran
unshiftyasplicemanipulation steps indexes pointer position crash kar dete hain. Modifying active iterating ranges leads to buggy code. - Shallow Reference Pointer mutation bugs:
let copy = arrmanually reference pass hai. Copy array modify karne par original update ho jayega. Copying ke liyeconst copy = [...arr]use karo.
Tumne notice kiya hoga ki jab hum elements starting positions par shift/remove karte hain, toh complexity linear O(n) ho jati hai.
- Actual work behind the scenes: Memory contiguous blocks are locked consecutive sequence blocks. Agar index
0space empty karni hai, toh computer internally har index values ko register storage par shifts move linear direction instructions execute karta hai. - Yahi physical shifting actions actual workload coordinate badhate hain, jisse complexity scale lines increase ho jati hai.
- Custom loops nested iterations multiplication rule generate karti hain: outer scale N times, inner dynamic triangle M times → total complexity is O(n^2) ya sequential linear loops add up to O(n + m).
Aao bacho, whiteboard clear hai, ab in problems ke solution trace karo. Pehle pause karke khud socho ki isme actual code and complexity bounds kya hone chahiye!
- Problem Statement: Ek array diya hai, humein index
0se starting alternate indices (0, 2, 4, ...) ke elements ko print karna hai. - Hint to Think: Hum sequential increment steps variable size badha sakte hain (
i += 2).
// Solution Code
function printAlternate(arr) {
for (let i = 0; i < arr.length; i += 2) {
console.log(arr[i]);
}
}- Complexity: O(n) Time (since loop runs approximately n/2 times, and constants are dropped). O(1) Space.
- Problem Statement: Array mein se second largest element dhoondho bina use sort kiye.
- SDE Strategy to Think: Hum do trackers
largestaursecondLargestinitially-Infinityset karenge. Pure array par ek baar loop lagayenge:- Agar
arr[i] > largesthai, toh pehlesecondLargestko update karolargestse, firlargestko update karoarr[i]. - Agar current element
largestse chota hai, parsecondLargestse bada hai, toh update onlysecondLargest.
- Agar
// Solution Code
function findSecondLargest(arr) {
if (arr.length < 2) return null;
let largest = -Infinity;
let secondLargest = -Infinity;
for (let i = 0; i < arr.length; i++) {
if (arr[i] > largest) {
secondLargest = largest;
largest = arr[i];
} else if (arr[i] < largest && arr[i] > secondLargest) {
secondLargest = arr[i];
}
}
return secondLargest === -Infinity ? null : secondLargest;
}- Complexity: O(n) Time (Single-pass logic). O(1) Space (no sorting space limits used).
- Foundational definition of Arrays, memory offsets zero-based indexing maths.
- JS heterogeneous arrays vs static contiguous traditional arrays.
- Manual raw operations loops and element shifting visual mechanics.
- Complexities of push/pop/shift/unshift/splice/slice methods.
- Variable references, mutations, const modifications, shallow/deep copies.
- Problem solving: Sum, Min/Max, Reverse, Check Sorted, Alternate print.
- Index Access hamesha constant time O(1) hota hai offset formulas ke computation optimization se.
- Shift/Unshift/Splice linear time O(n) complexities lete hain internal contiguous blocks movements shifts calculations ke pipeline overhead se.
- Loop limits boundaries standard check boundaries comparison using wrong logical operations indices mismatch.
- Reference mutation errors inside variable assignments without slice/clones.