By the end of this chapter you'll be able to…

  • 1State the single question that distinguishes the linear structures
  • 2Compute an element address in a one- or two-dimensional array
  • 3Explain why array access is constant time and insertion is linear
  • 4Explain why doubling makes dynamic-array appends amortised constant
  • 5State the cost of every array operation
  • 6Explain why linked-list insertion is constant given a pointer
  • 7Explain why deleting from a singly linked list needs the predecessor
  • 8Describe the copy-successor deletion trick and its two failure modes
  • 9Compare singly, doubly, circular and sentinel-headed lists
  • 10Compare arrays and linked lists on access, modification and memory behaviour
  • 11Explain why arrays outperform lists more than the asymptotics suggest
  • 12Implement a stack in both array and linked forms
  • 13Apply a stack to balanced parentheses, infix conversion and postfix evaluation
  • 14Explain why function calls require a stack
  • 15Explain why a naive array queue wastes space
  • 16State the empty and full conditions for a circular queue
  • 17Compute the element count of a circular queue correctly across a wrap
  • 18Explain why full and empty are ambiguous and the two remedies
  • 19Distinguish a deque and a priority queue from a plain queue
  • 20Match queue applications to the first-in-first-out property
  • 21Build a queue from two stacks and analyse the amortised cost
  • 22Explain why building a stack from two queues has no cheap solution
💡
Why this chapter matters in GATE
The linear data structures look like four separate topics and are really one. Each is a different answer to a single question: where in the sequence are insertions and deletions allowed? An array allows access anywhere and cheap insertion nowhere. A linked list allows cheap insertion anywhere and cheap access nowhere. A stack allows both but only at one end, and a queue allows insertion at one end and deletion at the other. Every cost, every implementation choice and every application follows from that restriction. The second organising fact is that arrays and linked lists trade the same two costs in opposite directions: contiguity buys computed addresses and costs shifting, while pointers buy splicing and cost traversal. There is no third option that wins both, and every more elaborate structure in later chapters is an attempt to buy back one cost without paying the other in full.

Before you start — revise these

🔗
Programming in C & Recursion
Pointers, array decay and the stack discipline of function calls are the machinery every structure here is built from.
🔗
Asymptotic Complexity & Recurrences
Amortised analysis of dynamic-array growth and of the two-stack queue uses the aggregate reasoning developed there.

Arrays, Stacks, Queues & Linked Lists

The linear data structures look like four separate topics and are really one. Each is a different answer to a single question: where in the sequence are insertions and deletions allowed?

An array allows access anywhere and insertion nowhere cheaply. A linked list allows insertion anywhere cheaply and access nowhere cheaply. A stack allows both, but only at one end. A queue allows insertion at one end and deletion at the other.

Every cost, every implementation choice and every application follows from that restriction, so a question about any of them is answered by asking what the restriction permits.

The second organising fact is that arrays and linked lists trade the same two costs in opposite directions. An array stores elements contiguously, so it can compute any element's address arithmetically but must shift elements to make room. A linked list stores each element with a pointer to the next, so it can splice in a node by changing two pointers but must walk from the start to find anything.

There is no third option that wins both. Every more elaborate structure in later chapters is an attempt to buy back one cost without paying the other in full.

1. Arrays

An array occupies contiguous memory, so the address of element is computed rather than searched for.

That single formula is the array's entire advantage: constant-time access to any element regardless of position, and perfect cache behaviour on sequential traversal because consecutive elements share cache lines.

The costs follow equally directly from contiguity.

Inserting at position requires shifting every later element up by one, costing in the worst case and on average. Deleting requires shifting down. Only insertion and deletion at the very end avoid the shift.

The size is fixed at allocation. A dynamic array grows by allocating a larger block and copying, and doubling the capacity on each growth gives amortised constant-time appends — because the total copying cost across appends is bounded by .

OperationArray cost
Access by index
Search unsorted
Search sorted
Insert at end amortised
Insert at position
Delete at position

2. Linked Lists

A linked list stores each element in a node containing the data and a pointer to the next node. The nodes need not be adjacent in memory.

Insertion and deletion cost — given a pointer to the right place. Splicing a node in changes two pointers and touches nothing else, which is the whole reason linked lists exist.

The qualification is where the marks are. Finding that place costs , because the only way to reach node is to follow pointers from the head.

Deleting a node from a singly linked list requires a pointer to its predecessor, since the predecessor's next pointer must be updated. Given only the node itself, the standard trick is to copy the successor's data into it and delete the successor instead — which fails for the last node.

Three variants address specific weaknesses.

A doubly linked list adds a previous pointer, making backward traversal possible and making deletion given only the node. The cost is one extra pointer per node and two extra updates per operation.

A circular list makes the last node point to the first, so traversal never falls off the end and any node can serve as an entry point. This suits round-robin scheduling directly.

A header or sentinel node removes the special case for an empty list, since the list is never truly empty and no operation needs to check for a null head.

OperationSingly linkedDoubly linked
Access by index
Insert at head
Insert after a known node
Delete a known node
Traverse backwardsImpossible

3. Arrays Versus Linked Lists

The comparison is examined constantly and reduces to three points.

Access is the array's win: constant time against linear time, because the address is computed rather than followed.

Structural modification is the list's win: two pointer updates against shifting half the elements on average.

Memory behaviour favours the array more than the asymptotics suggest. An array stores only data, while a list pays a pointer per node — often as much as the data itself for small elements. And contiguous storage means a traversal reads full cache lines, while list nodes scattered across memory can miss on every access.

The practical consequence is that for small , or for any workload dominated by traversal, an array frequently outperforms a list even where the asymptotic analysis favours the list.

4. Stacks

A stack permits insertion and deletion at one end only, giving last-in-first-out order.

Both operations are in either implementation. An array implementation keeps a top index and is fastest, but has a fixed capacity. A linked implementation pushes at the head and has no capacity limit, at the cost of a pointer per element.

Underflow is popping an empty stack and overflow is pushing a full array-based one, and both must be checked.

The applications are what GATE actually examines.

Function calls use a stack, because the nesting discipline of calls matches last-in-first-out exactly, and this is what makes recursion possible.

Balanced-parenthesis checking pushes each opening symbol and pops on each closing one, verifying the popped symbol matches. The string is balanced if every pop matches and the stack is empty at the end.

Infix-to-postfix conversion uses a stack to hold operators awaiting their right operands, popping any operator of higher or equal precedence before pushing a new one.

Postfix evaluation pushes operands and, on each operator, pops two, applies, and pushes the result. The final stack holds one value.

Depth-first traversal of a graph or tree uses a stack, explicitly or via recursion, and that is the same structure in a different guise.

5. Queues

A queue permits insertion at the rear and deletion at the front, giving first-in-first-out order.

A naive array implementation moves the front index forward on each dequeue, wasting the vacated space and eventually reporting full while most of the array is empty.

A circular queue fixes this by wrapping the indices modulo the capacity, so the space is reused. The wrap introduces one genuine difficulty.

Full and empty both give front equal to rear, and distinguishing them requires either keeping one slot permanently unused or maintaining an explicit count.

With one slot sacrificed, a queue of capacity holds elements, and the conditions become:

The number of elements is , and the is what handles the wrap correctly.

A deque permits insertion and deletion at both ends, and it generalises both stack and queue — restricting it to one end gives a stack, and to opposite ends gives a queue.

A priority queue dequeues by priority rather than arrival order, which breaks the first-in-first-out contract entirely and is implemented with a heap rather than a linear structure.

The applications mirror the stack's and are examined in the same way.

Breadth-first traversal uses a queue, exactly as depth-first uses a stack, and the choice of container is the only difference between the two algorithms. Replacing the queue with a stack in a breadth-first search turns it into a depth-first one.

Scheduling uses a queue whenever fairness matters, since first-in-first-out guarantees that no waiting job is passed over indefinitely. Round-robin scheduling is a circular queue read repeatedly.

Buffering between a fast producer and a slow consumer uses a queue, absorbing bursts so that neither side blocks unnecessarily. Keyboard input, print spooling and network packet handling are all this pattern.

The unifying observation is that a stack reverses order while a queue preserves it, and each application picks whichever property it needs.

6. Implementing One From Another

Two conversions are examined regularly, and both are worth being able to derive.

A queue can be built from two stacks. Push incoming elements onto stack A. To dequeue, if stack B is empty, pop everything from A into B, which reverses the order; then pop from B.

Each element is moved between stacks at most twice, so the amortised cost per operation is even though a single dequeue can cost .

A stack can be built from two queues, but less gracefully. One approach makes push and pop : to pop, dequeue all but the last element into the second queue and return the last. The other makes push and pop by reversing on every insertion.

The asymmetry is instructive. Two stacks give an efficient queue because reversal is exactly what converts last-in-first-out into first-in-first-out, while two queues cannot reverse anything and must therefore move elements repeatedly.

7. Worked Examples

Example 1. An array a[10][20] of 4-byte integers has base address 1000 and is stored in row-major order. Find the address of a[6][12].

Row-major means all of row 0 is stored first, then row 1, and so on. Reaching a[i][j] skips complete rows of 20 elements, then further elements.

The address is 1528.

Under column-major storage the formula reverses to base plus times the element size, giving .

C uses row-major, so the first answer applies to C code, and the difference matters for performance as well as correctness: traversing along rows walks contiguous memory while traversing along columns strides across it.

Example 2. A circular queue has capacity 8 with one slot sacrificed. Front is 6 and rear is 2. How many elements does it hold, and is it full?

The element count wraps, so use the modular formula.

The queue holds 4 elements.

For fullness, check whether advancing rear would collide with front: , which is not equal to 6, so the queue is not full.

With one slot sacrificed, the maximum occupancy is elements, and this queue is holding 4 of them.

Note why the matters. Computing directly gives , and taking a negative value modulo 8 is either wrong or implementation-dependent depending on the language. Adding first guarantees a non-negative operand.

Example 3. Convert the infix expression A + B * C - D / E to postfix using a stack.

Scan left to right, outputting operands immediately and using the stack for operators.

A is an operand: output. Output so far: A.

+ : the stack is empty, so push it.

B : output. Output: A B.

* : the stack top is +, which has lower precedence than *, so do not pop. Push *.

C : output. Output: A B C.

- : the stack top is *, which has higher precedence, so pop and output it. Now the top is +, equal precedence and left-associative, so pop and output that too. Then push -. Output: A B C * +.

D : output. Output: A B C * + D.

/ : the stack top is -, lower precedence, so push /.

E : output. Output: A B C * + D E.

End of input: pop the remaining operators. Pop /, then -.

The postfix expression is A B C * + D E / -.

The rule that does the work is: pop while the stack top has higher or equal precedence, then push. Equal precedence is popped because the operators are left-associative; for a right-associative operator such as exponentiation, equal precedence is not popped.

Example 4. Evaluate the postfix expression 5 3 + 8 2 - * using a stack.

Scan left to right, pushing operands and applying operators to the top two.

Push 5. Push 3. Stack: 5, 3.

+ : pop 3 and 5, compute , push 8. Stack: 8.

Push 8. Push 2. Stack: 8, 8, 2.

- : pop 2 and 8, compute , push 6. Stack: 8, 6.

* : pop 6 and 8, compute , push 48. Stack: 48.

The result is 48.

The critical detail is operand order for non-commutative operators. The first value popped is the right operand and the second is the left, so 8 2 - means and not . Getting this backwards is the single most common error in postfix evaluation, and it is invisible for + and *.

Example 5. Given only a pointer to a node in the middle of a singly linked list, delete it in constant time. What breaks?

The obvious approach fails: deleting a node requires updating its predecessor's next pointer, and a singly linked list gives no way to reach the predecessor except by walking from the head, which is .

The standard trick sidesteps this. Instead of removing the node, copy the successor's data into the node and then delete the successor.

Concretely, if the node holds value and its successor holds , overwrite with and then splice out the successor by setting the node's next pointer to the successor's next.

The list now contains the right sequence of values, the node count is right, and the cost is .

Two things break. The trick fails for the last node, since there is no successor to copy from, and it leaves the last node's predecessor pointing at a node that should be gone. And any external pointer that referred to the successor node now refers to a freed node, which is a dangling pointer.

The clean solution is a doubly linked list, where the previous pointer makes the predecessor immediately available and the deletion is genuinely with no caveats.

Example 6. Implement a queue using two stacks and analyse the amortised cost.

Keep two stacks, called in and out.

Enqueue pushes onto in, which is unconditionally.

Dequeue checks out. If it is non-empty, pop from it. If it is empty, pop every element from in and push each onto out, then pop from out.

The transfer reverses the order, so the element that entered in first ends up on top of out — which is exactly first-in-first-out.

For the cost: a single dequeue can cost when a transfer happens. But each element is pushed onto in once, popped from in once, pushed onto out once and popped from out once — four operations over its entire lifetime, regardless of how many dequeues occur.

So operations cost in total, giving amortised per operation.

The contrast with building a stack from two queues is worth noting. That direction has no cheap solution, because a queue cannot reverse a sequence, so one of push or pop must move every element on every call.

Summary

Every linear structure answers one question: where are insertions and deletions allowed.

An array computes element addresses arithmetically, giving access and perfect cache behaviour, but insertion and deletion cost because elements must shift. Doubling on growth makes appends amortised .

A linked list splices nodes with two pointer updates, giving insertion and deletion given a pointer, but access because the only route to a node is through its predecessors.

Deleting from a singly linked list needs the predecessor. A doubly linked list makes it at the cost of one extra pointer, a circular list removes the end case, and a sentinel node removes the empty case.

The array's advantage is larger in practice than in the asymptotics, because a list pays a pointer per node and scatters its data across cache lines.

A stack is one-ended and gives last-in-first-out. It underlies function calls, balanced-parenthesis checking, infix-to-postfix conversion, postfix evaluation and depth-first traversal.

A queue is two-ended and gives first-in-first-out. A circular implementation reuses space, and full and empty are distinguished by sacrificing a slot or keeping a count. The element count is the difference of indices plus the capacity, taken modulo the capacity.

A deque generalises both; a priority queue abandons arrival order entirely and is built on a heap.

Two stacks give a queue at amortised cost, because reversal converts one ordering into the other. Two queues give a stack only awkwardly, because a queue cannot reverse.

Key formulas & results

Everything to memorise for the exam hall, in one card. Screenshot this for revision.

The organising tool
EACH LINEAR STRUCTURE ANSWERS ONE QUESTION: WHERE ARE INSERTIONS AND DELETIONS ALLOWED?
ARRAY: ACCESS ANYWHERE, CHEAP INSERTION NOWHERE. LIST: CHEAP INSERTION ANYWHERE, CHEAP ACCESS NOWHERE. STACK: ONE END. QUEUE: OPPOSITE ENDS.
Array addressing
THE ADDRESS OF a[i] IS THE BASE PLUS i TIMES THE ELEMENT SIZE. FOR ROW-MAJOR a[i][j] IT IS THE BASE PLUS (i TIMES C PLUS j) TIMES THE ELEMENT SIZE.
COLUMN-MAJOR REVERSES THIS TO (j TIMES R PLUS i). C USES ROW-MAJOR, AND THE CHOICE AFFECTS CACHE BEHAVIOUR AS WELL AS THE ARITHMETIC.
Array costs
ACCESS BY INDEX IS CONSTANT. SEARCH IS LINEAR UNSORTED AND LOGARITHMIC SORTED. INSERTION AND DELETION AT A POSITION ARE LINEAR BECAUSE ELEMENTS MUST SHIFT.
ONLY OPERATIONS AT THE VERY END AVOID THE SHIFT. THE FIXED SIZE IS THE OTHER COST OF CONTIGUITY.
Dynamic array growth
DOUBLING THE CAPACITY ON EACH GROWTH GIVES AMORTISED CONSTANT-TIME APPENDS.
THE TOTAL COPYING COST ACROSS n APPENDS IS BOUNDED BY 2n, BECAUSE THE COPIES FORM A GEOMETRIC SERIES THAT SUMS TO LESS THAN TWICE THE FINAL SIZE.
Linked list costs
INSERTION AND DELETION COST CONSTANT TIME GIVEN A POINTER TO THE RIGHT PLACE, BUT FINDING THAT PLACE COSTS LINEAR TIME.
SPLICING A NODE IN CHANGES TWO POINTERS AND TOUCHES NOTHING ELSE, WHICH IS THE WHOLE REASON LINKED LISTS EXIST.
Deleting from a singly linked list
DELETION REQUIRES A POINTER TO THE PREDECESSOR, SINCE THE PREDECESSOR'S NEXT POINTER MUST BE UPDATED.
GIVEN ONLY THE NODE, COPY THE SUCCESSOR'S DATA INTO IT AND DELETE THE SUCCESSOR. THIS FAILS FOR THE LAST NODE AND DANGLES ANY EXTERNAL POINTER TO THE SUCCESSOR.
List variants
A DOUBLY LINKED LIST ADDS A PREVIOUS POINTER, MAKING DELETION CONSTANT GIVEN ONLY THE NODE. A CIRCULAR LIST MAKES THE LAST NODE POINT TO THE FIRST. A SENTINEL NODE REMOVES THE EMPTY-LIST SPECIAL CASE.
EACH VARIANT REMOVES ONE SPECIFIC WEAKNESS AT A SPECIFIC COST: AN EXTRA POINTER, A LOST TERMINATION TEST, OR ONE WASTED NODE.
Array versus list
ACCESS IS THE ARRAY'S WIN. STRUCTURAL MODIFICATION IS THE LIST'S WIN. MEMORY BEHAVIOUR FAVOURS THE ARRAY MORE THAN THE ASYMPTOTICS SUGGEST.
A LIST PAYS A POINTER PER NODE, OFTEN AS MUCH AS THE DATA, AND SCATTERED NODES CAN MISS THE CACHE ON EVERY ACCESS WHILE AN ARRAY READS FULL CACHE LINES.
Stack implementations
BOTH PUSH AND POP ARE CONSTANT TIME IN EITHER IMPLEMENTATION. AN ARRAY KEEPS A TOP INDEX AND IS FASTEST BUT FIXED IN CAPACITY; A LINKED STACK PUSHES AT THE HEAD AND IS UNBOUNDED.
UNDERFLOW IS POPPING AN EMPTY STACK AND OVERFLOW IS PUSHING A FULL ARRAY-BASED ONE, AND BOTH MUST BE CHECKED.
Stack applications
FUNCTION CALLS, BALANCED PARENTHESES, INFIX-TO-POSTFIX CONVERSION, POSTFIX EVALUATION AND DEPTH-FIRST TRAVERSAL.
ALL OF THEM EXPLOIT THE SAME PROPERTY: A STACK REVERSES ORDER, AND THE NESTING DISCIPLINE OF CALLS AND BRACKETS MATCHES LAST-IN-FIRST-OUT EXACTLY.
Infix to postfix
OUTPUT OPERANDS IMMEDIATELY. FOR AN OPERATOR, POP WHILE THE STACK TOP HAS HIGHER OR EQUAL PRECEDENCE, THEN PUSH. POP THE REMAINDER AT THE END.
EQUAL PRECEDENCE IS POPPED BECAUSE OPERATORS ARE LEFT-ASSOCIATIVE. FOR A RIGHT-ASSOCIATIVE OPERATOR SUCH AS EXPONENTIATION, EQUAL PRECEDENCE IS NOT POPPED.
Postfix evaluation
PUSH OPERANDS. ON AN OPERATOR, POP TWO, APPLY, AND PUSH THE RESULT. THE FINAL STACK HOLDS ONE VALUE.
THE FIRST VALUE POPPED IS THE RIGHT OPERAND AND THE SECOND IS THE LEFT. GETTING THIS BACKWARDS IS THE COMMONEST ERROR AND IS INVISIBLE FOR PLUS AND TIMES.
Circular queue conditions
WITH ONE SLOT SACRIFICED: EMPTY MEANS FRONT EQUALS REAR, AND FULL MEANS (REAR PLUS 1) MOD N EQUALS FRONT.
FULL AND EMPTY BOTH GIVE FRONT EQUAL TO REAR OTHERWISE, SO DISTINGUISHING THEM REQUIRES EITHER A SACRIFICED SLOT OR AN EXPLICIT COUNT.
Circular queue element count
THE NUMBER OF ELEMENTS IS (REAR MINUS FRONT PLUS N) MOD N.
THE PLUS N IS WHAT HANDLES THE WRAP CORRECTLY, BECAUSE A NEGATIVE VALUE MODULO N IS WRONG OR IMPLEMENTATION-DEPENDENT IN MOST LANGUAGES.
Deque and priority queue
A DEQUE PERMITS INSERTION AND DELETION AT BOTH ENDS AND GENERALISES BOTH STACK AND QUEUE. A PRIORITY QUEUE DEQUEUES BY PRIORITY RATHER THAN ARRIVAL ORDER.
RESTRICTING A DEQUE TO ONE END GIVES A STACK AND TO OPPOSITE ENDS GIVES A QUEUE. A PRIORITY QUEUE ABANDONS THE ORDERING CONTRACT AND IS BUILT ON A HEAP.
Queue applications
BREADTH-FIRST TRAVERSAL, FAIR SCHEDULING, AND BUFFERING BETWEEN A FAST PRODUCER AND A SLOW CONSUMER.
REPLACING THE QUEUE WITH A STACK IN A BREADTH-FIRST SEARCH TURNS IT INTO A DEPTH-FIRST ONE, WHICH IS THE ONLY DIFFERENCE BETWEEN THE TWO ALGORITHMS.
Queue from two stacks
PUSH ONTO STACK A. TO DEQUEUE, IF STACK B IS EMPTY, POP EVERYTHING FROM A INTO B, THEN POP FROM B.
EACH ELEMENT IS MOVED AT MOST FOUR TIMES OVER ITS LIFETIME, SO n OPERATIONS COST O(n) IN TOTAL AND THE AMORTISED COST PER OPERATION IS CONSTANT.
Stack from two queues
EITHER PUSH IS CONSTANT AND POP IS LINEAR, OR PUSH IS LINEAR AND POP IS CONSTANT. NO CHEAP SOLUTION EXISTS.
THE ASYMMETRY IS THAT REVERSAL CONVERTS LAST-IN-FIRST-OUT INTO FIRST-IN-FIRST-OUT, AND A STACK CAN REVERSE WHILE A QUEUE CANNOT.
⚠️

Traps GATE sets — and how to dodge them

These are the exact option-traps and misreads that cost marks under negative marking.

WATCH OUT
Assuming linked-list insertion is constant time in general
It is constant only given a pointer to the insertion point. Reaching position i costs linear time, so inserting at an arbitrary index is linear overall, exactly like an array.
WATCH OUT
Deleting a singly linked node without its predecessor
The predecessor's next pointer must be updated, and reaching it costs a full traversal. The copy-successor trick achieves constant time but fails on the last node and dangles any external pointer to the successor.
WATCH OUT
Computing a circular queue's element count as rear minus front
That is negative after a wrap. The correct expression adds the capacity before taking the modulus, since a negative operand modulo N is wrong or implementation-dependent in most languages.
WATCH OUT
Treating front equal to rear as unambiguously empty
It signals both empty and full unless a slot is sacrificed or a count is kept. With one slot sacrificed, a capacity of N holds at most N minus 1 elements and full becomes rear plus one equalling front.
WATCH OUT
Popping postfix operands in the wrong order
The first value popped is the right operand and the second is the left. For subtraction and division this reverses the result, and the error is invisible for addition and multiplication.
WATCH OUT
Popping equal-precedence operators for a right-associative operator
Left-associative operators pop on equal precedence so that the leftmost is applied first. Exponentiation is right-associative, so equal precedence must not be popped, or the grouping comes out backwards.
WATCH OUT
Assuming an array is always slower than a list for insertion
Appending at the end is amortised constant for a dynamic array. Only insertion at an interior position costs linear time, and for small collections the shifting is often faster than pointer chasing.
WATCH OUT
Ignoring the memory overhead of a linked list
A node pays a pointer for every element, often as much storage as the data itself, and scattered nodes defeat the cache. The array's practical advantage exceeds what the asymptotic comparison suggests.
WATCH OUT
Expecting the two-stack queue to be constant time per operation
A single dequeue can cost linear time when a transfer occurs. The guarantee is amortised: each element moves at most four times over its lifetime, so any sequence of n operations costs linear time in total.
WATCH OUT
Expecting a symmetric construction of a stack from two queues
There is none. A stack can reverse a sequence and a queue cannot, so one of push or pop must move every element, giving linear cost on one of the two operations.
WATCH OUT
Forgetting to check underflow and overflow
Popping an empty stack and pushing a full array-based one are both errors that must be tested explicitly. A linked implementation removes overflow but not underflow.
WATCH OUT
Using row-major addressing for a column-major language
C stores rows contiguously so the offset is i times the column count plus j. Column-major reverses the roles, and the two give different addresses for the same indices.

Exam-pattern practice

PYQ-style questions with full solutions. Work through them as a readiness check — mark yourself honestly and get your gap report at the end.

Readiness check

Are you exam-ready for Arrays, Stacks, Queues & Linked Lists?

9 problems from this chapter. Try each one, reveal the worked solution, mark yourself honestly — get your gap report at the end.

9 questions~6 min

5-minute revision

The whole chapter, distilled. Read this the night before the exam.

  • Each structure answers where insertion and deletion are allowed.
  • Array addresses are computed, not searched.
  • Array access is constant; insertion and deletion are linear.
  • Only end operations avoid the shift.
  • Doubling gives amortised constant appends.
  • List insertion is constant given a pointer.
  • Reaching a list position costs linear time.
  • Singly linked deletion needs the predecessor.
  • The copy-successor trick fails on the last node.
  • A doubly linked list makes deletion constant.
  • A circular list removes the end case.
  • A sentinel node removes the empty case.
  • A list pays a pointer per node.
  • Scattered nodes defeat the cache.
  • Stack push and pop are constant in both implementations.
  • Check underflow on pop and overflow on array push.
  • Function calls, brackets and depth-first search all use stacks.
  • Infix conversion pops on higher or equal precedence.
  • Right-associative operators do not pop on equal precedence.
  • Postfix evaluation pops the right operand first.
  • The postfix stack must end with exactly one value.
  • A naive array queue wastes vacated space.
  • A circular queue wraps indices modulo the capacity.
  • Front equal to rear is ambiguous between empty and full.
  • Sacrifice a slot or keep a count to disambiguate.
  • Count is (rear minus front plus N) mod N.
  • A deque generalises stack and queue.
  • A priority queue abandons arrival order.
  • Breadth-first search uses a queue; depth-first uses a stack.
  • Two stacks give a queue at constant amortised cost.
  • Two queues give a stack only with linear cost on one operation.

GATE question blueprint

How this topic is asked, tier by tier — so you can prep to the pattern.

Typical weightage: Programming & Data Structures contributes roughly 8-10 of the 72 core-CS marks; linear structures supply 2-3 of those across 2-3 questions

Question styleMarks eachTypical countWhat it tests
Array costs1~1Operation costs and why contiguity forces shifting
Array versus list2~1Asymptotic and memory-behaviour comparison for a stated workload
Linked lists2~1Deletion cost, the predecessor requirement and what the extra pointer buys
Stack applications1~1Recognising which application uses a stack and why
Infix to postfix2~1Precedence popping, associativity and bracket handling
Postfix evaluation2~1Operand order and maximum stack height
Circular queue2~1Element count across a wrap and the full and empty conditions
Queue from stacks2~1The construction and its amortised analysis

Exam-hall strategy

Battle-tested tips from mentors and toppers for this topic under the sectional clock.

  1. Identify which end the structure permits operations at before reasoning about cost.
  2. For linked-list costs, check whether a pointer to the position is given or must be found.
  3. For circular queues, always add the capacity before taking the modulus.
  4. For infix conversion, pop on equal precedence unless the operator is right-associative.
  5. For postfix evaluation, pop the right operand first and verify one value remains.
  6. For an array-versus-list question, check whether access is by index or by pointer.
  7. Circular-queue counts and stack heights are commonly set as NAT, which carries no negative marking, so never leave one blank.
  8. For 1-mark and 2-mark MCQs, negative marking is -1/3 and -2/3, so guess only after eliminating an option.
  9. GATE gives a single freely-navigable 180-minute window, so flag a long conversion trace and return to it.

Beyond the exam

Where this skill shows up in the job you're competing for — and in life.

Undo in an editor

A stack of edit operations gives undo for free, because the most recent action is exactly the one that must be reversed first.

Print spooling and packet buffering

A queue between a fast producer and a slow consumer absorbs bursts, which is why both printer queues and network receive buffers use the structure.

An LRU cache chain

A doubly linked list makes moving a node to the front constant time, which is exactly what a least-recently-used policy needs on every access.

Expression parsing in a compiler

Infix-to-postfix conversion and postfix evaluation are the same stack algorithms a compiler front end uses to build expression trees.

Where else this topic is tested

Prepare once, score in every exam that asks it.

GATE DAModerate overlap — basic data structures appear, with less emphasis on pointer-level implementation detail
UGC NET Computer ScienceHigh overlap — stack applications, circular queue conditions and array addressing are examined as direct recall
ISRO / BARC / DRDO computer science papersVery high overlap — postfix conversion, circular queue arithmetic and linked-list operations are recurring MCQ topics

Questions aspirants ask

Pulled from the Q&A community and mentor sessions.

Because the constant-time guarantee is conditional on already holding a pointer to the insertion point, and reaching an arbitrary point costs a linear traversal. Inserting at index i in a list therefore costs the same asymptotic O(n) as inserting at index i in an array — the list spends the time walking and the array spends it shifting. The list only genuinely wins when the position arrives as a pointer rather than an index, which happens during a traversal that decides to insert, or in structures such as a free list or an LRU chain where nodes are reached by other means. Beyond the asymptotics, two practical factors favour the array more than the analysis suggests. First, memory overhead: a node in a doubly linked list carries two pointers, which on a 64-bit machine is 16 bytes, so storing 4-byte integers costs five times as much memory as an array would. Second, cache behaviour: an array traversal reads full cache lines and obtains many elements per miss, while list nodes allocated at different times are scattered and can miss on nearly every access. A cache miss costs on the order of a hundred cycles, which dwarfs the handful of cycles a shift would have taken. The result is that for small or traversal-dominated collections, arrays frequently win outright.

Because both conditions leave the two indices equal, and the indices are the only state the naive implementation keeps. Starting from an empty queue with front and rear both at position 0, enqueueing N elements advances rear all the way around until it returns to 0, which is exactly where front still sits. The queue is now full and its indices are indistinguishable from the empty case that preceded it. Two remedies exist. Sacrificing one slot means the queue is declared full when advancing rear by one would collide with front, so rear never actually catches up. The cost is one wasted slot out of N, and the benefit is that no extra state is needed and both tests are a single comparison. Maintaining an explicit count of elements recovers that slot, since empty and full become count equal to zero and count equal to N. The cost is an extra field that must be incremented and decremented on every operation, and which must be kept consistent with the indices under concurrent access. For exam purposes the sacrificed-slot version is the standard, and it is worth memorising both its conditions: empty when front equals rear, and full when rear plus one modulo N equals front. The element count formula must also add N before taking the modulus, because the difference is negative whenever the queue has wrapped.

Because the operands were pushed in left-to-right order, so the right operand is on top when the operator is encountered. Consider the expression 8 2 minus. Scanning left to right, 8 is pushed first and 2 is pushed second, leaving 2 on top. When the minus arrives, the first pop yields 2 and the second yields 8. Since the original infix expression was 8 minus 2, the first popped value must be the subtrahend, that is the right operand. Writing the operation as second-popped minus first-popped gives 8 minus 2 = 6, which is correct, while reversing it would give minus 6. The error is invisible for addition and multiplication, which are commutative, and that is exactly why it survives casual checking and then produces a wrong answer on the subtraction or division in the same expression. Two other checks are worth building into the procedure. The stack must contain exactly one value when the input is exhausted; more than one means the expression had too few operators, and attempting to pop from an empty stack means too many. And the maximum stack height reached equals the maximum number of pending operands, which is a quantity GATE asks for directly and which is read straight off the trace rather than computed separately.

Because a stack reverses order and a queue preserves it, and converting between the two orderings requires exactly one reversal. Two stacks exploit this directly. Elements are pushed onto an input stack in arrival order, so the earliest arrival is at the bottom. Transferring the whole stack to a second stack reverses that, putting the earliest arrival on top, which is precisely first-in-first-out. The transfer is done lazily, only when the output stack runs dry, and once an element has been transferred it is never moved again. Each element therefore experiences at most four stack operations across its whole lifetime, giving an amortised constant cost per queue operation even though an individual dequeue that triggers a transfer costs linear time. Two queues have no reversal primitive to exploit. A queue can only remove from the front and append at the back, so obtaining the most recently added element requires cycling every other element out and back, which is linear work. The choice is only about where to pay it: push can be made constant with a linear pop, by rotating on removal, or pop can be made constant with a linear push, by rotating on insertion. Either way one of the two operations moves every element on every call, and no lazy scheme helps because the required ordering is destroyed and rebuilt each time.

Ask three questions in order, and the answer usually falls out of the first or second. First, what dominates the workload: random access by index, or structural modification? If elements are read by index, the array is not merely faster but asymptotically better, since a list has no way to compute a position. If the workload splices nodes whose pointers are already held, the list wins by the same margin in reverse. Second, where do the modifications happen? Insertions and deletions confined to the end favour a dynamic array, whose amortised cost is constant. Insertions at the front or middle by index cost linear time in both structures, so the supposed list advantage evaporates. Third, is the size known or bounded? A fixed-capacity array is simplest, a dynamic array handles growth at amortised constant cost, and a list handles it with no reallocation at all but pays a pointer per element. In an exam, two specific traps recur. A question that says insertion is frequent may still favour an array if the insertions are at the end. And a question comparing memory should account for the pointer overhead, which for small elements can exceed the data itself, and for a doubly linked list can be four times the payload on a 64-bit machine.
Header Logo