Process Management MCQs & GATE PYQs - Operating Systems
Master Link List Concepts for your Data Structure course and the GATE exam. This guide covers key concepts, essential MCQs, and previous year questions (PYQs) to sharpen your skills in Link List
Link List
Link List Concepts, tricks , Tips
Practice MCQs & GATE PYQs
1. In a circular linked list organisation, insertion of a record involves modification of: [ GATE CSE 1987 ]
Answer & Explanation
Correct Answer: B) Two pointers.
When inserting a new node into a circular linked list (for example, inserting `newNode` after `nodeA`), you need to perform two pointer modifications:
1. Set `newNode.next` to point to `nodeA.next`.
2. Set `nodeA.next` to point to `newNode`.
This correctly links the new node into the circle.
Trick to Remember
Think of adding a link to a closed chain. You have to connect the new link to the link after it (pointer 1) and connect the link before it to the new link (pointer 2).
2. Linked lists are not suitable data structures for which one of the following problems? [ GATE CSE 1994 ]
Answer & Explanation
Correct Answer: B) Binary search
Binary search relies on the ability to access the middle element of a data structure directly and efficiently (in constant time, or O(1)). This is known as random access. Linked lists, by their nature, only allow sequential access, meaning you have to traverse the list from the head to reach an element. Finding the middle element of a linked list takes linear time (O(n)), which defeats the purpose and efficiency of the binary search algorithm.
Linked lists are suitable for:
Insertion sort: No need to swap here just find appropriate place and join the link
Polynomial manipulation: Linked List is a natural solution for polynomial manipulation
Radix sort: Here we are putting digits according to same position(unit,tens) into buckets; which can be effectively handled by linked lists.
Concept Required
To answer this, you need to understand the fundamental difference between data access patterns:
Random Access: The ability to access any element in a collection in constant time, regardless of its position. Arrays provide random access (e.g., `myArray[100]`).
Sequential Access: The need to traverse elements in order from the beginning to reach a specific element. Linked lists are a primary example of this.
Concept to Remember
Binary search needs to "jump" to the middle of a list. Arrays are like rulers where you can instantly point to any number. Linked lists are like a chain of clues, where you must follow each one to get to the next.
3. For merging two sorted lists of sizes m and n into a sorted list of size m+n, we require comparisons of: [ GATE CSE 1995 ]
Answer & Explanation
Correct Answer: C)O(m+n)
The standard algorithm to merge two sorted lists involves iterating through both lists with pointers. In the worst-case scenario, you make one comparison for each element you add to the new list until one of the lists is exhausted. The total number of operations is therefore proportional to the sum of the lengths of the two lists, giving a time complexity of O(m+n).
Concept to Remember
Think of merging two lines of people already sorted by height. To create a single sorted line, you'd look at the first person in each line, pick the shorter one, and ask them to move to the new line. You have to repeat this for nearly every person in both lines. The total effort depends on the total number of people (m+n).
4. A circularly linked list is used to represent a Queue. A single variable p is used to access the Queue. To which node should p point such that both the operations enQueue and deQueue can be performed in constant time? [ GATE CSE 2004 ]
Answer & Explanation
Correct Answer: A) rear node
By pointing to the rear node of the circular list, we can access both the rear (for enQueue) and the front (for deQueue) in constant time, O(1). The front node is simply `rear->next`.
Let's see how the operations work with the pointer `p` pointing to the rear node:
EnQueue (Adding an element)
To insert a new node, we link it after the current rear node, and then update `p` to point to this new rear node. This takes constant time.
// p points to the rear node
// 'item' is the new node to be inserted
void enqueue(struct node *item) {
item->next = p->next; // New node points to the old front
p->next = item; // Old rear points to the new node
p = item; // p is updated to be the new rear
}
DeQueue (Removing an element)
To remove the front element, we access it via `p->next`. We then adjust the rear's `next` pointer to skip the old front node and free its memory. This also takes constant time.
// p points to the rear node
void dequeue() {
// temp points to the front node (p->next)
struct node* temp = p->next;
// The rear now points to the second node in the list
p->next = p->next->next;
// Free the memory of the old front node
free(temp);
}
Concept to Remember
In a circular list, the "end" is connected back to the "beginning." By holding a pointer to the **rear** element, you get a free pointer to the **front** element (`rear->next`). This two-for-one access is the key to implementing a queue efficiently.
5. Consider the function f defined below. [ GATE CSE 2003 ]
For a given linked list p, the function f returns 1 if and only if:
Answer & Explanation
Correct Answer: B) the elements in the list are sorted in non-decreasing order of data value
This is a recursive function that checks if a linked list is sorted. Let's break down the return statement:
(p == NULL): This is the first base case. If the list is empty, it's considered sorted, so it returns true (1).
(p->next == NULL): This is the second base case. If the list has only one element, it's also considered sorted, and it returns true (1).
((p->data <= p->next->data) && f(p->next))): This is the recursive step. It returns true only if both conditions are met:
The current node's data is less than or equal to the next node's data.
The rest of the list, starting from the next node, is also sorted (checked by the recursive call f(p->next)).
This logic effectively checks every pair of adjacent elements to ensure they are in non-decreasing (ascending) order.
Concept to Remember
The function's logic is based on a fundamental recursive principle: "A list is sorted if its first element is less than or equal to its second, AND the rest of the list is also sorted." The base cases (empty or single-element lists) provide the stopping point for the recursion.
6. In the worst case, the number of comparisons needed to search a singly linked list of length n for a given element is:[ GATE CSE 2002 ]
Show AnswerTry Again
Answer & Explanation
Correct Answer: D) n
A linked list only supports sequential access. This means you cannot jump to the middle of the list; you must start at the head and traverse from one node to the next. 🧐
In the worst-case scenario, the element you are looking for is either the very last element in the list or it is not in the list at all. In both cases, you have to visit and compare every single one of the n elements to be certain.
Concept to Remember
Think of a linked list like a train with n carriages. 🚂 To find something in the last carriage, you have to walk through all the other carriages from the engine to the end. You can't just teleport to the last one!
7. The concatenation of two lists is to be performed in $O(1)$ time. Which of the following implementations of a list should be used? GATE CSE 1997
A) Singly linked listB) Doubly linked listC) Circular doubly linked listD) Array implementation of list
Show AnswerTry Again
Answer & Explanation
Correct Answer: C) Circular doubly linked list
To achieve concatenation in constant time, $O(1)$, we must be able to access the beginning of the second list and the end of the first list without traversing them. Let's look at the options:
Array: Concatenating two arrays requires creating a new, larger array and copying all elements, making it an $O(m+n)$ operation.
Singly/Doubly Linked List: To join List2 to List1, you must find the last node of List1. This requires traversing List1, which takes $O(n)$ time, unless you maintain an extra pointer to the tail.
Circular Doubly Linked List: This structure is ideal. If you maintain a single pointer to the 'rear' node of each list, you can access both the head (rear->next) and the tail (rear) in $O(1)$ time. Concatenation is just a matter of rearranging a few pointers to join the tail of the first list with the head of the second, and the tail of the second with the head of the first. This takes a constant number of steps.
Concept to Remember
The key to O(1) concatenation is avoiding traversal. Any list implementation that keeps track of its last node (tail) can do this. A circular list is particularly elegant because a single pointer to the tail naturally gives you a direct link back to the head, making the pointer manipulations straightforward.
8. Which of the following statements is true?
As the number of entries in a hash table increases, the number of collisions increases.
Recursive programs are efficient.
The worst case complexity for Quicksort is O(n^2).
Binary search using a linear linked list is efficient.
A) I and IIB) II and IIIC) I and IVD) I and III
Show AnswerTry Again
Answer & Explanation
Correct Answer: D) I and III
Let's break down each statement:
I. Hash Table Collisions (True): As you add more entries to a hash table, its "load factor" increases. With more items occupying the available slots, the probability that a new item will hash to an already occupied slot (a collision) rises significantly. 💥
II. Recursive Programs (False): While often elegant, recursive programs are generally not the most efficient. Each recursive call adds a new frame to the call stack, consuming memory and adding overhead. An iterative solution is often faster and more memory-efficient.
III. Quicksort Worst Case (True): The average-case complexity of Quicksort is an excellent O(n \log n), but its worst-case complexity is $O(n^2)$. This occurs when the chosen pivot is consistently the smallest or largest element, leading to highly unbalanced partitions (e.g., when sorting an already sorted list).
IV. Binary Search on Linked List (False): Binary search is only efficient if you can access the middle element of a collection in constant time (O(1)), i.e., random access. Linked lists only allow sequential access (you must traverse from the beginning), which makes finding the middle an O(n) operation, defeating the purpose of binary search.
Key Concepts Tested
This question hits on several core CS fundamentals: the nature of hash tables, the trade-offs of recursion, the performance characteristics of sorting algorithms, and the importance of data structure access patterns (random vs. sequential).
9. The following C function takes a singly-linked list of integers as a parameter and rearranges the elements of the list. The function is called with the list containing the integers 1, 2, 3, 4, 5, 6, 7 in the given order. What will be the contents of the list after the function completes execution? [ GATE CSE 2008 ]
struct node {
int value;
struct node *next;
};
void rearrange(struct node *list) {
struct node *p, *q;
int temp;
if (!list || !list->next) return;
p = list;
q = list->next;
while (q) {
temp = p->value;
p->value = q->value;
q->value = temp;
p = q->next;
q = p ? p->next : 0;
}
}
A) 1,2,3,4,5,6,7B) 2,1,4,3,6,5,7C) 1,3,2,5,4,7,6D) 2,3,4,5,6,7,1
Show AnswerTry Again
Answer & Explanation
Correct Answer: B) 2,1,4,3,6,5,7
The function works by iterating through the list and swapping the values of adjacent pairs of nodes. It does not change the node pointers, only the `value` fields within the nodes.
Line-by-Line Code Explanation
struct node *p, *q;: Declares two node pointers, `p` and `q`, which will be used to traverse the list.
if (!list || !list->next) return;: A safety check. If the list is empty (`!list`) or has only one node (`!list->next`), there are no pairs to swap, so the function exits.
p = list; q = list->next;: Initializes the pointers. `p` points to the first node, and `q` points to the second node. This sets up the first pair.
while (q): The main loop. It continues as long as `q` is not `NULL`. This ensures we always have a valid pair to process.
temp = p->value; ... q->value = temp;: These three lines perform a classic swap of the integer values between the nodes `p` and `q`.
p = q->next;: `p` is moved forward two steps to the start of the next pair (i.e., it skips over the node it just swapped with).
q = p ? p->next : 0;: This line moves `q` forward two steps. It first checks if `p` is not `NULL`. If `p` is valid, `q` is moved to the node after `p`. If `p` is `NULL` (which happens at the end of a list with an odd number of elements), `q` is set to 0 (`NULL`), which terminates the `while` loop.
Execution Trace
Let's trace the execution with the input list: 1 → 2 → 3 → 4 → 5 → 6 → 7
Iteration 1:
`p` points to 1, `q` points to 2.
Values are swapped. List becomes: 2 → 1 → 3 → 4 → 5 → 6 → 7
`p` moves to node 3. `q` moves to node 4.
Iteration 2:
`p` points to 3, `q` points to 4.
Values are swapped. List becomes: 2 → 1 → 4 → 3 → 5 → 6 → 7
`p` moves to node 5. `q` moves to node 6.
Iteration 3:
`p` points to 5, `q` points to 6.
Values are swapped. List becomes: 2 → 1 → 4 → 3 → 6 → 5 → 7
`p` moves to node 7. `q` tries to move to `p->next`, which is `NULL`. So, `q` becomes `NULL`.
End of Loop: The `while(q)` condition is now false, and the loop terminates. The final node (7) is untouched as it had no pair.
10. The following C function takes a singly-linked list of integers as a parameter and rearranges the elements of the list. The function is called with the list containing the integers 1, 2, 3, 4, 5, 6, 7 in the given order. What will be the contents of the list after the function completes execution? [ GATE IT 2005 ]
struct node {
int value;
struct node *next;
};
void rearrange(struct node *list) {
struct node *p, *q;
int temp;
if (!list || !list->next) return;
p = list;
q = list->next;
while (q) {
temp = p->value;
p->value = q->value;
q->value = temp;
p = q->next;
q = p ? p->next : 0;
}
}
A) 1,2,3,4,5,6,7B) 2,1,4,3,6,5,7C) 1,3,2,5,4,7,6D) 2,3,4,5,6,7,1
Show AnswerTry Again
Answer & Explanation
Correct Answer: B) 2,1,4,3,6,5,7
The function works by iterating through the list and swapping the values of adjacent pairs of nodes. It's important to note that it only swaps the `value` data, not the nodes themselves. ↔️
Line-by-Line Code Explanation
struct node *p, *q;: Declares two node pointers, `p` and `q`, which will act as iterators to traverse the list.
if (!list || !list->next) return;: This is a base case. If the list is empty (`!list`) or has only one node (`!list->next`), there are no pairs to swap, so the function simply returns.
p = list; q = list->next;: This initializes the pointers. `p` points to the first node in a pair, and `q` points to the second.
while (q): The main loop continues as long as `q` is not `NULL`. This ensures that `p` always has a valid partner `q` to swap with.
temp = p->value; ... q->value = temp;: These three lines perform a standard swap of the integer values stored in the nodes pointed to by `p` and `q`.
p = q->next;: After swapping, `p` is advanced two positions to the start of the *next* pair. It jumps from its current position to the node right after `q`.
q = p ? p->next : 0;: This line also advances `q` two positions. The ternary operator `? :` checks if `p` became `NULL` in the previous step. If `p` is valid, `q` is set to the node after `p`. If `p` is `NULL` (which happens at the end of a list with an odd number of elements), `q` is set to 0 (`NULL`), which gracefully terminates the `while` loop.
Execution Trace
Let's trace the execution with the input list: 1 → 2 → 3 → 4 → 5 → 6 → 7
Initial State: `p` is at node 1, `q` is at node 2.
Iteration 1:
The values of node 1 and node 2 are swapped.
List becomes: 2 → 1 → 3 → 4 → 5 → 6 → 7
`p` advances to node 3. `q` advances to node 4.
Iteration 2:
The values of node 3 and node 4 are swapped.
List becomes: 2 → 1 → 4 → 3 → 5 → 6 → 7
`p` advances to node 5. `q` advances to node 6.
Iteration 3:
The values of node 5 and node 6 are swapped.
List becomes: 2 → 1 → 4 → 3 → 6 → 5 → 7
`p` advances to node 7. `q` attempts to advance to `p->next`, which is `NULL`. `q` becomes `NULL`.
End of Loop: The condition `while(q)` is now false, so the loop terminates. The last node (7) is left unchanged because it did not have a pair.
11. Suppose there are
⌈logn⌉ sorted lists of
⌊n/logn⌋ elements each. The time complexity of producing a sorted list of all these elements is: (Hint: Use a heap data structure). { GATE CSE 2005 ]
This problem describes merging multiple sorted lists, a task perfectly suited for a min-heap data structure. Here's the breakdown:
1. Identify the Key Variables
Number of lists (k): The problem states there are k = ceil(log n) lists.
Total elements (N): The total number of elements is N = (number of lists) * (elements per list) = ceil(log n) * floor(n/log n), which is approximately n.
2. The Min-Heap Algorithm
The standard algorithm for k-way merging is as follows:
Create a min-heap of size 'k'.
Insert the first element from each of the 'k' sorted lists into the heap. The time to build this initial heap is O(k log k).
Repeat 'N' times:
Extract the smallest element from the heap. This operation costs O(log k).
Add this element to the final sorted list.
Take the next element from the same list the smallest element came from and insert it into the heap. This also costs O(log k).
3. Calculate the Total Time Complexity
The total time is the sum of building the heap plus the N extract/insert operations.
Total Time = (Time to build heap) + N * (Time per operation)
Total Time = O(k log k) + N * O(log k)
Since N is much larger than k, the dominant term is N * O(log k). So, the complexity is O(N log k).
4. Substitute the Values
Now, we substitute our variables back into the formula O(N log k):
Replace N with n.
Replace k with log n.
Complexity = O(n log (log n)).
Concept to Remember
The time complexity to merge 'k' sorted lists with a total of 'N' elements using a min-heap is always O(N log k). The key to solving these problems is to correctly identify 'N' and 'k' from the problem description.
12. Let P be a singly linked list. Let Q be the pointer to an intermediate node x in the list. What is the worst-case time complexity of the best known algorithm to delete the node x from the list? [ GATE IT 2004 ]
A) O(n)B) O(\log^2 n)C) O(\log n)D) O(1)
Show AnswerTry Again
Answer & Explanation
Correct Answer: A)O(n)
In a singly linked list, to delete a node, you must modify the `next` pointer of the previous node to bypass the node being deleted. The problem is that we only have a pointer `Q` to the node `x` itself, not to its predecessor. 😥
Why not O(1)?
There is a common trick that works in O(1) for *intermediate* nodes: copy the data from the next node (`x->next`) into `x`, and then delete the next node. However, this trick fails in the worst-case scenario: when `x` is the last node in the list. In that case, there is no `next` node to copy from.
The Worst-Case Scenario
When the node to be deleted (`x`) is the last node, the only way to delete it is to find its predecessor and set that node's `next` pointer to `NULL`. To find the predecessor, you have no choice but to start from the head of the list (`P`) and traverse all the way to the end. This traversal takes a number of steps proportional to the length of the list, `n`.
Concept to Remember
The "single" in "singly linked list" is the key. You can only move forward. ➡️ To modify a link, you need a handle on the node *before* that link. Without a pointer to the previous node, you are forced to start from the beginning and search, which is an O(n) operation in the worst case.
13. Consider the following C function that operates on a singly linked list. What is the output when the function is called with the list `1→2→3→4→5→6→7→NULL` and `k = 3`?
struct node {
int data;
struct node* next;
};
struct node* reverseK(struct node* head, int k) {
if (!head)
return NULL;
struct node* current = head;
struct node* next = NULL;
struct node* prev = NULL;
int count = 0;
// Reverse the first k nodes of the linked list
while (current != NULL && count < k) {
next = current->next;
current->next = prev;
prev = current;
current = next;
count++;
}
// 'next' now points to the (k+1)th node.
// Recursively call for the list starting from 'next'.
if (next != NULL) {
head->next = reverseK(next, k);
}
// 'prev' is the new head of the input list.
return prev;
}
A) 7→6→5→4→3→2→1→NULLB) 3→2→1→6→5→4→7→NULLC) 3→2→1→4→5→6→7→NULLD) 1→2→3→4→5→6→7→NULL
Show AnswerTry Again
Answer & Explanation
Correct Answer: B) 3→2→1→6→5→4→7→NULL
The function `reverseK` reverses a linked list in groups of `k` nodes. It uses a combination of iteration (to reverse one group) and recursion (to handle subsequent groups).
Code's Logic Explained
The `while` loop: This part iteratively reverses the first `k` nodes. At the end of the loop, `prev` points to the new head of this reversed group (e.g., node 3 in the first call), and `head` (the original head, e.g., node 1) is now the tail of this group.
The recursive call `if (next != NULL)`: After reversing one group, the original head's `next` pointer (e.g., `1->next`) is set to the result of the recursive call on the rest of the list (which starts at the `(k+1)`th node).
`return prev;`: The function returns `prev`, which is the head of the now-reversed sublist.
Execution Trace (List: 1→2→3→4→5→6→7, k=3)
Initial Call: `reverseK(head=1, k=3)`
The `while` loop runs 3 times, reversing `1→2→3` into `3→2→1`.
After the loop: `prev` is 3, `current` is 4, and the original `head` (node 1) points to `NULL`.
A recursive call is made: `reverseK(head=4, k=3)`. The original `head` (node 1) will have its `next` pointer set to the result of this call.
Recursive Call 1: `reverseK(head=4, k=3)`
The `while` loop runs 3 times, reversing `4→5→6` into `6→5→4`.
After the loop: `prev` is 6, `current` is 7, and the original `head` (node 4) points to `NULL`.
A recursive call is made: `reverseK(head=7, k=3)`. The original `head` (node 4) will have its `next` pointer set to the result of this call.
The `while` loop runs only once, as `current` becomes `NULL` after processing node 7. The "group" `7` is reversed to just `7`.
`prev` becomes 7. There is no `next`, so no more recursive calls.
This call returns `7`.
Returning and Linking:
Call 1 receives `7` as the result. It links its tail (node 4) to 7. So `4→next = 7`. It returns its new head, `6`. The list is now `6→5→4→7`.
The Initial Call receives `6→5→4→7` as the result. It links its tail (node 1) to 6. So `1→next = 6`. It returns its new head, `3`.
Final List: 3→2→1→6→5→4→7→NULL
Concept to Remember
This is a "divide and conquer" approach. The problem is broken down: "Reverse the first k nodes, and then recursively handle the rest of the list." The key is to correctly link the tail of the current reversed group to the head of the next reversed group returned by the recursive call. 🔗
14. Suppose each set is represented as a linked list with elements in arbitrary order. Which of the operations among union, intersection, membership, and cardinality will be the slowest? [ GATE CSE 2004 ]
A) union onlyB) intersection, membershipC) membership, cardinalityD) union, intersection
Show AnswerTry Again
Answer & Explanation
Correct Answer: D) union, intersection
Let's analyze the time complexity for each operation, assuming the two lists have lengths $m$ and $n$. Since the lists are in an arbitrary (unsorted) order, we can't use any shortcuts.
Analysis of Operations
Membership: To check if an element exists in a list of size $m$, you must, in the worst case, traverse the entire list.
Complexity: $O(m)$
Cardinality: To find the number of elements in a list of size $m$, you must traverse the entire list to count each node.
Complexity: $O(m)$
Intersection: To find the intersection, you must take each of the $m$ elements from the first list and, for each one, search the entire second list (of size $n$) to see if it exists.
Complexity: $O(m \times n)$
Union: A common way to perform union is to first copy the first list (an $O(m)$ operation). Then, for each of the $n$ elements in the second list, you must check if it's already in the new list (which has at least $m$ elements) before adding it. This check takes $O(m)$ for each of the $n$ elements.
Complexity: $O(m \times n)$
As you can see, the complexities for union and intersection are quadratic ($O(m \times n)$), which is significantly slower than the linear ($O(n)$) complexities for membership and cardinality.
Concept to Remember
Operations that require comparing every element of one unsorted list against every element of another will typically have a quadratic ($O(m \times n)$) complexity. Operations on a single list are usually linear ($O(n)$). 📜
0 Comments