Linear Data Structures
Level: 2 (Recall — definitions, standard textbook problems, short derivations) Time Limit: 30 minutes Total Marks: 40
Q1. Define amortized time complexity. State the amortized cost of appending an element to a dynamic array, and briefly explain why it is not per append despite occasional resizing. (4 marks)
Q2. State two differences between a static array and a dynamic array. Also explain what "cache locality" means and why arrays benefit from it more than linked lists. (4 marks)
Q3. Give the node structure (fields) of a singly linked list node and a doubly linked list node. State the time complexity of inserting a node at the head of a singly linked list. (4 marks)
Q4. For a singly linked list with nodes, state the time complexity of each operation: (a) traversal to print all elements (b) insert at head (c) insert at tail (no tail pointer maintained) (d) delete a node given only a pointer to it (not the previous node) (4 marks)
Q5. Define a stack and state its ordering discipline. List the three primary stack operations and describe what each does. (4 marks)
Q6. Using a stack, determine whether the following string of brackets is balanced. Show the stack contents step by step: State your final answer (balanced / not balanced). (4 marks)
Q7. Convert the infix expression to postfix using the standard operator-precedence algorithm. Show the operator stack and output at each step: (5 marks)
Q8. A circular array implementation of a queue has capacity 5 (indices 0–4). Starting empty with front = rear = 0, perform:
enqueue(10), enqueue(20), enqueue(30), dequeue(), enqueue(40), enqueue(50).
Give the final front, rear indices and the array contents. State the formula used to advance rear circularly. (5 marks)
Q9. Define a deque and list its four core operations. Give one real-world use case where a deque is preferred over a plain queue. (3 marks)
Q10. Define a priority queue. State how it differs from a normal FIFO queue in terms of dequeue order. (3 marks)
End of paper
Answer keyMark scheme & solutions
Q1. (4 marks)
- Amortized time = the average cost per operation over a worst-case sequence of operations, spreading the cost of expensive operations across many cheap ones. (2)
- Amortized cost of dynamic-array append = . (1)
- Why: resizing (copy of elements) happens only when the array is full, and doubling capacity means resizes occur after appends. Total copy work over appends is , so cost per append averages . (1)
Q2. (4 marks)
- Two differences (1 each, any two): static array has fixed size at compile/allocation time vs dynamic can grow/shrink; static allocated on stack (often) vs dynamic on heap; dynamic needs resize logic. (2)
- Cache locality: array elements are stored contiguously in memory, so accessing one element loads neighboring elements into cache lines. (1)
- Arrays benefit more than linked lists because linked-list nodes are scattered (non-contiguous) on the heap, causing more cache misses during traversal. (1)
Q3. (4 marks)
- Singly node:
data+next(pointer to next node). (1.5) - Doubly node:
data+next+prev(pointers to next and previous). (1.5) - Insert at head of singly list = . (1)
Q4. (4 marks) (1 each)
- (a)
- (b)
- (c) (must walk to end)
- (d) trick (copy next node's data into current, delete next) for a non-tail node; otherwise . Accept with reasoning.
Q5. (4 marks)
- Stack = linear structure following LIFO (Last-In-First-Out) ordering. (1)
- Operations: push — add element to top; pop — remove and return top element; peek/top — return top without removing. (3, 1 each)
Q6. (4 marks) Step-by-step (stack top on right):
| Symbol | Action | Stack |
|---|---|---|
| ( | push | ( |
| [ | push | ( [ |
| { | push | ( [ { |
| ] | top is { ≠ [ → mismatch | — |
Mismatch occurs: closing ] does not match top {. (3)
Final answer: NOT balanced. (1)
Q7. (5 marks) Convert . Precedence: * > +,-; left-associative.
| Token | Action | Stack | Output |
|---|---|---|---|
| A | output | A | |
| + | push | + | A |
| B | output | + | A B |
| * | push (higher prec) | + * | A B |
| C | output | + * | A B C |
| - | pop *, pop + (≥ prec), push - |
- | A B C * + |
| D | output | - | A B C * + D |
| end | pop all | A B C * + D - |
Postfix = A B C * + D - (5) — (2 for correct method/trace, 3 for correct result)
Q8. (5 marks) Capacity 5. Advance formula: rear = (rear + 1) % capacity (and similarly for front).
Tracing (using count to track fullness):
- enqueue(10): arr[0]=10, rear→1
- enqueue(20): arr[1]=20, rear→2
- enqueue(30): arr[2]=30, rear→3
- dequeue(): removes arr[0]=10, front→1
- enqueue(40): arr[3]=40, rear→4
- enqueue(50): arr[4]=50, rear→0 (wraps)
Final: front = 1, rear = 0. Array = [50, 20, 30, 40, 50] where valid elements are indices 1–4: 20, 30, 40, 50. Index 0 holds stale/overwritten 50. (3 for trace, 1 for indices, 1 for formula)
Q9. (3 marks)
- Deque = double-ended queue: elements can be inserted/removed at both ends. (1)
- Four operations:
pushFront,pushBack,popFront,popBack(accept peekFront/peekBack too). (1) - Use case (any valid): sliding-window maximum, undo/redo history, browser navigation, palindrome checking. (1)
Q10. (3 marks)
- Priority queue = a queue where each element has a priority; elements are served by priority rather than insertion order. (2)
- Difference: FIFO queue dequeues the earliest inserted element; priority queue dequeues the highest (or lowest) priority element regardless of insertion order. (1)
[
{"claim":"Dynamic array total copy cost for n appends with doubling is < 2n (O(1) amortized)","code":"n=1024; total=sum(2**k for k in range(0,11)); result = total < 2*n and total == 2047"},
{"claim":"Circular queue rear wraps to 0 after 5 enqueues from index 0 with capacity 5","code":"cap=5; rear=0\nfor _ in range(5): rear=(rear+1)%cap\nresult = rear==0"},
{"claim":"Q8 final front index = 1 after 5 enqueues and 1 dequeue","code":"cap=5; front=0; rear=0\nenq=[10,20,30]; \nfor _ in enq: rear=(rear+1)%cap\nfront=(front+1)%cap # one dequeue\nfor _ in [40,50]: rear=(rear+1)%cap\nresult = front==1 and rear==0"},
{"claim":"Bracket string ( [ { ] } ) is NOT balanced","code":"s='([{]})'; pairs={')':'(',']':'[','}':'{'}; st=[]; ok=True\nfor c in s:\n if c in '([{': st.append(c)\n else:\n if not st or st.pop()!=pairs[c]: ok=False; break\nresult = (ok and not st)==False"}
]