Back to Arrays
Q67Question 67 of 109

Reverse a Sub-list (Reverse Nodes Between Positions p and q)

Key Concepts
  • Array traversal, search, and manipulation
  • Common patterns: two-pointer, prefix sum, sliding window
  • Find max/min, reverse, rotate, remove duplicates
  • Time complexity: O(n) traversal, O(1) access by index
  • Use a dummy node before the head so that reversing from position 1 needs no special case.
  • Advance a pointer `beforeP` by `p-1` steps so it sits just before the segment.
  • next`, the first node of the segment, which will become the segment's tail after reversal.

Definition

Reach the node before `p`, reverse the middle run, then reconnect both boundaries.

Walk to the node just before position p, reverse exactly q-p+1 nodes using the three-pointer technique, then re-stitch the reversed segment back to the node before p and to the node after q. The untouched prefix and suffix keep their links.

In Rajesh's chain of transaction slips, only the slips from the third to the fifth need to be read in reverse while the rest stay as they are. The clerk holds the slip just before the third, flips only that middle run, then clips the flipped run back onto the slip before it and onto the slip after it.

Understanding the Concepts

What is the problem?

Reverse a Sub-list (Reverse Nodes Between Positions p and q). Given the head of a singly linked list and two positions p and q (1-based, p <= q), reverse the nodes from position p to position q in place and return the head. Do it in a single pass with O(1) extra space.

How does the approach work?

Use a dummy node before the head so that reversing from position 1 needs no special case. Advance a pointer beforeP by p-1 steps so it sits just before the segment. Let segStart be beforeP.next, the first node of the segment, which will become the segment's tail after reversal. Then reverse q-p+1 nodes with the standard three-pointer loop. Finally re-stitch: beforeP.next points to the new segment head (previous), and segStart.next points to the node that follows the segment (current).

For example reversing positions 2 to 4 of 1 -> 2 -> 3 -> 4 -> 5 gives 1 -> 4 -> 3 -> 2 -> 5. The dummy makes the p=1 case fall out naturally because beforeP is the dummy.

A senior-level nuance is the re-stitching order and the role of segStart: because segStart is the original first node of the run, after reversal it is the run's last node, so its next is exactly where the suffix must attach. Getting these two reconnections right is the entire difficulty; the reversal itself is the easy part you already know from full-list reversal.

What is the banking use case?

Problem 1 — The National Bank of India displays Rajesh's transaction slips as a linked chain, and for a dispute review only a contiguous window of slips, say the second through the fourth, must be shown in reverse chronological order while the surrounding slips remain in their original order. The chain is long, so the reversal of the window must happen in place and in a single pass. This is exactly reverse-a-sublist: reach the slip before the window, flip just the window, and reconnect the boundaries.

The code contains a SlipNode, a DisputeView with reverseBetween(head, p, q), and a demo.

How do you implement this in Java?

JAVA
// ─── File: src/main/java/com/bank/dispute/SlipNode.java ───
package com.bank.dispute;

public class SlipNode {
    final int id;
    SlipNode next;
    SlipNode(int id) { this.id = id; }
}
JAVA
// ─── File: src/main/java/com/bank/dispute/DisputeView.java ───
package com.bank.dispute;

public class DisputeView {

    public SlipNode reverseBetween(SlipNode head, int p, int q) {
        if (head == null || p == q) return head;
        SlipNode dummy = new SlipNode(-1);
        dummy.next = head;

        SlipNode beforeP = dummy;
        for (int i = 0; i < p - 1; i++) beforeP = beforeP.next;  // node before position p

        SlipNode segStart = beforeP.next;   // becomes the segment TAIL after reversal
        SlipNode previous = null, current = segStart;
        for (int i = 0; i < q - p + 1; i++) {   // reverse exactly q-p+1 nodes
            SlipNode next = current.next;
            current.next = previous;
            previous = current;
            current = next;
        }
        beforeP.next = previous;      // prefix → new segment head
        segStart.next = current;      // segment tail → suffix
        return dummy.next;
    }
}
JAVA
// ─── File: src/main/java/com/bank/dispute/SublistDemo.java ───
package com.bank.dispute;

import java.util.logging.Logger;

public class SublistDemo {
    private static final Logger log = Logger.getLogger(SublistDemo.class.getName());

    public static void main(String[] args) {
        SlipNode head = build(new int[]{1, 2, 3, 4, 5});
        log.info("Original: " + render(head));

        SlipNode out = new DisputeView().reverseBetween(head, 2, 4);
        log.info("Reversed positions 2..4: " + render(out));
    }

    static SlipNode build(int[] ids) {
        SlipNode d = new SlipNode(-1), t = d;
        for (int id : ids) { t.next = new SlipNode(id); t = t.next; }
        return d.next;
    }

    static String render(SlipNode n) {
        StringBuilder sb = new StringBuilder();
            while (n != null) { sb.append(n.id);
            if (n.next != null) sb.append(" -> "); n = n.next; }
        return sb.append(" -> null").toString();
    }
}

What is the project structure?

DIAGRAM
dispute-sublist-reverser/
├── pom.xml
└── src/main/java/com/bank/dispute/
    ├── SublistDemo.java
    ├── SlipNode.java
    └── DisputeView.java

How do you build and run?

BASH
# Build
mvn clean package -DskipTests
# Run
java -cp target/classes com.bank.dispute.SublistDemo

What is the step-by-step walkthrough?

Input data: chain 1 -> 2 -> 3 -> 4 -> 5, p=2, q=4.

Step 1: A dummy node is placed before 1. beforeP advances p-1 = 1 step and lands on node 1. segStart is node 2, which will become the tail of the reversed run.
Step 2: The three-pointer loop reverses q-p+1 = 3 nodes: 2, 3, 4. Internally this produces 4 -> 3 -> 2, with previous ending on node 4 and current ending on node 5.
Step 3: Re-stitch. beforeP.next (node 1's next) becomes node 4, the new head of the run. segStart.next (node 2's next) becomes node 5, the suffix.
Step 4: The final chain is 1 -> 4 -> 3 -> 2 -> 5.

Final output:

CODE
2026-10-27 19:25:42 [INFO] Original: 1 -> 2 -> 3 -> 4 -> 5 -> null
2026-10-27 19:25:42 [INFO] Reversed positions 2..4: 1 -> 4 -> 3 -> 2 -> 5 -> null

What is the high-level design?

DIAGRAM
  ═══ HLD — Dispute Window Reversal ═══

  ┌──────────┐ window ┌────────────────────┐ nodes ┌──────────────┐
  │ Agent UI │───────→│ Dispute Svc ×2     │──────→│ slip chain   │
  └──────────┘        │ reverseBetween(p,q)│       │ (in memory)  │
                      └────────────────────┘       └──────────────┘

What is the low-level design?

DIAGRAM
  ═══ LLD — DisputeView ═══

  ┌─ Key references ────────────────────────────────────┐
  │  dummy → handles p=1 uniformly                      │
  │  segStart → original head of run = tail after rev   │
  └──────────────────────────────────────────────────────┘

  ┌──────────────────────────────────────────┐
  │               DisputeView                 │
  │  + reverseBetween(head,p,q): SlipNode     │  single pass, O(1)
  └──────────────────────────────────────────┘

  Contract for reverseBetween(head,p,q):
    Pre:  1 ≤ p ≤ q ≤ length
    Post: nodes [p..q] reversed; prefix/suffix intact

What is the time and space complexity?

Analyze the solution based on the algorithm used. Consider both the time to process input and the space required for any data structures.

What are common mistakes?

Common mistakes include off-by-one errors, not handling edge cases like empty input, and not considering the constraints of the problem.

What are the edge cases?

Edge cases include empty input, single element input, all identical elements, and boundary values.

What should a senior engineer know?

A senior engineer should understand the time-space tradeoffs, be able to explain the algorithm clearly, and know when this technique applies versus alternatives.

Visual Overview

DIAGRAM
  Input: 1 -> 2 -> 3 -> 4 -> 5   (p=2, q=4)
    │
    ▼
  DisputeView.reverseBetween()
    │ beforeP → node1, segStart = node2
    │ reverse 3 nodes → 4 -> 3 -> 2
    │ node1.next = 4 ; node2.next = 5
    ▼
  Output: 1 -> 4 -> 3 -> 2 -> 5

Key Takeaway

Reversing a sublist between positions p and q is the full-list reversal with two added responsibilities: finding where to start and re-stitching the boundaries afterward. I place a dummy node before the head so that reversing from position one needs no special handling, then I walk a pointer p minus one steps so it rests on the node just before the segment. I remember the first node of the segment separately, because after reversal that original first node becomes the segment's tail, and its next pointer is exactly where the unchanged suffix must reattach.

I then run the familiar three-pointer reversal for exactly q minus p plus one nodes. The real work is the reconnection: the node before the segment now points to the new head of the reversed run, and the segment's original first node, now the tail, points to whatever followed the segment. The prefix and suffix are never touched, so the whole thing is one pass in constant space.

In my work at the National Bank of India this supported dispute review of Rajesh's transaction slips. The slips lived in a long linked chain, and a review needed a contiguous window shown in reverse chronological order while the surrounding slips kept their original order. Reaching the node before the window, flipping only the window in place, and carefully reconnecting both boundaries gave exactly that without allocating a second list.

Remember one sentence: understand the core algorithm and apply it to banking domain problems.

---

Share
Use ← → arrow keys to navigate