C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
240: Whiteboard Practice: Reversing a Linked List In Place
Alright, let's get into one of those classic whiteboard questions that makes everyone nervous: reversing a singly linked list in place. When an interviewer asks this, they aren't just checking if you know the algorithm; they're watching to see how you handle pointers without causing a segmentation fault. Let's walk through how I'd actually tackle this if I were sketching it out on a board.
The "Wait, I just broke my list" moment
Usually, my first instinct is to keep it simple. If I have a list like 1 -> 2 -> 3 -> NULL, I just want the arrows to point backward. I'll start with a current pointer at the head and a prev pointer set to NULL. My logic seems sound: just make current->next point to prev.
while (current != NULL) {
current->next = prev; // Flip the pointer
prev = current; // Move prev forward
current = current->next; // Move current forward... wait.
}
I stop right there. If I'm actually tracing this in my head, I realize I've just committed a cardinal sin of linked list manipulation. The moment I executed current->next = prev, I destroyed the only link I had to the rest of the list. If current was at node 1, current->next now points to NULL. When I try to move current forward using current = current->next, I'm not moving to node 2—I'm moving back to NULL. I've effectively orphaned the rest of my data.
Saving the future
To fix this, I need a way to remember where the rest of the list is before I sever the connection. I need a third pointer. I'll call it next_node. It's essentially a bookmark.
Let's try the sequence again. I'll grab the reference to the next node first, then I can safely flip the pointer without losing my place.
while (current != NULL) {
struct Node* next_node = current->next; // Bookmark the next node
current->next = prev; // Flip the arrow
// Now I can move my trackers forward
prev = current;
current = next_node;
}
This feels much better. If I trace it with 1 -> 2 -> 3:
- Iteration 1:
next_nodeis 2. Node 1 now points toNULL.prevbecomes 1,currentbecomes 2. - Iteration 2:
next_nodeis 3. Node 2 now points to 1.prevbecomes 2,currentbecomes 3. - Iteration 3:
next_nodeisNULL. Node 3 now points to 2.prevbecomes 3,currentbecomesNULL.
Dealing with the new head
Now, here is where a lot of people trip up. The loop terminates when current hits NULL. But if I return the original head of the list, I'm returning node 1, which is now the tail of my reversed list. That's not what we want.
Looking at my trace, when the loop finished, prev was sitting on node 3. That's my new head. So, the final step is simply returning prev. It's a subtle detail, but it's the difference between a working function and one that returns a list of length one.
Here is the complete logic we arrived at:
struct Node* reverseList(struct Node* head) {
struct Node* prev = NULL;
struct Node* current = head;
struct Node* next_node = NULL;
while (current != NULL) {
next_node = current->next; // Save next
current->next = prev; // Reverse
prev = current; // Advance prev
current = next_node; // Advance current
}
return prev; // prev is now the new head
}
The beauty of this approach is that it's O(n) time and O(1) space. We didn't allocate any new nodes; we just shuffled some addresses around. In a real interview, I'd mention that this handles an empty list (head == NULL) and a single-node list perfectly without needing extra if statements.
📋 Practical Task
Exercise: Implement a Reverse-and-Print Linked List Utility
Write a complete C program that does the following:
- Defines a
Nodestruct containing an integerdataand astruct Node* nextpointer. - Implements a function
struct Node* reverseList(struct Node* head)using the in-place logic discussed in the lesson. - Implements a helper function
void printList(struct Node* head)to display the list elements. - In
main, manually create a linked list with at least four elements (e.g., 10 → 20 → 30 → 40), print it, reverse it using your function, and print the reversed list to verify the result.
Constraint: You must not use any additional arrays or temporary lists to store the values; the reversal must happen by manipulating the pointers of the existing nodes.
There are no comments for now.