Returning array

In Java, the shorthand array initializer syntax ({...}) is restricted by the language grammar to variable declarations only.

Why Assignment Works

When you write:

int[] arr = {-1, -1};

This is a variable declaration with initialization. The Java compiler uses the type declared on the left side (int[]) to understand and build the array directly from the shorthand curly braces.

Why Return (and Later Assignment) Fails

If you try to write:

return {-1, -1}; // Compile error

or

int[] arr;
arr = {-1, -1}; // Compile error

Expression

Java’s grammar does not treat {...} as a standalone expression. It only recognizes it during initial declaration. Because a return statement expects a full expression, the compiler throws a syntax error.

The Role of new int[]

Using new int[]{-1, -1} explicitly creates an anonymous array expression. Because it is a valid expression, Java can evaluate it anywhere an expression is expected—including inside a return statement or a late assignment.

Returning after assigning

return int[] arr = {-1, -1}; causes a compile-time syntax error in Java.

Why It Fails

  • Expressions vs. Statements: A return statement expects an expression (a piece of code that evaluates to a value).
  • Declarations: int[] arr = {-1, -1} is a statement (specifically, a variable declaration). Java’s grammar does not allow you to declare a new named variable inline inside a return expression.

How to Write It If You Need a Variable

If you want to assign the array to a variable first and then return it, you must split it into two separate lines:

int[] arr = {-1, -1};
return arr;

Summary

  • Use either the direct anonymous array expression (return new int[]{-1, -1};) or separate the declaration and return into two lines to successfully compile your fallback block.

char

char temp= ''; // error: empty character literal

In Java, there is no such thing as an “empty” character literal like ''. Single quotes must contain something.

In Java, the distinction between an uninitialized variable and an “empty” value comes down to syntax rules, memory allocation, and compilation checks.

1. The Differences

  • Uninitialized Variable (char temp;)

    • What it means: You are telling Java, “I am going to use a variable named temp of type char, but I haven’t assigned a value to it yet.”
    • Java’s Behavior: Java’s compiler is very strict about local variables. If you try to read or use an uninitialized local variable before giving it a value (e.g., System.out.println(temp);), the compiler will throw a variable temp might not have been initialized error.
    • When it works: It is completely legal to declare it without initialization as long as you assign a value to it later before you ever try to read it (which is exactly what your temp = s[i]; line does inside the loop).
  • “Empty” Character (char temp = ' '; or \u0000)

    • What it means: This is an actual value assigned to the variable. In Java, there is no such thing as an “empty” character literal like ''. Single quotes must contain something.
    • The Space Alternative (' '): If you write char temp = ' ';, you are initializing it with a blank space (ASCII value 32). It is a valid character, but it takes up memory space as a literal blank character.
    • The Null/Zero Alternative ('\u0000'): In Java, the default value for primitive char fields in a class is the null character, represented as '\u0000' (integer value 0).

2. When to Use Which?

  • Use Uninitialized (char temp;) when:
    • You need a temporary holding variable (like in a swap algorithm) where you are **guaranteed** to assign a value to it before reading it.
    • Why: It avoids unnecessary overhead of assigning a dummy value (' ' or '\u0000') that you are just going to overwrite immediately anyway.
  • Use Initialized (char temp = 'a'; or char temp = '\u0000';) when:
    • Your code logic requires the variable to have a fallback or default state right from the start, especially if it might be read before a new value is assigned (e.g., inside a conditional if statement that might get skipped).
    • You are declaring an instance variable (field) in a class, where Java automatically gives primitives like char a default value of '\u0000' if you don’t provide one.

If, else if and else

If, else if, else

  1. is else if() only invoked if first if() fails
  2. Will second else if also run if first fails and first else if is true
  3. Are all else if evaluated ?
  1. Is else if only invoked if the first if fails?
    • Yes. An else if is completely skipped if the preceding if (or else if) condition evaluates to true.
  2. Will the second else if run if the first if fails and the first else if is true?
    • No. Java evaluates an if-else if chain from top to bottom and stops at the very first true condition. Once a branch executes, the rest of the chain is bypassed entirely for that iteration.
  3. Are all else if branches evaluated?
    • No. As soon as one condition matches, Java short-circuits and ignores all subsequent else if and else blocks in that chain.

Note

Java evaluates if-else if chains sequentially from top to bottom and short-circuits at the very first true condition.

Control Flow, Loops

Use a for Loop When:

  • You are iterating through a fixed-length structure (like arrays or strings) from index 0 to n.
  • The step increment (i++) is uniform, predictable, and tied directly to index traversal.
  • Example: Linear scans, array rankings, and static index lookups.
// Fixed linear scan
for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

Use a while Loop When:

  • The number of iterations is unknown beforehand and depends on dynamic runtime conditions.
  • Pointers move at variable rates or conditionally based on logic inside the loop (e.g., left pointer moves sometimes, right pointer moves others).
  • Example: Two-pointer convergence, binary search, sliding window expansion/contraction, and linked list traversals.
// Dynamic pointer convergence
int left = 0, right = arr.length - 1;
while (left < right) {
    if (arr[left] == target) {
        break;
    }
    left++; // Conditional movement
}

Critical Pitfalls to Watch Out For in Loops

When writing algorithms, always double-check these four high-risk areas:

Off-by-One Errors (< vs <=)

  • Using <= with arr.length causes an ArrayIndexOutOfBoundsException. Always use < arr.length for standard zero-indexed array scans.

Infinite Loops

  • In a while loop, ensure every code path updates your tracking variables or pointers. If a condition gets stuck without moving left, right, or i, the loop will run forever.

Modifying Collections During Iteration

  • Never add or remove elements from an active collection (like an ArrayList) using a standard index loop without adjusting your index, or using an explicit iterator, to avoid skipping elements or throwing runtime exceptions.

State Leakage

  • If a loop or helper function uses running totals, flags, or tracking variables, ensure they are cleanly reset or initialized inside the outer method scope before execution begins.

Loop Control: continue vs. break

continue

  • In a loop (for, while), the continue keyword instantly terminates the current iteration right where it is.
  • It skips all remaining lines of code inside the loop body for that specific element and
  • jumps straight to the next loop step (incrementing i).

The Upfront Bouncer Pattern

  • In some algorithm, continue acts as an **upfront bouncer/filter
  • Use continue at the very top of your loop body as an explicit duplicate filter or guard clause.
  • Weed out invalid or duplicate values early so your core logic only processes clean data.
int[] nums = {2, 2, 3, 1};
Integer max1 = null;
 
for (int i = 0; i < nums.length; i++) {
    // Upfront filter: skip processing if duplicate exists
    if (max1 != null && nums[i] == max1) {
        continue; 
    }
    
    if (max1 == null || nums[i] > max1) {
        max1 = nums[i];
    }
}

break

  • Instantly terminates the entire loop altogether, exiting execution immediately.
  • Use it when you have
    • found your target or
    • met a termination condition and
    • no further scanning is required.

Code Example: Using break for Early Exit

int[] nums = {10, 20, 30, 40, 50};
int target = 30;
int targetIndex = -1;
for (int i = 0; i < nums.length; i++) {
    if (nums[i] == target) {
        targetIndex = i;
        break; // Stop scanning once found, saving CPU cycles
    }
}

The Shadowing Rule

Broader conditions must never precede stricter ones. Always place the most restrictive, highest-threshold condition at the top of the chain to prevent wide nets from blocking valid specific checks.

Incorrect - condition bypassed

int x = 15;
 
if (x > 20) {
    // Skipped (15 > 20 is false)
} else if (x > 10) {
    // This is TRUE! Executes this block.
} else if (x > 5) {
    // Bypassed entirely, even though 15 > 5 is also true.
} else {
    // Skipped because an earlier condition matched.
}

Correct

int x = 25;
// Correct: Strictest condition is checked first
if (x > 20) {
    System.out.println("Greater than 20"); // Executes for 25
} else if (x > 15) {
    System.out.println("Between 16 and 20"); 
} else {
    System.out.println("15 or below");
} else { // Skipped because an earlier condition matched. }

Tree

Is a Tree interface always sorted in insertion order?

Question after reading this solution of LC 414

  • No. LinkedHashMap or LinkedList are sorted by insertion order, Linked being the keyword here.
  • Tree-based collections in Java (like TreeSet and TreeMap, which implement SortedSet and NavigableSet) are always by natural sorting order or a custom Comparator (backed by a Red-Black Tree). They completely discard the sequence in which insert items.
  • If you need a structure that preserves insertion order, use LinkedHashSet or LinkedHashMap.