Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Jordan pulled out a piece of paper and started sketching. "I've been reading abo
They drew a simple diagram:

```
🎵 "Bohemian Rhapsody" → 🎵 "Hotel California" → 🎵 "Stairway to Heaven" → 🎵 "Sweet Child O' Mine" → 🎵 "Imagine" → null
<start> → 🎵 "Bohemian Rhapsody" → 🎵 "Hotel California" → 🎵 "Stairway to Heaven" → 🎵 "Sweet Child O' Mine" → 🎵 "Imagine" → null
```

"See?" Jordan continued excitedly. "Each song is connected to the next song, like links in a chain. If I want to add 'Thunderstruck' after 'Hotel California', I just need to:"
Expand Down
10 changes: 5 additions & 5 deletions src/sections/06-linked-lists/02-linked-lists-intro/solution.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class SongNode {
}

toString() {
return \`\${this.title} - \${this.artist}\`;
return `${this.title} - ${this.artist}`;
}
}

Expand Down Expand Up @@ -47,7 +47,7 @@ function playNextSong(playlist, targetSong) {
function removeSong(playlist, targetTitle) {
// Handle case where first song should be removed
if (playlist && playlist.title === targetTitle) {
console.log(\`🗑️ Removed first song: \${playlist.toString()}\`);
console.log(`🗑️ Removed first song: ${playlist.toString()}`);
return playlist.next;
}

Expand All @@ -57,13 +57,13 @@ function removeSong(playlist, targetTitle) {
if (currentSong.next.title === targetTitle) {
const removedSong = currentSong.next;
currentSong.next = removedSong.next;
console.log(\`🗑️ Removed song: \${removedSong.toString()}\`);
console.log(`🗑️ Removed song: ${removedSong.toString()}`);
return playlist;
}
currentSong = currentSong.next;
}

console.log(\`🎵 Song "\${targetTitle}" not found in playlist\`);
console.log(`🎵 Song "${targetTitle}" not found in playlist`);
return playlist;
}

Expand All @@ -76,6 +76,6 @@ function countSongs(playlist) {
currentSong = currentSong.next;
}

console.log(\`📊 Total songs in playlist: \${count}\`);
console.log(`📊 Total songs in playlist: ${count}`);
return count;
}
19 changes: 10 additions & 9 deletions src/sections/06-linked-lists/03-linked-list-types/checkpoint.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ export default [
"🚫 Circular linked lists cannot be traversed",
"📈 Doubly linked lists require more memory than singly linked lists"
],
correctAnswers: [0,1,2],
// explanation: {
// "⟷ Doubly linked lists enable bidirectional navigation": "✅ Correct — Doubly linked lists have both next and prev pointers.",
// "🔄 Circular linked lists create infinite loops": "✅ Correct — Circular lists loop back to the beginning.",
// "💾 Singly linked lists use less memory per node": "✅ Correct — Singly linked lists only need one pointer per node.",
// "⚡ All linked list types have the same performance characteristics": "❌ Incorrect — Different types have different performance characteristics.",
// "🚫 Circular linked lists cannot be traversed": "❌ Incorrect — Circular lists can be traversed with proper loop detection.",
// "📈 Doubly linked lists require more memory than singly linked lists": "✅ Correct — Two pointers per node vs one."
// }
correctAnswers: [0,1,2,5],
explanation:
(<ul>
<li>⟷ Doubly linked lists enable bidirectional navigation — ✅ Correct: Doubly linked lists have both <code>next</code> and <code>prev</code> pointers.</li>
<li>🔄 Circular linked lists create infinite loops — ✅ Correct: Circular lists loop back to the beginning.</li>
<li>💾 Singly linked lists use less memory per node — ✅ Correct: Singly linked lists only need one pointer per node.</li>
<li>⚡ All linked list types have the same performance characteristics — ❌ Incorrect: Different types have different performance characteristics.</li>
<li>🚫 Circular linked lists cannot be traversed — ❌ Incorrect: Circular lists can be traversed with proper loop detection.</li>
<li>📈 Doubly linked lists require more memory than singly linked lists — ✅ Correct: Two pointers per node vs one.</li>
</ul>)
},
{
type: QUESTION_TYPES.TEXT,
Expand Down
96 changes: 1 addition & 95 deletions src/sections/06-linked-lists/03-linked-list-types/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,66 +333,6 @@ class SmartCircularPlaylist extends CircularPlaylist {
}
```

## Real-World Applications of Different List Types

Maya used the whiteboard to show how different linked list types solve different real-world problems:

### Singly Linked Lists
- **Music streaming**: Basic playlist playback
- **Browser history**: Forward navigation only
- **Undo systems**: Simple action history
- **RSS feeds**: Chronological article lists

### Doubly Linked Lists
- **Media players**: Forward and backward navigation
- **Text editors**: Cursor movement in documents
- **Browser tabs**: Navigate between open tabs
- **Photo galleries**: Previous/next image viewing

### Circular Linked Lists
- **Round-robin scheduling**: CPU task scheduling
- **Multiplayer games**: Turn-based player rotation
- **Carousel displays**: Infinite image rotation
- **Background music**: Continuous playlist looping

## ⏱️ Alex's Circular Playlist Challenge!

"Now for the advanced challenge," Maya said with a smile. "Jordan wants to implement a feature that can detect if a playlist has accidentally become circular when it shouldn't be."

Jordan explained: "Sometimes when building playlists programmatically, you might accidentally create a loop. We need a way to detect this."

<iframe width="560" height="315" src="https://www.youtube.com/embed/S5TcPmTl6ww?si=82xnZM_NhIV7CVqH" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

🔓 **Uncomment the below code section in the editor 👉:**
- Implement `detectLoop()` to check if a playlist has an unintended circular connection
- Use the "tortoise and hare" algorithm (two pointers at different speeds)
- **Click Run Code**
- **Inspect 📋 Console Output window and run test to check for correctness!**

"This challenge teaches you about cycle detection - a classic computer science problem," Maya explained.

## Choosing the Right Playlist Type

As their session continued, Maya helped Alex and Jordan understand when to use each type:

### Use Singly Linked Lists When:
- **Memory is limited**: Only one pointer per node
- **Simple forward navigation**: No need to go backward
- **Implementation simplicity**: Easier to code and debug
- **Append-heavy operations**: Frequently adding to the end

### Use Doubly Linked Lists When:
- **Bidirectional navigation**: Need to move forward and backward
- **Frequent deletions**: Easier to remove nodes when you have previous pointer
- **LRU caches**: Need to move items to front/back efficiently
- **Text editing**: Cursor can move in both directions

### Use Circular Linked Lists When:
- **Continuous cycling**: Round-robin or infinite loops needed
- **No clear start/end**: Data naturally forms a cycle
- **Resource sharing**: CPU scheduling, printer queues
- **Game mechanics**: Turn-based systems, circular menus

## Performance Comparison

Jordan had prepared a comparison table:
Expand All @@ -407,38 +347,4 @@ Jordan had prepared a comparison table:
| **Delete node** | O(n) to find prev | O(1) if have node | O(n) to find prev |
| **Cycle detection** | Not applicable | Not applicable | Built-in |

*With tail pointer

## Looking Ahead: Advanced Playlist Features

As their session wound down, Jordan was already thinking about even more advanced features:

"What if we combined these concepts? Like a doubly linked circular playlist for a DJ system where you can go forward, backward, and it loops forever?"

Maya's eyes twinkled. "That's absolutely possible! You could create a doubly circular linked list. Each node would have both next and previous pointers, and the first and last nodes would connect to each other."

Alex was amazed. "So you can mix and match these concepts?"

"Exactly," Maya said. "Data structures are tools, and like any tools, you can combine them creatively to solve complex problems."

## Key Insights from Playlist Types

By the end of their session, Alex had learned:

- **Singly linked lists** provide simple, memory-efficient forward navigation
- **Doubly linked lists** enable bidirectional navigation at the cost of more memory
- **Circular linked lists** create infinite loops perfect for continuous operations
- **Each type solves different problems** and has different trade-offs
- **Real-world applications** exist for all three types
- **Performance characteristics** vary significantly between types
- **Creative combinations** are possible for complex requirements

Jordan was already sketching ideas for their next features. "I want to build a smart DJ system that can seamlessly switch between different playlist types based on the situation!"

"That sounds like an excellent project," Maya said. "Understanding these fundamental variations gives you the building blocks to create sophisticated systems."

As they packed up, Alex reflected on how much their understanding had grown. "It's amazing how something as simple as changing the connections between nodes can create completely different behaviors."

Maya smiled. "That's the beauty of data structures, Alex. Small changes in structure can lead to dramatically different capabilities. Tomorrow, we'll explore how to traverse and manipulate these different playlist types efficiently."

The journey into linked list variations had opened up a world of possibilities, each type offering unique advantages for different musical and programming challenges.
*With tail pointer
28 changes: 1 addition & 27 deletions src/sections/06-linked-lists/03-linked-list-types/solution.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,27 +55,6 @@ function navigatePlaylist(currentSong, direction, steps = 1) {
return current;
}

function detectLoop(playlist) {
// Floyd's cycle detection algorithm (tortoise and hare)
if (!playlist) return false;

let slow = playlist;
let fast = playlist;

while (fast && fast.next) {
slow = slow.next; // Move one step
fast = fast.next.next; // Move two steps

if (slow === fast) {
console.log("🔄 Loop detected in playlist!");
return true;
}
}

console.log("✅ No loop detected - playlist is linear");
return false;
}

// Helper function to create a test circular playlist
function createCircularPlaylist() {
const songA = new DoublySongNode("Song A", "Artist A");
Expand All @@ -93,9 +72,4 @@ function createCircularPlaylist() {
console.log("=== Testing navigatePlaylist ===");
console.log("Forward 1 step from Hotel California:", navigatePlaylist(song2, "forward", 1)?.title);
console.log("Backward 1 step from Stairway to Heaven:", navigatePlaylist(song3, "backward", 1)?.title);
console.log("Forward 2 steps from Bohemian Rhapsody:", navigatePlaylist(song1, "forward", 2)?.title);

console.log("\n=== Testing detectLoop ===");
console.log("Linear playlist has loop:", detectLoop(song1));
const circularPlaylist = createCircularPlaylist();
console.log("Circular playlist has loop:", detectLoop(circularPlaylist));
console.log("Forward 2 steps from Bohemian Rhapsody:", navigatePlaylist(song1, "forward", 2)?.title);
26 changes: 0 additions & 26 deletions src/sections/06-linked-lists/03-linked-list-types/starterCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,6 @@ function navigatePlaylist(currentSong, direction, steps = 1) {
}
*/

// ⏱️ Alex's Circular Playlist Challenge!
// 🔓 Uncomment the below code section and implement the required logic:

/*
function detectLoop(playlist) {
// Detect if a playlist has a circular connection (Floyd's cycle detection)
// Return true if loop exists, false otherwise

if (!playlist) return false;

let slow = playlist;
let fast = playlist;

// TODO: Implement the tortoise and hare algorithm
// Hint: Move slow pointer one step, fast pointer two steps
// If they meet, there's a loop

return false;
}
*/

// Helper function to create a test circular playlist
function createCircularPlaylist() {
const songA = new DoublySongNode("Song A", "Artist A");
Expand All @@ -86,9 +65,4 @@ console.log("=== Testing navigatePlaylist ===");
console.log("Forward 1 step from Hotel California:", navigatePlaylist(song2, "forward", 1)?.title);
console.log("Backward 1 step from Stairway to Heaven:", navigatePlaylist(song3, "backward", 1)?.title);
console.log("Forward 2 steps from Bohemian Rhapsody:", navigatePlaylist(song1, "forward", 2)?.title);

console.log("\n=== Testing detectLoop ===");
console.log("Linear playlist has loop:", detectLoop(song1));
const circularPlaylist = createCircularPlaylist();
console.log("Circular playlist has loop:", detectLoop(circularPlaylist));
*/
43 changes: 1 addition & 42 deletions src/sections/06-linked-lists/03-linked-list-types/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,48 +99,7 @@ const tests = [
}
},
message: "navigatePlaylist should handle forward, backward, and multi-step navigation."
},
{
name: "Test detectLoop function",
test: (code) => {
try {
const testCode = code + `
let linearResult = false;
let circularResult = false;

if (typeof detectLoop === 'function') {
// Test with linear playlist
linearResult = detectLoop(song1);

// Test with circular playlist
const circularPlaylist = createCircularPlaylist();
circularResult = detectLoop(circularPlaylist);
}

return ({ linearResult, circularResult });
`;

const testResult = new Function(testCode)();

if (typeof testResult.linearResult === 'undefined') {
return new TestResult({ passed: false, message: "detectLoop function not found. Make sure to uncomment and implement it." });
}

if (testResult.linearResult !== false) {
return new TestResult({ passed: false, message: "detectLoop should return false for linear playlists" });
}

if (testResult.circularResult !== true) {
return new TestResult({ passed: false, message: "detectLoop should return true for circular playlists" });
}

return new TestResult({ passed: true });
} catch (error) {
return new TestResult({ passed: false, message: error.message });
}
},
message: "detectLoop should correctly identify circular vs linear playlists using Floyd's algorithm."
},
}
];

export { tests, TestResult };
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ export default [
"Arrays require shifting elements for middle insertions",
"Linked lists provide O(1) random access",
"Arrays and linked lists have identical performance",
"Linked lists use less memory than arrays"
],
correctAnswers: [0,1,2],
// explanation: {
// "Arrays provide O(1) random access": "✅ Correct — Arrays can access any element by index in O(1) time.",
// "Linked lists provide O(1) insertion at beginning": "✅ Correct — Just update head pointer in O(1) time.",
// "Arrays require shifting elements for middle insertions": "✅ Correct — Elements after insertion point must shift.",
// "Linked lists provide O(1) random access": "❌ Incorrect — Must traverse from head, O(n) time.",
// "Arrays and linked lists have identical performance": "❌ Incorrect — They have different strengths and weaknesses.",
// "Linked lists use less memory than arrays": "❌ Incorrect — Extra pointers increase memory usage."
// }
explanation: (
<ul>
<li>Arrays provide O(1) random access — ✅ Correct: Arrays can access any element by index in O(1) time.</li>
<li>Linked lists provide O(1) insertion at beginning — ✅ Correct: Just update the head pointer in O(1) time.</li>
<li>Arrays require shifting elements for middle insertions — ✅ Correct: Elements after the insertion point must shift.</li>
<li>Linked lists provide O(1) random access — ❌ Incorrect: You must traverse from the head, which takes O(n) time.</li>
<li>Arrays and linked lists have identical performance — ❌ Incorrect: They have different strengths and weaknesses.</li>
</ul>
)
},
{
type: QUESTION_TYPES.TEXT,
questionJsx: "What is the time complexity for accessing the 50th element in a linked list?",
correctAnswer: "O(n)",
// explanation: "Accessing the 50th element requires traversing from the head through 49 nodes, making it **O(n)** time complexity, where n is the position of the element."
explanation: ("Accessing the 50th element requires traversing from the head through 49 nodes, making it **O(n)** time complexity, where n is the position of the element.")
}
]
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import contentMd from './index.md?raw';
import starterCode from './starterCode.js?raw';
import solution from './solution.js?raw';
import { tests } from './tests.js';
import questions from './checkpoint.jsx';
import { Checkpoint } from '@nss-workshops/nss-core';

Expand All @@ -11,10 +8,5 @@ export default {
previousChapterId: "linked-types",
nextChapterId: "linked-traversal",
content: contentMd,
exercises: [{
starterCode,
solution,
tests
}],
quiz: {component: () => <Checkpoint questions={questions}/> }
};
15 changes: 1 addition & 14 deletions src/sections/06-linked-lists/04-linked-list-tradeoffs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,4 @@ Inserting at beginning:
new head
Just change two pointers! O(1)
```

## ⏱️ Alex's Performance Analysis Challenge!

Maya pulled out her tablet. "Alex, let's put this theory to the test. I want you to implement a function that compares the performance characteristics of both approaches."

Jordan nodded enthusiastically. "This would help me understand exactly when to use each approach!"

🔓 **Uncomment the below code section in the editor 👉:**
- Implement `comparePerformance()` to fill in different operations time complexity in big O notation
- **Click Run Code**
- **Inspect 📋 Console Output window and run test to check for correctness!**

"This challenge will help you understand the practical implications of these performance differences," Maya explained.
```
Loading