31 NVIDIA Java Developer Interview Questions & Answers

nvidia icon

Questions with Detailed ExplanationsWith Detailed Explanations

(Last Updated: August 28, 2026)

1. How would you remove duplicate from linked list?CodingEasyNvidia

Question Details

Given a linked list, remove duplicate elements and explain the data structure choices and edge cases.

Short Interview Answer (30-60 seconds)

I would remove duplicates with one pass through the linked list and a HashSet. The set remembers which values I have already kept. If I see a new value, I link that node into the result list. If I see the same value again, I skip it. A dummy node and a tail pointer make the rebuild simple. This gives O(n) expected time and O(n) auxiliary space.

Detailed Explanation

See the Code while reading this explanation.

The problem gives me a chain of numbers and asks me to keep only the first time each number appears. If the same number shows up again later, I remove that later copy. The order of the first copies must stay the same. In the shown example, 1→2→3→2→4→3→5 becomes 1→2→3→4→5. I use a simple memory of values I have already kept, so I can decide quickly whether to keep or skip each new item. That makes it easy to know whether to keep the new item or skip it without changing the order.

Useful Questions to Ask the Interviewer
  1. Should I keep the first copy of each value and preserve the original order?
  2. Can I reuse the existing nodes, or should I build a new linked list?
How would you remove duplicate from linked list? diagram
How to Explain It in an Interview
1. Understand the input and required output

The input is the head of a singly linked list. The output is the head of the list after duplicate values are removed. In the diagram example, the first copies stay in order, so 1→2→3→2→4→3→5 becomes 1→2→3→4→5.

2. Choose the algorithm and data structure

I use a HashSet to remember values I have already seen. The set stores only values from earlier nodes. That is the key invariant. If the current value is new, I keep it. If it is already in the set, I skip that node.

3. Initialize the state

I start with an empty set, a dummy node, and a tail pointer. The dummy node makes head handling easy. The tail pointer always points to the last node in the result list. The traversal starts at the original head.

4. Walk through the example

For 1→2→3→2→4→3→5: 1. See 1. It is new, so I keep it. 2. See 2. It is new, so I keep it. 3. See 3. It is new, so I keep it. 4. See 2 again. It is already in the set, so I skip it. 5. See 4. It is new, so I keep it. 6. See 3 again. It is already in the set, so I skip it. 7. See 5. It is new, so I keep it. The result is 1→2→3→4→5.

5. Explain why the result is correct

The invariant is simple. The set always contains the values from nodes that were already kept in the result list. So when I see a value again, I know it is a duplicate of an earlier kept node. That means I can safely skip it and still keep the first copy only.

6. Explain the Java implementation

The method takes the head node as input. It creates a HashSet, a dummy node, and a tail pointer. Then it walks through the list with a current pointer. For each node, it checks whether the value is already in the set. If not, it adds the value to the set and links that node after tail. After the loop, it returns dummy.next as the new head.

7. Explain complexity and edge cases

The list is processed at most once. HashSet lookup and insertion are O(1) on average, so the total time is O(n) expected time. The extra memory is O(n) because the set can grow with the number of distinct values. Important edge cases are an empty list, a list with one node, a list with all unique values, and a list where all nodes have the same value.

Key Insight / Why This Solution Works

The key idea is to keep only the first time each value appears. The HashSet stores values from earlier nodes. That gives the invariant: every value in the result list has already been recorded once, and no later duplicate can enter the result. I use a dummy node and tail pointer so I can rebuild the list in the same order without special head logic. This is why one pass is enough and why the first copy of each value stays in place.

Code
import java.util.HashSet;

public class Main {

    static class ListNode {

        int val;
        ListNode next;

        ListNode(int val) {
            this.val = val;
        }
    }

    public static ListNode removeDuplicates(ListNode head) {
        // Defensive fallback for an empty list.
        if (head == null) {
            return null;
        }

        // seen stores every value that we have already kept in the result list.
        HashSet<Integer> seen = new HashSet<>();

        // Dummy node makes it easy to build the answer, even when the first node is kept.
        ListNode dummy = new ListNode(0);
        // Tail always points to the last node in the rebuilt list.
        ListNode tail = dummy;
        // curr walks through the original linked list.
        ListNode curr = head;

        while (curr != null) {
            // If this value was not seen before, keep the node.
            if (!seen.contains(curr.val)) {
                seen.add(curr.val); // Remember this value so later duplicates are skipped.
                tail.next = curr; // Attach the current node to the result list.
                tail = tail.next; // Move tail to the new last node.
            }
            // Move to the next original node.
            curr = curr.next;
        }

        // Cut off any remaining old links after the last kept node.
        tail.next = null;

        // The real head is the node after the dummy node.
        return dummy.next;
    }

    private static ListNode buildExample() {
        // Build the exact example shown in the diagram: 1 -> 2 -> 3 -> 2 -> 4 -> 3 -> 5.
        ListNode n1 = new ListNode(1);
        ListNode n2 = new ListNode(2);
        ListNode n3 = new ListNode(3);
        ListNode n4 = new ListNode(2);
        ListNode n5 = new ListNode(4);
        ListNode n6 = new ListNode(3);
        ListNode n7 = new ListNode(5);

        n1.next = n2;
        n2.next = n3;
        n3.next = n4;
        n4.next = n5;
        n5.next = n6;
        n6.next = n7;

        return n1;
    }

    private static void printList(ListNode head) {
        // Print the final list in the same arrow format used in the diagram.
        ListNode curr = head;
        while (curr != null) {
            System.out.print(curr.val);
            if (curr.next != null) {
                System.out.print(" -> ");
            }
            curr = curr.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // Run the exact diagram example.
        ListNode head = buildExample();
        ListNode result = removeDuplicates(head);

        // Expected output: 1 -> 2 -> 3 -> 4 -> 5
        printList(result);
    }
}
Time & Space Complexity

I process the linked list at most once. HashSet lookup and insertion are O(1) on average in Java. So the running time is O(n) expected time, where n is the number of nodes.

The extra memory is O(n) because the set may need one entry for every distinct value. The dummy node and pointers use only constant extra space. The main tradeoff is speed versus memory: I use extra memory to avoid a slower nested scan.

Where it is used

This is useful when cleaning linked data where repeated records should be removed but the first copy must stay. It also works well for log-like streams, imported contact lists, and other ordered data where later duplicates should be dropped without changing the original order of the first valid items.

Why Interviewers Ask This

The interviewer wants to see whether you can recognize a simple linked-list pattern and choose the right helper structure. This question checks whether you can preserve order, handle duplicates correctly, and reason about node links without losing part of the list. It also shows whether you can explain Java code clearly and give the right expected-time and auxiliary-space complexity without overclaiming.

Common interview mistakes

A common mistake is to keep the wrong copy of a value. Here, the first copy must stay, and later copies must be skipped. Another mistake is to forget the HashSet check and then rebuild the list with duplicates still inside it. Some candidates also forget to set tail.next = null at the end, so old links can remain. A final mistake is to claim O(1) extra space, even though the set grows with the number of distinct values.

Interview tip

Say the invariant out loud: the set contains only values from nodes already kept in the result. That makes the duplicate check easy to justify.

Interviewer may ask next
How would you remove duplicates without using a HashSet?

I would use two pointers. For each node, I would scan the rest of the list and remove later nodes with the same value. That keeps O(1) extra space, but the time becomes O(n^2) because each node may compare with many later nodes. The correctness idea is the same: keep the first copy and delete later copies.

What if the linked list is already sorted?

If the list is sorted, duplicates are next to each other. Then I do not need a HashSet. I only compare the current node with the next node. If the values are equal, I skip the next node. This keeps O(n) time and O(1) extra space, and it is simpler because equal values are grouped together.

2. How would you write a program to shuffle the elements of an array?CodingEasyNvidia

Question Details

Shuffle the elements of an array and explain how you would keep the result unbiased.

Short Interview Answer (30-60 seconds)

I would shuffle the array in place with the Fisher-Yates algorithm. I start at the last index and move left. At each step, I pick a random index j from 0 to i and swap a[i] with a[j]. That keeps the shuffle unbiased, because each remaining position has the same chance to be chosen. I process the array at most once. The time is O(n), and the extra space is O(1).

Detailed Explanation

See the Code while reading this explanation.

This problem asks you to rearrange the numbers in an array so the order looks random and fair. Fair means every possible ordering has the same chance. A good way to do that is to lock one position at a time from the end, choose one random element from the still-free part, and swap it into place. That keeps the shuffle unbiased because each remaining element is picked with equal probability before the next position is fixed. That is the Fisher-Yates shuffle.

Useful Questions to Ask the Interviewer
  1. Should I shuffle the array in place, or may I return a new array?
  2. Do you want a normal random source, or a stronger one like SecureRandom?
How would you write a program to shuffle the elements of an array? diagram
How to Explain It in an Interview
1. Understand the input and output

The input is an integer array. The output is the same array in a random order. The goal is not to sort it. The goal is to create one fair permutation.

2. Choose Fisher-Yates and keep it fair

I use the Fisher-Yates shuffle. I walk from right to left. At index i, I pick a random j from 0 to i. Then I swap a[i] and a[j]. This is fair because each of the i + 1 choices is equally likely.

3. Walk through the example

The diagram uses [1, 2, 3, 4, 5]. First, i = 4 and j = 1, so 5 swaps with 2 and the array becomes [1, 5, 3, 4, 2]. Then i = 3 and j = 3, so nothing changes. Next, i = 2 and j = 0, so 3 swaps with 1 and the array becomes [3, 5, 1, 4, 2]. Then i = 1 and j = 1, so nothing changes. One valid shuffled result is [3, 5, 1, 4, 2].

4. Explain the Java code and complexity

The Java code uses SecureRandom to pick the random index. The loop starts at the last index and stops at 1. Each step does one random pick and one swap. That matches the diagram exactly. The time is O(n). The extra space is O(1).

Key Insight / Why This Solution Works

The key idea is to fill the array from right to left. At step i, every element in positions 0 through i is still free. I choose one of those elements uniformly at random and swap it into position i. After that swap, position i is final and will not change again. The central invariant is that the suffix to the right of i is already a uniformly random shuffle of the values it contains. Because each remaining choice is equally likely, the final array is unbiased.

Code
import java.security.SecureRandom;
import java.util.Arrays;

public class Main {

    private static final SecureRandom RNG = new SecureRandom();

    public static void shuffle(int[] a) {
        // Walk from right to left. Each pass fixes one final position.
        for (int i = a.length - 1; i > 0; i--) {
            // Pick one free index from 0..i with equal chance.
            int j = RNG.nextInt(i + 1);

            // Swap the chosen value into position i.
            int tmp = a[i];
            a[i] = a[j];
            a[j] = tmp;
        }
    }

    public static void main(String[] args) {
        // Example from the diagram.
        int[] arr = { 1, 2, 3, 4, 5 };

        // Shuffle the array in place.
        shuffle(arr);

        // Print one valid shuffled result.
        System.out.println(Arrays.toString(arr));
    }
}
Time & Space Complexity

We look at the array one position at a time from right to left. That means the array is processed at most once. Each step does one random pick and one swap, so the total time is O(n). We do not build another array. We only use a few extra variables, so the extra space is O(1).

Where it is used

This pattern is useful when you need a fair random order. Examples include shuffling cards, randomizing quiz questions, picking a random presentation order, and running simulations or games.

Why Interviewers Ask This

They want to see whether you know the correct shuffle pattern, can keep it unbiased, and can explain why the random range shrinks at each step. They also check that you preserve in-place behavior, handle duplicates naturally, and give the right complexity. This question is a good test of careful reasoning, because a small range mistake can make the shuffle biased even if the code looks correct.

Common interview mistakes

A common mistake is picking j from the whole array every time. That makes the shuffle biased. Another mistake is forgetting that the random range must shrink as i moves left. Some candidates also create a second array when the goal is an in-place shuffle. Another easy mistake is thinking j can never equal i. It can, and that is part of the algorithm. Finally, people sometimes claim the result is only one specific order, but any permutation is valid.

Interview tip

Say the invariant out loud: the suffix on the right is already fixed and uniformly random. Then show how one swap extends that suffix by one more position.

Interviewer may ask next
What changes if I need to keep the original array unchanged?

I would copy the array first, shuffle the copy with the same Fisher-Yates steps, and return the copy. The fairness stays the same. The time is still O(n), but the extra space becomes O(n) because of the copy.

What changes if I only want to shuffle a subarray?

I would limit the loop to the chosen range. At each step, I would pick j only inside that range and swap inside the same range. The same invariant still works. The time is O(k) for a subarray of length k, and the extra space stays O(1).

3. There are two large arrays filled with random 64-bit signed numbers. How do you determine what are the common numbers in the arrays?CodingMediumNvidia

Question Details

Design a linear-time algorithm to find the common numbers in two large arrays and explain the memory tradeoffs.

Short Interview Answer (30-60 seconds)

I find the common numbers by first putting every value from array A into a HashSet, then scanning array B and adding only the values that are already in that set into a second set. This keeps each common number once, even if it appears many times. I process each array at most once. Because HashSet lookup and insert are O(1) on average, the total time is O(n + m) expected time, and the extra memory is O(n + min(n, m)).

Detailed Explanation

See the Code while reading this explanation.

This question asks me to compare two big lists of numbers and return the values that appear in both lists. I do not need the positions. I only need each common value once. The best approach is to remember the values from the first list, then check each value in the second list against that memory. That makes each check fast and keeps the answer unique. It also uses extra space only for the values I store, so it is a good fit when the arrays are very large.

Useful Questions to Ask the Interviewer
  1. Do you want each common value only once, or should duplicates be counted too?
  2. Does the output need a specific order, or is any order fine?
There are two large arrays filled with random 64-bit signed numbers. How do you determine what are the common numbers in the arrays? diagram
How to Explain It in an Interview
1. Understand the input and required output

The input is two arrays of 64-bit signed numbers. The output is the distinct numbers that appear in both arrays. The diagram shows values, not indices. It also shows that the result is a set of common numbers, so duplicates should not appear twice.

2. Choose the algorithm and data structure

I use a HashSet. First, I store every value from array A in one set called seen. Then I scan array B. If a value from B is already in seen, I add it to the result set. This is fast because set lookup is quick on average, and duplicates are removed automatically.

3. Initialize the state

I start with an empty seen set and an empty result set. seen means values that were already found in A. result means values that were found in both A and B. The first loop begins at the start of array A. The second loop begins at the start of array B. The invariant is simple: seen always contains the unique values from A that have been processed so far.

4. Walk through the example

The diagram uses: A = [7, -3, 15, 22, -3, 42, 7, 19, 100] B = [19, 7, -3, 88, 42, 100, 7, -3, 5]

First, I add every value from A into seen. After that pass, seen contains {7, -3, 15, 22, 42, 19, 100}. Then I scan B in order. 19 is in seen, so I add it. 7 is in seen, so I add it. -3 is in seen, so I add it. 88 is not in seen, so I skip it. 42 is in seen, so I add it. 100 is in seen, so I add it. The next 7 and -3 are already in the result set, so they do not change the answer. 5 is not in seen, so I skip it. The final distinct common values are {-3, 7, 19, 42, 100}.

5. Explain why the result is correct

Every value in seen came from A. When I add a value from B only if it is already in seen, that value must be in both arrays. The result set removes duplicates, so each common value appears once. That is the key invariant. We process each element at most once and stop when the scan is done.

6. Explain the Java implementation

The Java code creates a HashSet<Long> for seen and fills it with all values from A. Then it creates another HashSet<Long> for common and scans B. For each value in B, it checks seen.contains(value). If the check is true, it adds the value to common. At the end, it converts the set to a list and returns it. The main method runs the exact example from the diagram.

7. Explain complexity and edge cases

The solution takes O(n + m) expected time because it scans each array once and set operations are average O(1). The extra memory is O(n + min(n, m)) because seen can store all unique values from A and common can store up to the smaller unique count from the two arrays. Important edge cases are empty arrays, no common numbers, duplicate values, negative numbers, and zero.

Key Insight / Why This Solution Works

The key idea is to use a HashSet for fast membership checks. First, I insert every value from A into seen. Then I scan B. If a value from B is already in seen, I add it to common. The invariant is that seen contains the unique values from A, and common contains only unique values that appear in both arrays. This works because every common value must be present in both arrays, and the set automatically removes duplicates.

Code
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Main {

    public static List<Long> commonNumbers(long[] A, long[] B) {
        // Defensive fallback: if one input is missing, there can be no common numbers.
        if (A == null || B == null) {
            return Collections.emptyList();
        }

        // seen stores every unique value from array A.
        // The initial capacity is sized to reduce rehashing on large inputs.
        Set<Long> seen = new HashSet<>(A.length * 2);
        for (long value : A) {
            // Add each value from A once. Duplicate values do not change the set.
            seen.add(value);
        }

        // common stores the distinct values that appear in both arrays.
        Set<Long> common = new HashSet<>();
        for (long value : B) {
            // Check B against the values already stored from A.
            // If the value is in A, it belongs in the intersection.
            if (seen.contains(value)) {
                common.add(value);
            }
        }

        // Convert the set to a list for the final answer.
        // The order is not guaranteed because sets do not promise a fixed order here.
        return new ArrayList<>(common);
    }

    public static void main(String[] args) {
        // Example from the diagram.
        long[] A = { 7, -3, 15, 22, -3, 42, 7, 19, 100 };
        long[] B = { 19, 7, -3, 88, 42, 100, 7, -3, 5 };

        List<Long> answer = commonNumbers(A, B);
        System.out.println(answer); // Possible output: [-3, 7, 19, 42, 100]
    }
}
Time & Space Complexity

We walk through A once and B once. HashSet lookup and insertion are O(1) on average, so the total time is O(n + m) expected time. The extra memory is the seen set plus the common set, so the auxiliary space is O(n + min(n, m)). That matches the memory tradeoff shown in the diagram.

Where it is used

This pattern is useful when you need fast membership checks. It works well for intersections, de-duplicating values, checking whether data appears in two feeds, and joining two large lists in backend code.

Why Interviewers Ask This

The interviewer wants to see whether I can turn a simple-sounding comparison problem into a fast membership check solution. They are checking if I choose the right data structure, handle duplicates correctly, explain expected time honestly, and keep the logic simple. They also want to see whether I can connect the code, the example, and the complexity without mixing up values, order, or memory tradeoffs.

Common interview mistakes

A common mistake is returning values with duplicates instead of a distinct set. Another mistake is scanning B without first storing A, which makes membership checks slow. Some candidates also forget that negative numbers, zero, and repeated values must work. Another mistake is assuming the output order is guaranteed when a HashSet does not promise that. Finally, do not claim worst-case guaranteed O(1) behavior for hashing.

Interview tip

Say the invariant out loud: seen holds all unique values from A, and common only grows when a value from B is already in seen.

Interviewer may ask next
What changes if I need to keep the common numbers in the order they first appear in B?

I would keep the same two-pass idea, but I would use a LinkedHashSet for the result or a list with a second set to avoid duplicates. That preserves the first time each common value appears in B. The correctness stays the same. The time remains O(n + m) expected time, and the space stays O(n + min(n, m)). The tradeoff is a little more memory to keep order.

What changes if the input arrives as a stream and I cannot keep all of A in memory?

I would need a different memory tradeoff. If A is too large to store, I could process A in chunks and keep a smaller summary only when that is enough for the business rule. If exact results are still required, I still need some form of membership storage for the values from A. The main tradeoff is that exact intersection needs memory for the values I must remember.

4. How would you sum a billion long array of floating point numbers to achieve the best possible accuracy?CodingHardNvidia

Question Details

Sum a very large floating-point array with maximum numerical accuracy and explain the numerical stability considerations.

Short Interview Answer (30-60 seconds)

I would keep the running sum in two doubles, hi and lo, so tiny rounding losses are not thrown away. For each value, I add it to the high part, capture the error, fold that error into the low part, and renormalize the pair. That keeps the best possible double result for the numbers seen so far. I process each item at most once. The time is O(n), and the extra space is O(1).

Detailed Explanation

See the Code while reading this explanation.

I would add the numbers in a way that keeps the small bits that normal addition can drop. A plain left-to-right sum can lose tiny values when very large values come first. So I keep a main total and a small leftover part. Each new number updates both parts before I move on. That gives the closest double result while still using one pass and very little memory. It is a good fit when the array is huge and the order of addition affects the final answer.

Useful Questions to Ask the Interviewer
  1. Should I return one best double value, or do you want the exact mathematical sum too?
  2. Can the input contain NaN or infinity, and should I follow normal Java double rules?
How would you sum a billion long array of floating point numbers to achieve the best possible accuracy? diagram
How to Explain It in an Interview
1. Understand the input and output

The input is a very large array of floating-point numbers. The output is one double. The goal is the most accurate value that a double can hold.

2. Keep two running parts

I keep hi for the main total and lo for the tiny leftover bits. Together, hi + lo is the best running answer for the values I have already processed.

3. Walk through the example

The diagram uses [1e16, 1.0, -1e16, 1.0]. A normal sum loses the first 1.0. The compensated sum keeps it. The final result is 2.0.

4. Explain the exact state changes

I start with hi = 0.0 and lo = 0.0. After 1e16, the state becomes (1e16, 0). After 1.0, the low part stores 1.0. After -1e16, the high part drops back to 0, but the low part still keeps 1.0. After the last 1.0, the pair becomes (1.0, 1.0), so hi + lo = 2.0.

5. Explain why it is correct

The key invariant is simple: hi holds the main sum and lo holds the lost rounding error. Each new number is added in a way that keeps those lost bits instead of discarding them. That is why the result is much more accurate than a plain running sum.

6. Explain the Java code

The Java code does the same three-step repair for each value. First it adds the value to hi. Then it adds the error into lo. Then it renormalizes the pair so the large part stays in hi and the small part stays in lo.

7. Complexity and edge cases

The code reads the array once, so the time is O(n). It uses only a few doubles, so the extra space is O(1). If the array is empty, the result is 0.0. If the array contains NaN or infinity, Java double rules apply.

Key Insight / Why This Solution Works

The key idea is to store the sum as two doubles, hi and lo. hi keeps the main value. lo keeps the small error that would usually be lost to rounding. For each new number, I first add it to hi, then I add the rounding error into lo, and then I renormalize the pair. The invariant is that hi + lo is the most accurate running double for the numbers already seen. This is better than a plain left-to-right sum because it preserves tiny values that would otherwise disappear.

Code
public class Main {

    public static void main(String[] args) {
        // Example from the diagram.
        double[] values = { 1e16, 1.0, -1e16, 1.0 };
        System.out.println(sumAccurate(values)); // 2.0
    }

    public static double sumAccurate(double[] a) {
        // Defensive fallback for an empty array. The diagram's edge case says 0.0.
        if (a == null || a.length == 0) {
            return 0.0;
        }

        double hi = 0.0; // Main part of the running sum.
        double lo = 0.0; // Low part that stores lost rounding error.

        for (double x : a) {
            // First compensated addition:
            // s is the rounded sum, and e is the exact error from adding x to hi.
            double s = hi + x;
            double bb = s - hi;
            double e = hi - (s - bb) + (x - bb);

            // Second compensated addition:
            // fold the new error into the low part and capture any new error there.
            double t = lo + e;
            bb = t - lo;
            double f = lo - (t - bb) + (e - bb);

            // Renormalize the pair so hi keeps the large part and lo keeps the leftover bits.
            hi = s + t;
            bb = hi - s;
            lo = s - (hi - bb) + (t - bb) + f;
        }

        // Return the most accurate double the pair represents.
        return hi + lo;
    }
}
Time & Space Complexity

We read the array once, so the time grows linearly with the number of values: O(n). We only keep hi, lo, and a few temporary doubles, so the extra memory is O(1). This is a sequential algorithm, so the order of addition matters for accuracy.

Where it is used

This pattern is useful in scientific code, simulations, finance, and signal processing. It helps when many floating-point additions would otherwise lose small values.

Why Interviewers Ask This

They want to see whether I understand floating-point error, not just plain addition. They also want to know if I can keep small values from disappearing, preserve the processing order, and explain the result clearly in Java. This question checks numerical stability, the hi and lo invariant, and the tradeoff between accuracy, speed, and memory.

Common interview mistakes

A common mistake is using a plain running sum and losing tiny values when a huge value comes first. Another mistake is returning only hi and forgetting the low part that still holds useful error. Some candidates change the order of addition or sort the values, which changes the rounding behavior. It is also easy to think the goal is the exact mathematical sum, but the diagram asks for the best possible double result.

Interview tip

Say the invariant out loud: hi keeps the main sum, lo keeps the lost bits, and each loop step repairs the rounding error before the next number arrives.

Interviewer may ask next
What changes if the numbers arrive as a stream instead of an array?

Nothing important changes in the algorithm. I keep the same hi and lo state and update them for each new number as it arrives. The invariant stays the same, so the result is still the most accurate double for the processed prefix. Time stays O(n) and extra space stays O(1).

What changes if the interviewer wants the exact mathematical sum?

I would switch to exact arithmetic, such as BigDecimal, instead of a double-double pair. That gives an exact result, but it uses more time and much more memory. The tradeoff is clear: exactness goes up, but performance goes down.

5. How would you find if there is a loop in a singly linked list?CodingEasyNvidia

Question Details

Given a singly linked list, determine whether it contains a cycle and explain the edge cases.

Short Interview Answer (30-60 seconds)

I would use Floyd’s cycle detection with two pointers. Slow moves one node at a time and fast moves two. If there is a loop, the fast pointer will eventually catch the slow pointer inside the cycle. If fast reaches null, there is no loop. I process each node at most once and stop when the answer is found. That gives O(n) time and O(1) extra space.

Detailed Explanation

See the Code while reading this explanation.

This problem asks whether a chain of linked items ever comes back to an earlier item and starts repeating. I begin at the first item and move through the chain in two ways. One moves slowly and the other moves faster. If there is a loop, the fast one will eventually catch the slow one inside that loop. If the fast one reaches the end, then the chain stops normally and there is no loop. This fits well because it finds a loop without storing the whole chain.

Useful Questions to Ask the Interviewer
  1. Do you only want a yes or no answer, or should I also find the node where the loop starts?
  2. Can I assume the list is not changing while I traverse it?
How would you find if there is a loop in a singly linked list? diagram
How to Explain It in an Interview
1. Understand the input and required output

The input is the head node of a singly linked list. The output is a boolean. I only need to tell whether a cycle exists. The answer must use node references, not node values.

2. Choose the algorithm and data structure

I use Floyd’s cycle detection. I keep two node references. Slow moves one step. Fast moves two steps. The invariant is simple. If there is a cycle, the fast pointer will eventually meet the slow pointer inside the loop. If fast reaches null, the list ends and there is no cycle.

3. Initialize the state

I start both pointers at the head. That matches the diagram. If the list is empty or has one node with no self-loop, I can return false early because no cycle is possible.

4. Walk through the example

The diagram uses the list 1 -> 2 -> 3 -> 4 -> 5 and then the tail points back to node 3. Step 0: both pointers start at node 1. Step 1: slow moves to 2 and fast moves to 3. Step 2: slow moves to 3 and fast moves to 5. Step 3: slow moves to 4 and fast moves to 4. They meet, so the list has a cycle. The diagram also shows the optional start-of-loop idea. If I needed the entry node, I would move one pointer back to head and move both one step at a time until they meet at node 3.

5. Explain why the result is correct

The key reason is that fast moves faster than slow. Inside a cycle, fast keeps closing the gap. So if a cycle exists, the two pointers must meet. If there is no cycle, fast reaches the end first. That is why the method returns true for a loop and false for a normal list.

6. Explain the Java implementation

The Java code checks the easy empty-list cases first. Then it creates slow and fast pointers at the head. The while loop runs only while fast can safely move two steps. Inside the loop, slow moves one step and fast moves two steps. After each move, the code checks whether slow and fast point to the same node. If they do, it returns true. If the loop ends, fast reached null, so the code returns false.

7. Explain complexity and edge cases

The time is O(n) because the pointers move through the list in linear time. The extra space is O(1) because I only keep two pointers. Important edge cases are an empty list, one node with no cycle, one node that points to itself, a cycle that starts at the head, and a cycle that starts later in the list.

Key Insight / Why This Solution Works

The key idea is Floyd’s cycle detection. I keep two node references. Slow moves one step. Fast moves two steps. If the list has no cycle, fast reaches the end and I return false. If the list has a cycle, fast keeps moving inside the loop and must eventually meet slow. That meeting proves the cycle exists. The invariant is that fast keeps closing the gap whenever both pointers are in the same cycle.

Code
class ListNode {

    int val;
    ListNode next;

    ListNode(int val) {
        this.val = val;
    }
}

class Solution {

    public boolean hasCycle(ListNode head) {
        // Empty list or one node without a self-loop cannot form a cycle.
        if (head == null || head.next == null) {
            return false;
        }

        // Slow moves one step. Fast moves two steps.
        ListNode slow = head;
        ListNode fast = head;

        // Keep walking only while fast can safely move two steps.
        while (fast != null && fast.next != null) {
            slow = slow.next; // Move slow by one node.
            fast = fast.next.next; // Move fast by two nodes.

            // If both references point to the same node, the list loops.
            if (slow == fast) {
                return true;
            }
        }

        // Fast reached the end, so there is no cycle.
        return false;
    }
}

public class Main {

    public static void main(String[] args) {
        // Build the exact diagram example: 1 -> 2 -> 3 -> 4 -> 5 -> 3 ...
        ListNode n1 = new ListNode(1);
        ListNode n2 = new ListNode(2);
        ListNode n3 = new ListNode(3);
        ListNode n4 = new ListNode(4);
        ListNode n5 = new ListNode(5);

        n1.next = n2;
        n2.next = n3;
        n3.next = n4;
        n4.next = n5;
        n5.next = n3; // Tail connects back to node 3.

        Solution solution = new Solution();
        System.out.println(solution.hasCycle(n1)); // true
    }
}
Time & Space Complexity

We process the input at most once. The fast pointer either reaches the end or meets the slow pointer inside a loop. So the time is O(n). I do not use a map or set. I only keep two pointers, so the extra memory is O(1).

Where it is used

I use this pattern when I need to check a linked structure for cycles, such as linked list code, object chains, or any pointer path that must not loop forever.

Why Interviewers Ask This

The interviewer wants to see whether I recognize the fast and slow pointer pattern. They also want to see if I can guard null cases, keep node references straight, and explain the invariant in simple words. This question tests early stopping, correct Java pointer code, and honest complexity reasoning. It also shows whether I know when O(1) extra space is possible and when a linked list can loop forever if I do not detect it.

Common interview mistakes

A common mistake is to compare node values instead of node references. Another mistake is to move the fast pointer without first checking that fast and fast.next are not null. Some candidates move both pointers by one step, which can miss the cycle. Others forget that an empty list and a single node without a self-loop both return false. Also, stop as soon as the two pointers meet.

Interview tip

Say the invariant out loud. Fast moves twice as fast as slow, so if a cycle exists, fast must eventually catch slow inside the loop.

Interviewer may ask next
How would you find the node where the loop starts?

After slow and fast meet, move one pointer back to head and move both one step at a time. The node where they meet again is the start of the loop. The time stays O(n) and the extra space stays O(1).

How would you count the number of nodes in the loop?

After the two pointers meet, keep one pointer fixed and move the other until it comes back to the same node. Count those moves. That count is the loop length. This also takes O(n) time and O(1) space.

6. How would you find the meeting point in two linked lists?CodingEasyNvidia

Question Details

Given two linked lists, find their intersection or meeting node and explain the approach.

Short Interview Answer (30-60 seconds)

I would use two pointers. Each pointer starts at one head, then switches to the other list when it reaches the end. That makes both pointers walk the same total distance, so they meet at the first shared node by reference, or at null if the lists do not intersect. I process each node at most once, so the time is O(m + n) and the extra space is O(1).

Detailed Explanation

See the Code while reading this explanation.

We need to return the first shared node of two linked lists. The shared part is by reference, not by value. The idea in the diagram is to use two pointers. Each pointer walks one list, then switches to the other list when it reaches the end. That makes both pointers travel the same total distance. So they meet at the shared node, or at null if there is no intersection.

Useful Questions to Ask the Interviewer
  1. Should I return the node reference itself, or only its value?
  2. Can I assume both lists are acyclic?
How would you find the meeting point in two linked lists? diagram
How to Explain It in an Interview
1. Understand the input and output

The input is two singly linked lists. The output is the first common node by reference, not the first equal value. In the example, list A is 4 → 1 → 8 → 10 → 5 and list B is 6 → 3 → 10 → 5. The first shared node is the node with value 10.

2. Choose the algorithm and data structure

Use two pointers, one for each list. No extra data structure is needed. The key idea is that each pointer will walk both lists, so each one covers the same total distance. That is why they line up at the meeting point.

3. Initialize the state

Set pA = headA and pB = headB. These pointers store node references, not values. Start both pointers at the two list heads. The invariant is simple: both pointers always move one node at a time.

4. Walk through the example

pA starts on 4, then 1, then 8, then 10. pB starts on 6, then 3, then 10. When a pointer reaches null, it switches to the other list head. So pA later walks through list B, and pB later walks through list A. Because both pointers travel the same total distance, they meet at the shared node with value 10.

5. Explain why the result is correct

Each pointer travels lenA + lenB nodes in total. If the lists share a tail, the extra nodes in one list are canceled by the switch to the other head. That is why the pointers arrive at the first shared node together. If there is no shared node, both pointers become null at the same time.

6. Explain the Java implementation

The loop runs while pA != pB. Inside the loop, each pointer either moves to next or jumps to the other head when it becomes null. When the loop stops, pA and pB are the same reference. That reference is the meeting node, or null if there is no intersection.

7. Explain complexity and edge cases

The time is O(m + n), where m and n are the lengths of the two lists. The extra space is O(1) because the code uses only two pointers. Important edge cases are a shared head, one empty list, different list lengths, and no intersection.

Key Insight / Why This Solution Works

The key invariant is that both pointers walk the same total distance. Pointer A starts at headA and pointer B starts at headB. When one pointer reaches the end, it jumps to the other list's head. That equalizes the path length. If the lists intersect, the pointers meet at the first shared node by reference. If they do not intersect, both pointers become null together. This is why the two-pointer solution is correct and uses only O(1) extra space.

Code
public class Main {

    static class ListNode {

        int val;
        ListNode next;

        ListNode(int val) {
            this.val = val;
        }
    }

    public static ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // Start one pointer at each head.
        ListNode pA = headA;
        ListNode pB = headB;

        // Move both pointers one step at a time.
        // When a pointer reaches the end, switch it to the other list head.
        while (pA != pB) {
            // If pA is null, jump to headB. Otherwise move to the next node.
            pA = pA == null ? headB : pA.next;

            // If pB is null, jump to headA. Otherwise move to the next node.
            pB = pB == null ? headA : pB.next;
        }

        // pA and pB are the same node reference here.
        // That node is the meeting point, or null if the lists do not intersect.
        return pA;
    }

    private static void printList(ListNode head) {
        ListNode current = head;
        while (current != null) {
            System.out.print(current.val);
            if (current.next != null) {
                System.out.print(" -> ");
            }
            current = current.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // Example from the diagram:
        // List A: 4 -> 1 -> 8 -> 10 -> 5
        // List B: 6 -> 3 -> 10 -> 5
        // The shared tail starts at the node with value 10.

        ListNode shared10 = new ListNode(10);
        ListNode shared5 = new ListNode(5);
        shared10.next = shared5;

        ListNode a4 = new ListNode(4);
        ListNode a1 = new ListNode(1);
        ListNode a8 = new ListNode(8);
        a4.next = a1;
        a1.next = a8;
        a8.next = shared10;

        ListNode b6 = new ListNode(6);
        ListNode b3 = new ListNode(3);
        b6.next = b3;
        b3.next = shared10;

        System.out.print("List A: ");
        printList(a4);
        System.out.print("List B: ");
        printList(b6);

        ListNode intersection = getIntersectionNode(a4, b6);
        if (intersection != null) {
            System.out.println("Intersection node value: " + intersection.val);
        } else {
            System.out.println("Intersection node value: null");
        }
    }
}
Time & Space Complexity

The code walks through the two lists with two pointers. Each pointer may visit both lists once, so the total work is O(m + n). We process each node at most once and stop as soon as the two pointers are the same. The extra memory is O(1) because we keep only the two pointers and no map or set.

Where it is used

This pattern is useful when two linked structures may join and you need the first shared object. It appears in linked list questions, shared history chains, and reference tracking problems where identity matters more than value.

Why Interviewers Ask This

Interviewers use this question to see whether you understand node identity, not just node values. They also want to know if you can find the right invariant, choose a low-space solution, and explain why the two-pointer switch works even when the lists have different lengths. It also checks whether you can write clean Java and talk through edge cases clearly.

Common interview mistakes

A common mistake is comparing node values instead of node references. Two nodes can hold the same number and still be different nodes. Another mistake is moving only one pointer or forgetting to switch heads after null. That breaks the equal-distance idea. People also stop too early when one list ends, or they add a set even though the diagram uses O(1) extra space.

Interview tip

When you explain this solution, say one sentence about equal total distance. That is the core invariant the interviewer wants to hear.

Interviewer may ask next
What if the lists may not intersect?

The same code already handles that case. Both pointers eventually become null at the same time, so the method returns null. The time stays O(m + n) and the extra space stays O(1).

What if I am allowed extra memory and want a simpler approach?

Store every node reference from list A in a HashSet, then scan list B and return the first node already in the set. That is still O(m + n) time, but it uses O(m) extra space. The tradeoff is more memory for simpler logic.

7. How would you implement thread safe queue?CodingHardNvidia

Question Details

Implement a thread-safe queue and explain synchronization, blocking behavior, and contention handling.

Short Interview Answer (30-60 seconds)

I build a bounded FIFO queue with one ReentrantLock and two Condition objects. Producers call enqueue, and they wait on notFull when the queue is full. Consumers call dequeue, and they wait on notEmpty when the queue is empty. Inside the lock, addLast and removeFirst keep FIFO order. After each state change, I signal the opposite side so one waiting thread can continue. Each successful operation is O(1) amortized, and the queue uses O(N) extra space.

Detailed Explanation

See the Code while reading this explanation.

The problem asks for a queue that many threads can use at the same time without breaking order. When the line is full, new items must wait. When the line is empty, removal must wait. The goal is to keep the first item in, first item out order and to avoid race conditions. A single shared lock with two wait signals fits this well because it lets one side sleep until the queue changes.

Useful Questions to Ask the Interviewer
  1. Should the queue have a fixed maximum size, or should it grow without a limit?
  2. When the queue is full or empty, should the method block, time out, or return a failure?
  3. Do you want fair scheduling so waiting threads get a more even chance?
How would you implement thread safe queue? diagram
How to Explain It in an Interview
1. Understand the input and required output

The input is a capacity and items that may be added or removed by many threads. The output is a safe queue that keeps FIFO order. If the queue is full, producers wait. If it is empty, consumers wait. The dequeue result is the removed item.

2. Choose the algorithm and data structure

I use one ReentrantLock to protect one LinkedList. The LinkedList is the queue storage. I use two Condition objects. notEmpty means there is at least one item. notFull means there is room for one more item. The key invariant is simple: only the lock holder changes the queue, and the queue always keeps FIFO order.

3. Initialize the state

I start with an empty queue, the given capacity, and a lock created with the fair option when needed. Then I create notEmpty and notFull from the same lock. This makes both waiting signals part of the same safe critical section.

4. Walk through the example

The diagram uses capacity 5 and this order: E(10), E(20), E(30), D(), E(40), D(), D(), E(50). Start: queue = [], head = 0, tail = 0, size = 0. After E(10): queue = [10]. After E(20): queue = [10, 20]. After E(30): queue = [10, 20, 30]. After D(): remove 10, so queue = [20, 30], returned = 10. After E(40): queue = [20, 30, 40]. After D(): remove 20, so queue = [30, 40], returned = 20. After D(): remove 30, so queue = [40], returned = 30. After E(50): queue = [40, 50]. The final state is the same in every section: queue = [40, 50], head = 40, tail = 50, size = 2.

5. Explain why the result is correct

The lock prevents two threads from changing the queue at the same time. notFull stops producers only when the queue is full. notEmpty stops consumers only when the queue is empty. Because items are added at the tail and removed from the head, FIFO order never changes.

6. Explain the Java implementation

The constructor checks that capacity is positive. enqueue first rejects null, then locks, waits while the queue is full, adds the item to the tail, signals notEmpty, and unlocks in finally. dequeue locks, waits while the queue is empty, removes the head item, signals notFull, returns the item, and unlocks in finally. The demo code runs the same example from the diagram and prints the final queue state.

7. Explain complexity and edge cases

Each successful enqueue and dequeue does constant work, so the time is O(1) amortized per operation. The queue stores at most N items, so auxiliary space is O(N). Important edge cases are a full queue, an empty queue, interrupted threads, null items, and optional fair locking.

Key Insight / Why This Solution Works

I use one lock to protect the whole queue. That makes every change safe. The queue stays FIFO because new items go to the tail and removed items come from the head. Two Conditions control blocking. notFull waits when the queue has no room. notEmpty waits when the queue has no items. After an enqueue, I wake one waiting consumer. After a dequeue, I wake one waiting producer. The invariant is that the queue state is always valid when a thread leaves the lock.

Code
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Main {

    // A bounded FIFO queue that is safe to use from many threads.
    public static final class ThreadSafeQueue<T> {

        private final int capacity;
        private final LinkedList<T> queue = new LinkedList<>();
        private final ReentrantLock lock;
        private final Condition notEmpty;
        private final Condition notFull;

        public ThreadSafeQueue(int capacity, boolean fair) {
            if (capacity <= 0) {
                throw new IllegalArgumentException("Capacity must be positive");
            }

            this.capacity = capacity;
            this.lock = new ReentrantLock(fair);
            this.notEmpty = lock.newCondition();
            this.notFull = lock.newCondition();
        }

        public void enqueue(T item) throws InterruptedException {
            if (item == null) {
                throw new NullPointerException("Null elements not allowed");
            }

            lock.lock();
            try {
                // Wait while the queue is full. This blocks producers instead of busy-waiting.
                while (queue.size() == capacity) {
                    notFull.await();
                }

                // Add at the tail to keep FIFO order.
                queue.addLast(item);

                // Wake one waiting consumer because the queue is now non-empty.
                notEmpty.signal();
            } finally {
                // Always release the lock, even if waiting or insertion throws.
                lock.unlock();
            }
        }

        public T dequeue() throws InterruptedException {
            lock.lock();
            try {
                // Wait while the queue is empty. This blocks consumers instead of busy-waiting.
                while (queue.isEmpty()) {
                    notEmpty.await();
                }

                // Remove from the head to keep FIFO order.
                T item = queue.removeFirst();

                // Wake one waiting producer because the queue now has room.
                notFull.signal();
                return item;
            } finally {
                // Always release the lock.
                lock.unlock();
            }
        }

        public List<T> snapshot() {
            lock.lock();
            try {
                // Return a safe copy so the demo can print the final queue state.
                return new ArrayList<>(queue);
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // Use the exact example from the diagram.
        ThreadSafeQueue<Integer> queue = new ThreadSafeQueue<>(5, false);
        List<Integer> returned = new ArrayList<>();

        queue.enqueue(10);
        queue.enqueue(20);
        queue.enqueue(30);
        returned.add(queue.dequeue());
        queue.enqueue(40);
        returned.add(queue.dequeue());
        returned.add(queue.dequeue());
        queue.enqueue(50);

        System.out.println("Returned by dequeues: " + returned);
        System.out.println("Final queue state: " + queue.snapshot());
    }
}
Time & Space Complexity

Each enqueue and dequeue does a small fixed amount of work once the thread gets the lock. So the time is O(1) amortized per operation. The queue can hold at most N items, so the extra memory is O(N). If a thread must wait, that waiting time depends on contention, but the core queue work is still constant.

Where it is used

This pattern is useful in producer-consumer systems, job queues, request buffers, logging pipelines, and any service that must cap memory while many threads share the same work queue.

Why Interviewers Ask This

The interviewer is checking if I can protect shared state, keep FIFO order, and block threads without busy waiting. They also want to see if I know how locks, Conditions, interruption, and contention work together. This question shows whether I can write correct Java and explain why the queue stays valid when many threads use it at once.

Common interview mistakes

A common mistake is using if instead of while around await(). Spurious wakeups can happen, so the code must check the condition again. Another mistake is signaling the wrong condition after a change. After enqueue, the queue is no longer empty, so notEmpty should be signaled. After dequeue, the queue has room, so notFull should be signaled. Some candidates forget to lock every access to the shared queue. That breaks safety. Another mistake is removing from the tail or adding to the head, which breaks FIFO order. A final mistake is ignoring InterruptedException or allowing null items when the policy forbids them.

Interview tip

Say the invariant out loud: one lock protects the whole queue, and each state change wakes the opposite side with the right Condition.

Interviewer may ask next
What changes if enqueue and dequeue should time out instead of waiting forever?

I would replace await() with timed waits such as awaitNanos() or await(time, unit). If the wait times out, the method can return a failure value instead of blocking forever. The lock, FIFO order, and signal logic stay the same. The main tradeoff is that callers get control back sooner, but they must handle timeout results.

What changes if the queue should be unbounded?

I would remove the capacity check and the notFull condition. enqueue would only lock, add the item to the tail, signal notEmpty, and unlock. That removes producer blocking and makes the queue simpler. The queue still stays FIFO, but the space can grow with the number of stored items.

8. Write the base-11 representation of the decimal number 175.CodingEasyNvidia

Question Details

Convert decimal 175 to base 11 and explain the digit-by-digit conversion.

Short Interview Answer (30-60 seconds)

I would convert the number by repeated division by 11. Each remainder becomes the next base-11 digit, and I store the digits from right to left, then reverse them at the end. For 175, the remainders are 10, 4, and 1, so the final answer is 14A. This works because each division removes one base-11 place value. The time is O(log_11 n), and the extra space is also O(log_11 n).

Detailed Explanation

See the Code while reading this explanation.

The question asks us to change 175 from ordinary counting into base 11. That means we keep dividing by 11, save each leftover number, and then read those leftovers in reverse order. For 175, the leftovers are 10, 4, and 1. In base 11, 10 is written as A, so the final answer is 14A. This method fits well because every step is simple, exact, and easy to check by hand. It also matches the Java code one step at a time.

Useful Questions to Ask the Interviewer
  1. Do you want only the base-11 string, or do you also want the conversion steps?
  2. Should I support only base 11, or any base from 2 to 36?
Write the base-11 representation of the decimal number 175. diagram
How to Explain It in an Interview
1. Understand the input and required output

The input is the decimal number 175. The output is its base-11 form. We only need the final converted string, not indices or extra data.

2. Choose repeated division by 11

The cleanest method is repeated division. Each remainder is one base-11 digit. This is better than trying to build the number by guessing digits.

3. Initialize the state

Start with n = 175 and an empty list of digits. The list stores digits from least significant to most significant. That means the first digit we find goes on the right side of the final answer.

4. Walk through the example

First, divide 175 by 11. The quotient is 15 and the remainder is 10, which is A in base 11. Then divide 15 by 11. The quotient is 1 and the remainder is 4. Then divide 1 by 11. The quotient is 0 and the remainder is 1. The saved digits are 10, 4, and 1. Reading them backward gives 14A.

5. Explain why the result is correct

At every step, n = 11 × quotient + remainder. That means the remainder is always the next correct digit in base 11. When n becomes 0, all place values are finished, so reversing the collected digits gives the exact answer.

6. Explain the Java implementation

The Java code keeps dividing the number by 11, converts remainder 10 to 'A', appends each digit, and reverses the result at the end. The main method runs the example 175, so the printed answer is 14A.

7. Explain complexity and edge cases

The number of divisions is small and depends on how many base-11 digits the number has. The extra memory also grows with the number of digits. For 175, there are three digits. The main edge cases are n = 0 and values smaller than 11.

Key Insight / Why This Solution Works

The key insight is that repeated division by 11 gives the base-11 digits from right to left. The remainder is the next digit, and the quotient becomes the next number to divide. The invariant is simple: after each step, the collected remainders are exactly the digits already found, in reverse order. When the quotient becomes 0, reversing the collected digits gives the final base-11 representation.

Code
public class Base11Converter {

    public static String toBase11(int n) {
        // Base case: zero is zero in every base.
        if (n == 0) {
            return "0";
        }

        // Store digits from least significant to most significant.
        StringBuilder sb = new StringBuilder();

        while (n > 0) {
            // The remainder is the next base-11 digit.
            int rem = n % 11;

            // In base 11, digit 10 is written as A.
            char digit = rem < 10 ? (char) ('0' + rem) : 'A';
            sb.append(digit);

            // Move to the next higher place value.
            n /= 11;
        }

        // Digits were collected in reverse order, so reverse them now.
        return sb.reverse().toString();
    }

    public static void main(String[] args) {
        // Run the exact example from the diagram.
        System.out.println(toBase11(175)); // Output: 14A
    }
}
Time & Space Complexity

This process uses one division loop for each base-11 digit. So the time is O(log_11 n). The extra memory also grows with the number of digits, because we store the remainders before reversing them. So the auxiliary space is O(log_11 n). In simple words, we process the number a small number of times, not once per decimal digit.

Where it is used

This pattern is useful in number base conversion, encoding systems, and interview problems about place value. It also helps when you need to show how a number is built in a different base, such as binary, octal, hexadecimal, or any custom base.

Why Interviewers Ask This

Interviewers want to see if you understand place value, repeated division, and digit mapping. They also check whether you can explain why the digits must be reversed and why 10 becomes A. This question is small, but it shows clear thinking, correct Java code, and careful handling of edge cases.

Common interview mistakes

A common mistake is forgetting that remainder 10 must be written as A. Another mistake is reading the digits in the same order they were found, instead of reversing them. Some candidates also stop too early and forget to keep dividing until n becomes 0. A final mistake is mixing up the quotient and remainder, which breaks the place values.

Interview tip

When you explain it, say one short rule: the remainder is the next digit, and the last remainder becomes the leftmost digit after reversing.

Interviewer may ask next
How would you convert a decimal number to any base from 2 to 36?

Use the same repeated division idea. Keep dividing by the target base, map digits 10 to 35 to letters A to Z, collect remainders, and reverse them at the end. The time and space both stay proportional to the number of digits in the new base.

How would you handle a negative decimal number?

Store the sign first, convert the absolute value with the same loop, and then add the minus sign back at the front. The conversion logic does not change. The time and space complexity stay the same.

9. How would you compute the square root without using obvious approaches?CodingMediumNvidia

Question Details

Compute a square root using a non-obvious algorithm and explain the tradeoffs.

Short Interview Answer (30-60 seconds)

I would use the Babylonian, or Newton-Raphson, method. I start with a positive guess, then I keep replacing it with the average of the guess and x divided by the guess. That moves the value quickly toward √x. I stop when the change is smaller than the tolerance. If x is negative, I throw an error. This runs in O(log log(x / ε)) time and uses O(1) extra space.

Detailed Explanation

See the Code while reading this explanation.

The problem asks for a square root of one non-negative number, but it does not want the direct built-in answer. I start with one positive guess and improve it again and again using the number itself. Each new guess is the average of the current guess and x divided by that guess. That makes the guess move quickly toward the square root. I stop when the change is tiny. This works well because the guess gets much better very fast, and the code stays small and easy to explain.

Useful Questions to Ask the Interviewer
  1. Should I treat negative input as an error, and do you want a tolerance like ε for the stop rule?
  2. Do you want the raw approximation, or do you want it printed to a fixed number of decimal places?
How would you compute the square root without using obvious approaches? diagram
How to Explain It in an Interview
1. Understand the input and output

The input is one number x. The output is an approximation of √x. The diagram uses x >= 0, a double value, and it stops when the change between two guesses is small enough.

2. Choose the algorithm and state

I use the Babylonian, or Newton-Raphson, update: next = 0.5 * (y + x / y). The state is just the current guess y, the next guess next, and the tolerance eps. The central idea is that each new guess is better than the last one.

3. Initialize the state

If x < 0, the code throws an error. If x is 0, the answer is 0. The code also returns infinity unchanged. For a positive number, the initial guess is x when x >= 1.0, otherwise it is 1.0. This avoids a bad starting point and keeps y positive.

4. Walk through the example

The diagram uses x = 2 and eps = 10^-12. Start with y0 = 1.0. Then next = 0.5 * (1.0 + 2 / 1.0) = 1.5. Then next = 0.5 * (1.5 + 2 / 1.5) = 1.4166666666666667. Then next = 1.4142156862745099. Then next = 1.4142135623746899. Then next = 1.4142135623730951. At that point the change is tiny, so the loop stops and returns the approximation.

5. Explain why the result is correct

The update comes from Newton's method for f(y) = y^2 - x. The guess stays positive, and each step moves it closer to √x. The diagram shows that the number of correct digits grows very fast once the guess is close. That is why the method converges quickly.

6. Explain the Java implementation

The method sqrtNewton(double x, double eps) does the same steps as the diagram. It first handles NaN, x < 0, x == 0, and infinity. Then it sets the initial guess. Inside the loop, it computes next with the Babylonian formula. If Math.abs(next - y) <= eps * next, it returns next. Otherwise it copies next into y and repeats. The main method runs the diagram's example with x = 2.0 and eps = 1e-12.

7. Explain complexity and edge cases

Each loop round does constant work, so the total time is O(log log(x / ε)) and the extra space is O(1). The important edge cases are NaN, x < 0, x == 0, very large x, and very small positive x. The chosen initial guess and relative stop rule help with those cases.

Key Insight / Why This Solution Works

The key idea is to use Newton's method on f(y) = y^2 - x. The update y_next = 0.5 * (y + x / y) moves the guess toward √x very fast. The invariant is simple: y is always the current positive guess, and next is the improved guess. We stop when the relative change is tiny, so the approximation is stable.

Code
public class Main {

    public static double sqrtNewton(double x, double eps) {
        // NaN cannot be improved into a real square root.
        if (Double.isNaN(x)) {
            return Double.NaN;
        }

        // Negative numbers do not have a real square root in this problem.
        if (x < 0.0) {
            throw new IllegalArgumentException("x must be >= 0");
        }

        // Zero is its own square root. Infinity stays infinity.
        if (x == 0.0 || Double.isInfinite(x)) {
            return x;
        }

        // Start with a positive guess. A larger guess helps for x >= 1.
        double y = x >= 1.0 ? x : 1.0;

        while (true) {
            // Babylonian / Newton-Raphson update.
            double next = 0.5 * (y + x / y);

            // Stop when the new guess is very close to the old one.
            if (Math.abs(next - y) <= eps * next) {
                return next;
            }

            // Keep improving the guess.
            y = next;
        }
    }

    public static void main(String[] args) {
        // Run the same example shown in the diagram.
        double x = 2.0;
        double eps = 1e-12;

        double result = sqrtNewton(x, eps);

        // Print enough digits to show the final approximation clearly.
        System.out.printf("sqrt(%.1f) = %.16f%n", x, result);
    }
}
Time & Space Complexity

Each round does a constant amount of math: one division, one addition, one multiply, and one comparison. Because Newton's method converges very fast, the number of rounds is O(log log(x / ε)). So the total time is O(log log(x / ε)) and the extra space is O(1).

Where it is used

This pattern is useful in scientific code, simulations, graphics, and any numeric program that needs a fast square-root approximation without calling a built-in square-root function.

Why Interviewers Ask This

The interviewer is checking if I can recognize Newton's method, keep the update order correct, handle special cases, and explain why the approximation gets better fast. They also want to see simple Java code and a correct complexity explanation.

Common interview mistakes

A common mistake is to use the built-in square root instead of the iterative method. Another mistake is to start with a bad initial guess, such as 0, which can break the division step. Candidates also forget the special cases for NaN, x < 0, and x == 0. A final mistake is to use the wrong stop test, or to keep iterating after the answer is already stable.

Interview tip

Say the update formula out loud, then point out the stop rule. That shows you know both the math and the code.

Interviewer may ask next
How would you change this if you needed a fixed number of decimal places?

I would keep the same iteration, but I would choose eps from the number of decimal places I want. Then I would stop when the relative change is small enough for that precision. The time and space complexity stay the same.

Why does the code use a relative stop rule instead of only an absolute stop rule?

A relative stop rule scales with the size of x. That is better when x is very large or very small. It keeps the precision target consistent, while the same absolute difference could be too loose or too strict.

10. How would you design a finite state machine to detect a specific combination of bits in a continuous stream?System DesignMediumNvidia

Question Details

Design an FSM that watches a bit stream and detects the target pattern, including states, transitions, and outputs.

Short Interview Answer (30-60 seconds)

At a high level, the goal is to recognize the bit pattern 1011 while bits keep arriving. The main challenge is remembering enough history to detect the pattern, including overlapping matches, without storing the whole stream. I would explain the input flow, the state transitions, and the match output. Inside one JVM, a current state register and transition function process each bit. This Mealy design emits detect=1 when the final 1 completes 1011. The trade-off is fewer states, but the output belongs to a transition.

Detailed Explanation

The goal is to watch a continuous stream of zeros and ones and report whenever the sequence 1011 appears. We do not need to remember every earlier bit. We only need to remember how much of 1011 has matched so far. The difficult part is handling partial matches correctly when the next bit does not continue the pattern. We also want overlapping matches to work. The diagram solves this with four progress states, a current state register, a transition function, and a match event produced when the final bit completes 1011.

Useful Questions to Ask the Interviewer
  1. Should overlapping matches be detected?
  2. Will we process one stream or many independent streams?
  3. Should the match signal be produced immediately on the bit that completes the pattern?
How would you design a finite state machine to detect a specific combination of bits in a continuous stream? diagram
How to Explain It in an Interview
1. Explain what each state means

I would start by saying that each state represents progress through 1011. S0 means no useful prefix has matched. S1 means we have matched 1. S2 means we have matched 10. S3 means we have matched 101.

This is enough information to continue processing. We do not need to save the complete bit history.

2. Explain the normal state transitions

For each incoming bit, the Transition function reads the current state and the new bit. It returns the next state and the output value.

From S0, input 0 stays in S0 with output 0. Input 1 moves to S1 with output 0. From S1, input 1 stays in S1 because that 1 can begin another possible 1011. Input 0 moves to S2.

From S2, input 1 moves to S3. Input 0 returns to S0. From S3, input 0 returns to S2 because the ending 10 is still the start of a possible match.

3. Explain how a match is produced

The important case happens in S3, where 101 has already matched. If the next input bit is 1, the complete pattern 1011 has been found. The Mealy machine therefore emits output 1 on that transition.

The diagram labels this detecting transition as 1/1. The first value is the input bit. The second value is the output. For overlap handling, the intended next state is S1 because the final 1 can also be the first bit of the next 1011 match.

4. Explain the Java processing flow

Inside the Java 21/25 Pattern Detector running in one JVM, the Current state register stores the active state. The Transition function performs the lookup for each new bit.

The diagram shows the core idea as var t = next[state][bit];, followed by state = t.nextState; and detect = t.output;. When detect becomes 1, the detector emits the Match event shown on the right.

5. Explain multiple streams and the main trade-off

For independent streams, each stream needs its own FSM instance or current state register. That keeps one stream from changing another stream's progress. Threads inside one JVM share heap memory, so shared mutable state must be protected if multiple threads can update it.

Processing n bits takes O(n) time because every bit is handled once. Memory is O(1) per stream because only the current state is stored. The benefit of the Mealy design is fewer states. The downside is that the output depends on a transition instead of a separate match state.

Engineering Considerations / Design Trade-offs

The benefit is that the detector is small and fast. Each incoming bit causes one transition lookup and one state update. Processing n bits therefore takes O(n) time. The machine stores only the current state, so memory is O(1) for each stream. The Mealy design also uses fewer states because it can report the match on the transition that completes 1011. The downside is that the output depends on both the state and the incoming bit. That can be slightly harder to reason about than a Moore machine, where output belongs to a state. Multiple streams also need separate state registers.

Why Interviewers Ask This

Interviewers use this problem to see whether you can turn a streaming requirement into a small set of clear states. They want to see correct transition logic, overlap handling, and output behavior. They also check whether you understand constant memory, linear processing time, state isolation for independent streams, and the practical difference between a Mealy machine and a Moore machine.

Interviewer may ask next
How would the design change if we needed to process many independent bit streams at the same time?

I would keep the same Transition function and FSM rules, but each independent stream would get its own Current state register. The transition table itself can be shared because it does not change. Only the current progress through 1011 is different for each stream.

For example, stream A might be in S2 because it has matched 10. Stream B might be in S1 because it has matched only 1. When another bit arrives, the detector looks up the state belonging to that stream and applies the same transition rules.

This keeps the result correct because bits from one stream never change another stream's progress. Inside one JVM, threads share heap memory. If two threads can update the same stream state at the same time, those updates must be synchronized or processed in order.

The downside is that total memory grows with the number of active streams, even though each individual stream still uses only O(1) state.

What would change if the interviewer required a Moore machine instead of the Mealy machine shown in the diagram?

I would keep the same pattern-matching idea, but I would add a separate state for the completed 1011 match. In a Moore machine, the output belongs to the current state instead of the transition that enters it.

The current diagram produces detect=1 on the transition from the state representing 101 when the next bit is 1. In a Moore version, that input would move into a new match state whose output is 1. Its next transitions would still preserve the longest useful suffix so overlapping matches continue to work.

The Current state register and Transition function would remain. Each stream would still keep its own state. Processing would remain O(n) time with O(1) memory per stream.

The benefit is that output depends only on the state, which can be easier to explain. The downside is that the Moore version needs an extra state compared with the four-state Mealy design.

More questions load as you scroll

Disclaimer: This interview guide is for educational and informational purposes only. It is designed to help readers prepare, but it does not guarantee any interview result, hiring decision, offer, or outcome. Interview questions, hiring criteria, and preferred answers can vary by employer, interviewer, industry, location, and time. The examples and explanations reflect the authors' research and judgment, are provided without warranties of any kind, and should not be treated as the only correct approach. Diagrams are simplified illustrations intended to highlight the main components and their interactions; actual systems and implementations may be more complex. Alternative approaches may be equally valid or better suited to a particular question, context, or interviewer. To the fullest extent permitted by applicable law, the author, contributors, and publisher are not liable for decisions made, actions taken, or losses incurred based on this guide.

Company Notice: This guide is an independent educational resource and is not affiliated with, endorsed by, sponsored by, or approved by the company named in this guide. Company names are used only to identify interview experiences commonly reported by candidates. Interview practices can change without notice, and inclusion of company-specific content does not mean these questions are official, complete, or guaranteed to be asked. To the fullest extent permitted by law, the author, contributors, and publisher are not responsible for outcomes related to use of this material.