Structured interview prep for software testers. Real questions from TCS, Infosys, Wipro, Amazon and more. 100% free.
Click any topic for structured interview prep — fresher to senior level.
A clear step-by-step path — from zero to job-ready Automation Engineer.
static keyword, shared across all objects
| Type | Size | Range | Default |
|---|---|---|---|
| byte | 1 byte | -128 to 127 | 0 |
| short | 2 bytes | -32768 to 32767 | 0 |
| int | 4 bytes | -2^31 to 2^31-1 | 0 |
| long | 8 bytes | -2^63 to 2^63-1 | 0L |
| float | 4 bytes | ~3.4e38 | 0.0f |
| double | 8 bytes | ~1.8e308 | 0.0d |
| char | 2 bytes | 0 to 65535 (Unicode) | '\u0000' |
| boolean | 1 bit | true / false | false |
int → long → float → doubleint a = 10; double d = a; // OK automaticallydouble d = 9.99; int a = (int) d; // a = 9, decimal lost
int i=5; int a=i++; // a=5, i=6int i=5; int a=++i; // a=6, i=6
condition ? valueIfTrue : valueIfFalseint a=10, b=20;
int max = (a > b) ? a : b; // max = 20
if(x > 0){ System.out.println("Positive"); }
else if(x < 0){ System.out.println("Negative"); }
else{ System.out.println("Zero"); }
switch(day){
case 1: System.out.println("Mon"); break;
case 2: System.out.println("Tue"); break;
default: System.out.println("Other");
}for(int i=0; i<5; i++){ ... }while(i<5){ ... i++; }do{ ... i++; }while(i<5);for(int x : arr){ ... }
int i=10;
while(i<5){ System.out.println(i); } // never runs
do{ System.out.println(i); }while(i<5); // runs once: prints 10
for(int i=0;i<10;i++){
if(i==5) break; // stops at 5
if(i%2==0) continue; // skips even
System.out.println(i); // prints 1,3
}
// break example
for(int i=0;i<5;i++){
if(i==3) break;
System.out.print(i); // 0 1 2
}
// continue example
for(int i=0;i<5;i++){
if(i==3) continue;
System.out.print(i); // 0 1 2 4
}
int[] arr = {10, 20, 30};
int[] arr;
int[] arr = new int[5];
int[] arr = {1, 2, 3, 4};
| Data Type | Default Value |
|---|---|
| int | 0 |
| float | 0.0 |
| double | 0.0 |
| char | '' |
| boolean | false |
| String / Object | null |
int[] arr = new int[-5]; // Runtime error
int[] arr = {1, 2, 3}; // ✔ Allowed
int[] arr;
arr = {1,2,3}; // ❌ Compile-time error
Object[] arr = new Integer[5];
arr[0] = "Hello"; // Runtime Exception
| ArrayStoreException | ArrayIndexOutOfBoundsException | |
|---|---|---|
| Cause | Wrong data type stored | Invalid index used |
| Type | Runtime exception | Runtime exception |
| Error | Type mismatch | Index range error |
List[] arr; // Not allowed
sum(new int[]{10, 20, 30});
static void sum(int[] arr) {
int total = 0;
for (int i : arr) total += i;
System.out.println(total); // 60
}
| Array | ArrayList | |
|---|---|---|
| Size | Fixed | Dynamic |
| Speed | Faster | Slightly slower |
| Types | Primitives + Objects | Objects only |
| Methods | No built-in methods | Rich API (add, remove...) |
| Array | LinkedList | |
|---|---|---|
| Memory | Continuous | Non-continuous |
| Access | Fast O(1) | Slow O(n) |
| Size | Fixed | Dynamic |
| Memory usage | Less | More (stores pointers too) |
int[][] arr = {
{1, 2},
{3, 4, 5},
{6}
};
int[] copy = arr.clone();
// Assume first element is largest, compare with rest
int[] arr = {3, 1, 9, 2, 7};
int max = arr[0]; // step 1: assume arr[0] is max
for(int i = 1; i < arr.length; i++){ // step 2: loop from index 1
if(arr[i] > max){ // step 3: if current > max
max = arr[i]; // step 4: update max
}
}
System.out.println("Largest: " + max); // Output: 9
import java.util.Arrays;
int[] arr = {3, 1, 9, 2, 7};
Arrays.sort(arr); // sort ascending
System.out.println("Largest: " + arr[arr.length - 1]); // last = largest
import java.util.Collections;
import java.util.Arrays;
Integer[] arr = {3, 1, 9, 2, 7};
System.out.println("Largest: " + Collections.max(Arrays.asList(arr)));
int[] arr = {1, 2, 3, 4, 5};
int left = 0, right = arr.length - 1;
while(left < right){ // step 1: loop until pointers meet
int temp = arr[left]; // step 2: save left value
arr[left] = arr[right]; // step 3: put right into left
arr[right] = temp; // step 4: put saved into right
left++; // step 5: move pointers inward
right--;
}
// Output: {5, 4, 3, 2, 1}
import java.util.*;
Integer[] arr = {1, 2, 3, 4, 5};
List list = Arrays.asList(arr);
Collections.reverse(list);
System.out.println(Arrays.toString(arr)); // [5, 4, 3, 2, 1]
int[] arr = {1, 2, 3, 4, 5};
int[] rev = new int[arr.length];
for(int i = 0; i < arr.length; i++){
rev[i] = arr[arr.length - 1 - i]; // fill from end
}
System.out.println(Arrays.toString(rev)); // [5, 4, 3, 2, 1]
int[] arr = {1, 2, 3, 2, 4, 3, 5};
for(int i = 0; i < arr.length; i++){
for(int j = i + 1; j < arr.length; j++){
if(arr[i] == arr[j]){ // compare each pair
System.out.println("Duplicate: " + arr[i]);
}
}
}
import java.util.HashSet;
int[] arr = {1, 2, 3, 2, 4, 3, 5};
HashSet seen = new HashSet<>();
for(int num : arr){
if(!seen.add(num)){ // add() returns false if already exists
System.out.println("Duplicate: " + num);
}
}
int[] arr = {1, 2, 3, 2, 4, 3, 5};
Arrays.sort(arr); // {1, 2, 2, 3, 3, 4, 5}
for(int i = 0; i < arr.length - 1; i++){
if(arr[i] == arr[i+1]){ // adjacent same = duplicate
System.out.println("Duplicate: " + arr[i]);
}
}
int[] arr = {10, 5, 8, 20, 15};
int first = Integer.MIN_VALUE; // track largest
int second = Integer.MIN_VALUE; // track second largest
for(int num : arr){
if(num > first){ // new largest found
second = first; // old largest becomes second
first = num;
} else if(num > second && num != first){ // new second
second = num;
}
}
System.out.println("Second largest: " + second); // 15
int[] arr = {10, 5, 8, 20, 15};
Arrays.sort(arr); // {5, 8, 10, 15, 20}
System.out.println("Second largest: " + arr[arr.length - 2]); // 15
int[] arr = {1, 2, 3, 4, 5};
int k = 2; // rotate right by 2
int n = arr.length;
int[] result = new int[n];
for(int i = 0; i < n; i++){
result[(i + k) % n] = arr[i]; // place at shifted position
}
System.out.println(Arrays.toString(result)); // [4, 5, 1, 2, 3]
// Rotate right by k: reverse all, reverse first k, reverse rest
void rotate(int[] arr, int k){
k = k % arr.length;
reverse(arr, 0, arr.length-1); // reverse whole array
reverse(arr, 0, k-1); // reverse first k
reverse(arr, k, arr.length-1); // reverse rest
}
void reverse(int[] arr, int l, int r){
while(l < r){ int t=arr[l]; arr[l]=arr[r]; arr[r]=t; l++; r--; }
}
String name = "Akshay";
String s = "Welcome";
s.concat(" Java");
System.out.println(s);
String s1 = "Java";
String s2 = new String("Java");
String s = "Java";
System.out.println(s.length());
String s = "Welcome to Java";
s.contains("Java");
String s = " Java ";
System.out.println(s.trim());
String s = "Java";
System.out.println(s.replace("a","o"));
String s1 = "Core";
String s2 = "Java";
System.out.println(s1.concat(s2));
String a = "Java";
String b = "Java";
a.equals(b);
"JAVA".equalsIgnoreCase("java");
String s = "Automation";
System.out.println(s.substring(0,4));
String s = "Java";
s.toUpperCase(); // JAVA
s.toLowerCase(); // java
String s = "Java,Python,SQL";
String[] arr = s.split(",");
| Operator | Compares |
|---|---|
| == | Memory address |
| equals() | Value |
String s1 = new String("Java");
String s2 = new String("Java");
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
| Type | Mutable | Thread Safe |
|---|---|---|
| String | ❌ | ✔ |
| StringBuffer | ✔ | ✔ |
| StringBuilder | ✔ | ❌ |
StringBuffer sb = new StringBuffer("Welcome");
sb.append(" Java");
System.out.println(sb);
StringBuilder sb = new StringBuilder("Welcome");
sb.append(" Java");
String s1 = "Java";
String s2 = "Java";
StringBuilder sb = new StringBuilder("Akshay");
sb.replace(0, 6, "Rahul");
System.out.println(sb);
String s = "racecar";
int left = 0, right = s.length() - 1;
boolean isPalin = true;
while(left < right){
if(s.charAt(left) != s.charAt(right)){ // chars don't match
isPalin = false;
break;
}
left++; // move inward
right--;
}
System.out.println(isPalin ? "Palindrome" : "Not Palindrome");
String s = "racecar";
String rev = new StringBuilder(s).reverse().toString();
System.out.println(s.equals(rev) ? "Palindrome" : "Not Palindrome");
String s = "racecar";
char[] ch = s.toCharArray();
boolean flag = true;
for(int i = 0; i < ch.length / 2; i++){
if(ch[i] != ch[ch.length - 1 - i]){ flag = false; break; }
}
System.out.println(flag ? "Palindrome" : "Not Palindrome");
String s = "Hello";
String rev = "";
for(int i = s.length() - 1; i >= 0; i--){ // loop from end
rev += s.charAt(i); // append each char in reverse
}
System.out.println(rev); // "olleH"
String s = "Hello";
String rev = new StringBuilder(s).reverse().toString();
System.out.println(rev); // "olleH"
String s = "Hello";
char[] ch = s.toCharArray();
int l = 0, r = ch.length - 1;
while(l < r){ char t = ch[l]; ch[l] = ch[r]; ch[r] = t; l++; r--; }
System.out.println(new String(ch)); // "olleH"
String s = "Hello World";
int vowels = 0, consonants = 0;
s = s.toLowerCase(); // normalize case
for(int i = 0; i < s.length(); i++){
char c = s.charAt(i);
if(c == 'a'||c == 'e'||c == 'i'||c == 'o'||c == 'u'){
vowels++; // it's a vowel
} else if(c >= 'a' && c <= 'z'){
consonants++; // it's a letter but not vowel
}
}
System.out.println("Vowels: " + vowels + ", Consonants: " + consonants);
String s = "Hello World";
int vowels = s.replaceAll("[^aeiouAEIOU]", "").length();
int consonants = s.replaceAll("[^a-zA-Z]","").length() - vowels;
System.out.println("Vowels: " + vowels + ", Consonants: " + consonants);
String s1 = "listen", s2 = "silent";
char[] a = s1.toCharArray();
char[] b = s2.toCharArray();
Arrays.sort(a); // sort both
Arrays.sort(b);
System.out.println(Arrays.equals(a, b) ? "Anagram" : "Not Anagram");
String s1 = "listen", s2 = "silent";
if(s1.length() != s2.length()){
System.out.println("Not Anagram"); return;
}
int[] count = new int[26];
for(int i = 0; i < s1.length(); i++){
count[s1.charAt(i) - 'a']++; // increment for s1
count[s2.charAt(i) - 'a']--; // decrement for s2
}
for(int c : count){
if(c != 0){ System.out.println("Not Anagram"); return; }
}
System.out.println("Anagram");
String s = "aabbcdeeff";
char result = '-';
for(int i = 0; i < s.length(); i++){
boolean repeat = false;
for(int j = 0; j < s.length(); j++){
if(i != j && s.charAt(i) == s.charAt(j)){
repeat = true; break;
}
}
if(!repeat){ result = s.charAt(i); break; }
}
System.out.println("First non-repeat: " + result); // c
import java.util.LinkedHashMap;
String s = "aabbcdeeff";
LinkedHashMap map = new LinkedHashMap<>();
for(char c : s.toCharArray()){
map.put(c, map.getOrDefault(c, 0) + 1); // count frequency
}
for(char c : map.keySet()){
if(map.get(c) == 1){ // first with count 1
System.out.println("First non-repeat: " + c); // c
break;
}
}
| Class | Object | |
|---|---|---|
| Nature | Blueprint, Logical | Real implementation, Physical |
| Memory | No memory | Occupies memory |
| Count | One per design | Many can be created |
class Car {
String brand;
int speed;
void drive() {
System.out.println("Car is driving");
}
}
public class MainClass {
public static void main(String[] args) {
Car car1 = new Car();
car1.brand = "BMW";
car1.speed = 120;
car1.drive();
Car car2 = new Car();
car2.brand = "Audi";
car2.speed = 140;
car2.drive();
}
}
| Type | Parameters | Return Type |
|---|---|---|
| 1 | No parameter | No return |
| 2 | No parameter | Return value |
| 3 | With parameter | No return |
| 4 | With parameter | Return value |
// Type 1: No parameter, no return
void display() {
System.out.println("Hello");
}
// Type 4: With parameter, with return
int add(int a, int b) {
return a + b;
}
| Type | Meaning |
|---|---|
| Default constructor | No parameters |
| Parameterized constructor | With parameters |
class Car {
void Car() { // This is NOT a constructor
System.out.println("Hello");
}
}
int add(int a, int b) { return a + b; }
double add(int a, int b) { return a + b; } // ❌ Compile error
Car c = new Car();
Car c1 = new Car(); // Object 1
Car c2 = new Car(); // Object 2
c2 = c1;
Stack: c1 ----> Object1
c2 ----> Object1
Heap: Object1 (referenced ✔)
Object2 (no reference ❌)
Car c = null;
| Type | Happens At | Achieved By |
|---|---|---|
| Compile-Time | During compilation | Method Overloading |
| Runtime | During execution | Method Overriding |
class Calculator {
int add(int a, int b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
double add(double a, double b) {
return a + b;
}
}
class Animal {
void sound() {
System.out.println("Animal makes sound");
}
}
class Dog extends Animal {
void sound() {
System.out.println("Dog barks");
}
}
// Runtime Polymorphism
Animal a = new Dog();
a.sound();
| Overloading | Overriding | |
|---|---|---|
| Where | Same class | Parent-child class |
| Parameters | Must differ | Must be same |
| Decision | Compile-time | Runtime |
| Inheritance | Not required | Required |
| Return type | Can differ | Must be same |
class Demo {
public static void main(String[] args) {
System.out.println("Standard main called by JVM");
main(10); // manually calling overloaded
}
public static void main(int x) {
System.out.println("Overloaded main: " + x);
}
}
class Parent {
static void show() { System.out.println("Parent static"); }
}
class Child extends Parent {
static void show() { System.out.println("Child static"); }
}
Parent obj = new Child();
obj.show(); // Output: Parent static (method hiding, not overriding)
class Shape {
void draw() { System.out.println("Drawing Shape"); }
}
class Circle extends Shape {
void draw() { System.out.println("Drawing Circle"); }
}
class Rectangle extends Shape {
void draw() { System.out.println("Drawing Rectangle"); }
}
Shape s;
s = new Circle(); s.draw(); // Drawing Circle
s = new Rectangle(); s.draw(); // Drawing Rectangle
class Student {
private int marks; // private - hidden from outside
public int getMarks() { // getter
return marks;
}
public void setMarks(int m) { // setter
if(m >= 0) marks = m; // validation possible!
}
}
student.marks = -100; // ❌ No validation possible
With encapsulation, setter can validate:student.setMarks(-100); // setMarks checks: if(m >= 0)
| Modifier | Same Class | Same Package | Subclass | Everywhere |
|---|---|---|---|---|
| private | ✔ | ❌ | ❌ | ❌ |
| default | ✔ | ✔ | ❌ | ❌ |
| protected | ✔ | ✔ | ✔ | ❌ |
| public | ✔ | ✔ | ✔ | ✔ |
class Student {
int marks;
Student(int marks) {
this.marks = marks; // this.marks = instance variable
} // marks = constructor parameter
}
class Employee {
static String department = "CAC"; // shared by all employees
}
class Demo {
static void show() {
System.out.println("Hello");
}
}
Demo.show(); // called without object
| Rule | Static Method | Non-Static Method |
|---|---|---|
| Access static variables | ✔ Yes | ✔ Yes |
| Access non-static variables | ❌ No | ✔ Yes |
| Use this keyword | ❌ No | ✔ Yes |
| Need object to call | ❌ No | ✔ Yes |
| Concept | Belongs To |
|---|---|
| this | Object |
| static method | Class |
class Demo {
static void show() {
System.out.println(this); // ❌ Error
}
}
extends.class Animal {
void eat() { System.out.println("Animal eats"); }
}
class Dog extends Animal {
void bark() { System.out.println("Dog barks"); }
}
Dog d = new Dog();
d.eat(); // inherited from Animal
d.bark(); // Dog's own method
| Type | Description | Supported |
|---|---|---|
| Single | A extends B | ✔ |
| Multilevel | A extends B, B extends C | ✔ |
| Hierarchical | A and B both extend C | ✔ |
| Multiple | A extends B and C | ❌ (via interface only) |
| Hybrid | Mix of above | ❌ (via interface only) |
class Vehicle {
void start() { System.out.println("Vehicle starts"); }
}
class Bike extends Vehicle {
@Override
void start() { System.out.println("Bike kick-starts"); }
}
| Used With | Meaning |
|---|---|
| final variable | Value cannot be changed (constant) |
| final method | Cannot be overridden by child |
| final class | Cannot be extended / inherited |
final int MAX = 100; // constant
// MAX = 200; // ❌ Error
final class Utility { } // cannot extend this
class MyUtil extends Utility { } // ❌ Error
class Animal {
String name = "Animal";
void sound() { System.out.println("Some sound"); }
}
class Dog extends Animal {
String name = "Dog";
void display() {
System.out.println(super.name); // Animal
super.sound(); // Some sound
}
}
abstract class Shape {
abstract void draw(); // no implementation
void display() {
System.out.println("Shape displayed"); // has body
}
}
class Circle extends Shape {
void draw() {
System.out.println("Drawing Circle"); // must implement
}
}
interface Flyable {
void fly(); // abstract by default
}
class Bird implements Flyable {
public void fly() {
System.out.println("Bird is flying");
}
}
| Abstract Class | Interface | |
|---|---|---|
| Methods | Abstract + concrete | All abstract (Java 7), default/static (Java 8+) |
| Variables | Any type | public static final only |
| Inheritance | extends (one only) | implements (multiple) |
| Constructor | Yes | No |
| Abstraction | 0 to 100% | 100% |
| Use when | Partial implementation shared | Full contract, multiple inheritance |
try{
int result = 10/0; // ArithmeticException
}catch(ArithmeticException e){
System.out.println("Cannot divide by zero!");
}finally{
System.out.println("Always runs!");
}
throw new IllegalArgumentException("Invalid input");public void readFile() throws IOException { ... }try{ ... }catch(Exception e){ ... }finally{
connection.close(); // always closes!
}
| ArrayList | LinkedList | Vector | |
|---|---|---|---|
| Internal | Dynamic array | Doubly linked list | Dynamic array |
| Access | Fast O(1) | Slow O(n) | Fast O(1) |
| Insert/Delete | Slow O(n) | Fast O(1) | Slow O(n) |
| Thread-safe | No | No | Yes |
| Use when | Frequent reads | Frequent add/remove | Multi-thread |
| HashMap | LinkedHashMap | TreeMap | |
|---|---|---|---|
| Order | No order | Insertion order | Sorted order |
| Null key | 1 allowed | 1 allowed | Not allowed |
| Performance | O(1) | O(1) | O(log n) |
| Use when | Fast lookup | Ordered insert | Sorted keys needed |
compareTo().class Student implements Comparable{
public int compareTo(Student s){ return this.age - s.age; }
} compare().Collections.sort(list, (a,b) -> a.name.compareTo(b.name));
// ── What does it mean? ──────────────────────────────────────────
// Input: 123 → Output: 321
// We extract digits one by one from the END using % and build the reversed number.
// APPROACH 1: Loop using % and / (Standard – Best for interviews)
public static int reverseNumber(int n) {
int reversed = 0; // will hold the final reversed number
while (n != 0) { // loop until all digits are processed
int digit = n % 10; // % gives the LAST digit e.g. 123%10 = 3
reversed = reversed * 10 + digit; // shift reversed left, add digit
n = n / 10; // remove last digit e.g. 123/10 = 12
}
return reversed;
}
// DRY RUN for n = 123:
// Iteration 1: digit=3, reversed=0*10+3=3, n=12
// Iteration 2: digit=2, reversed=3*10+2=32, n=1
// Iteration 3: digit=1, reversed=32*10+1=321, n=0 → loop ends
// Result: 321 ✅
// APPROACH 2: Using String + StringBuilder (Easiest – 1 line)
public static int reverseV2(int n) {
String s = String.valueOf(Math.abs(n)); // 123 → "123"
String rev = new StringBuilder(s).reverse().toString(); // "123" → "321"
return Integer.parseInt(rev); // "321" → 321
}
// Note: Math.abs handles negative numbers
// Output:
// reverseNumber(123) → 321
// reverseNumber(1200) → 21 (leading zeros dropped)
// reverseNumber(9) → 9
// Count how many digits are in a number. 1234 → 4 digits
// APPROACH 1: Loop dividing by 10 (Best for interviews)
public static int countDigits(int n) {
n = Math.abs(n); // handle negative input
if (n == 0) return 1; // special case: 0 has exactly 1 digit
int count = 0;
while (n != 0) {
count++; // count this digit
n = n / 10; // chop off the last digit
}
return count;
}
// DRY RUN for n = 4536:
// n=4536 count=1, n=453 count=2, n=45 count=3, n=4 count=4, n=0 → stop
// Result: 4 ✅
// APPROACH 2: String length (Simplest, 1 line)
public static int countDigitsStr(int n) {
return String.valueOf(Math.abs(n)).length(); // convert to string, get length
}
// APPROACH 3: Math.log10 (Mathematical trick)
// log10(1000) = 3, so digits = floor(log10(n)) + 1
public static int countDigitsLog(int n) {
if (n == 0) return 1;
return (int)(Math.log10(Math.abs(n))) + 1;
}
// Output:
// countDigits(4536) → 4 countDigits(7) → 1 countDigits(1000) → 4
// Add all digits together. 253 → 2+5+3 = 10
// APPROACH 1: Loop with % (Standard)
public static int sumDigits(int n) {
n = Math.abs(n); // handle negatives
int sum = 0;
while (n != 0) {
sum += n % 10; // add last digit to sum
n /= 10; // remove last digit
}
return sum;
}
// DRY RUN for n = 253:
// n=253: sum=0+3=3, n=25
// n=25: sum=3+5=8, n=2
// n=2: sum=8+2=10, n=0 → stop
// Result: 10 ✅
// APPROACH 2: Using chars (String-based)
public static int sumDigitsStr(int n) {
int sum = 0;
for (char c : String.valueOf(Math.abs(n)).toCharArray()) {
sum += (c - '0'); // trick: '5' - '0' = 5 (ASCII subtraction)
}
return sum;
}
// Output:
// sumDigits(253) → 10 sumDigits(999) → 27 sumDigits(0) → 0
// A number is palindrome if it reads the same forwards and backwards.
// 121 → palindrome ✅ 123 → not ❌
// APPROACH 1: Reverse and compare (Standard)
public static boolean isNumPalindrome(int n) {
if (n < 0) return false; // negative numbers are never palindrome
int original = n; // save original to compare later
int reversed = 0;
while (n != 0) {
reversed = reversed * 10 + n % 10; // build reversed number digit by digit
n /= 10;
}
return original == reversed; // same? → palindrome
}
// DRY RUN for n = 121:
// n=121: rev=1, n=12
// n=12: rev=12, n=1
// n=1: rev=121, n=0
// original(121) == reversed(121) → true ✅
// APPROACH 2: String comparison (Easiest)
public static boolean isNumPalindromeStr(int n) {
String s = String.valueOf(Math.abs(n));
return s.equals(new StringBuilder(s).reverse().toString());
}
// Output:
// isNumPalindrome(121) → true isNumPalindrome(1221) → true
// isNumPalindrome(123) → false isNumPalindrome(-121) → false
// Armstrong Number: sum of each digit raised to power of (total digits) = original
// Example: 153 → 1³+5³+3³ = 1+125+27 = 153 ✅
// Example: 9474 → 9⁴+4⁴+7⁴+4⁴ = 6561+256+2401+256 = 9474 ✅
public static boolean isArmstrong(int n) {
int original = n;
int digits = String.valueOf(n).length(); // how many digits does n have?
int sum = 0;
while (n != 0) {
int d = n % 10; // extract last digit
sum += (int) Math.pow(d, digits); // add digit^totalDigits
n /= 10; // remove last digit
}
return sum == original; // does sum equal the original?
}
// DRY RUN for n = 153 (3 digits):
// d=3: sum=0+27=27, n=15
// d=5: sum=27+125=152, n=1
// d=1: sum=152+1=153, n=0
// 153 == 153 → true ✅
// Output:
// isArmstrong(153) → true isArmstrong(370) → true
// isArmstrong(9474) → true isArmstrong(100) → false
// Prime: divisible ONLY by 1 and itself. 7 is prime, 6 is not.
// Key trick: only check divisors up to √n — saves time!
// APPROACH 1: Optimized √n check (Best for interviews)
public static boolean isPrime(int n) {
if (n <= 1) return false; // 0 and 1 are NOT prime
if (n == 2) return true; // 2 is the only even prime
if (n % 2 == 0) return false; // all even numbers > 2 are not prime
for (int i = 3; i * i <= n; i += 2) { // check odd numbers up to √n only
if (n % i == 0) return false;
}
return true;
}
// WHY i*i <= n?
// Divisors come in pairs: for 36 → (2,18)(3,12)(4,9)(6,6)
// After √36=6, divisors just repeat in reverse. No need to go further!
// DRY RUN for n = 17:
// 17%2 ≠ 0 → continue
// i=3: 3*3=9 ≤ 17, 17%3 ≠ 0
// i=5: 5*5=25 > 17 → loop ends
// return true ✅
// APPROACH 2: Brute force (Simpler to understand)
public static boolean isPrimeSimple(int n) {
if (n <= 1) return false;
for (int i = 2; i < n; i++) { // check every number from 2 to n-1
if (n % i == 0) return false;
}
return true;
}
// Output:
// isPrime(2) → true isPrime(7) → true isPrime(17) → true
// isPrime(4) → false isPrime(1) → false isPrime(9) → false
// APPROACH 1: Use isPrime helper for each number
public static void printPrimes(int n) {
for (int i = 2; i <= n; i++) { // start from 2 (smallest prime)
if (isPrime(i)) System.out.print(i + " ");
}
}
// APPROACH 2: Sieve of Eratosthenes (Most Efficient — impress interviewers!)
// Idea: cross out all multiples of each prime starting from 2
public static void sieve(int n) {
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true); // assume all are prime
isPrime[0] = isPrime[1] = false; // 0 and 1 are not prime
for (int i = 2; i * i <= n; i++) {
if (isPrime[i]) { // if i is still marked prime
for (int j = i * i; j <= n; j += i) { // mark multiples of i as NOT prime
isPrime[j] = false; // starting from i*i (smaller already marked)
}
}
}
for (int i = 2; i <= n; i++)
if (isPrime[i]) System.out.print(i + " ");
}
// HOW SIEVE WORKS for n=10:
// Initial: all true except index 0,1
// i=2: mark 4,6,8,10 as false
// i=3: mark 9 as false (3*3=9)
// Result: 2 3 5 7 ✅
// Output: sieve(20) → 2 3 5 7 11 13 17 19
// GCD = Greatest Common Divisor = largest number that divides BOTH a and b
// GCD(12, 8) = 4 because 4 divides both 12 and 8
// APPROACH 1: Euclidean Algorithm – iterative (Best, O(log n))
// Formula: GCD(a,b) = GCD(b, a%b) until b = 0
public static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b; // new b = remainder of a÷b
a = temp; // new a = old b
}
return a; // when b=0, a holds the GCD
}
// DRY RUN for gcd(48, 18):
// a=48, b=18 → b=48%18=12, a=18
// a=18, b=12 → b=18%12=6, a=12
// a=12, b=6 → b=12%6=0, a=6
// b=0 → return 6 ✅
// APPROACH 2: Recursive
public static int gcdRec(int a, int b) {
return b == 0 ? a : gcdRec(b, a % b);
}
// BONUS — LCM using GCD:
// LCM(a,b) = (a * b) / GCD(a,b)
public static int lcm(int a, int b) {
return (a / gcd(a, b)) * b; // divide first to prevent overflow
}
// Output:
// gcd(48, 18) → 6 gcd(100, 75) → 25
// lcm(4, 6) → 12 lcm(3, 5) → 15
// Factorial: n! = n × (n-1) × (n-2) × ... × 1
// 5! = 5×4×3×2×1 = 120 0! = 1 (by definition)
// APPROACH 1: Iterative Loop (Best for interviews)
public static long factorial(int n) {
if (n < 0) return -1; // undefined for negatives
long result = 1; // use long — factorials grow very fast!
for (int i = 2; i <= n; i++) {
result *= i; // multiply result by each number
}
return result;
}
// DRY RUN for n = 5:
// i=2: result=1×2=2
// i=3: result=2×3=6
// i=4: result=6×4=24
// i=5: result=24×5=120 ✅
// APPROACH 2: Recursive (Classic — must know!)
public static long factRec(int n) {
if (n == 0 || n == 1) return 1; // base case
return n * factRec(n - 1); // n × factorial of (n-1)
}
// CALL STACK for n=4:
// factRec(4) = 4 × factRec(3)
// 3 × factRec(2)
// 2 × factRec(1) = 1
// returns: 2×1=2 → 3×2=6 → 4×6=24 ✅
// Output:
// factorial(5) → 120 factorial(0) → 1 factorial(10) → 3628800
// Fibonacci: 0, 1, 1, 2, 3, 5, 8, 13, 21 ...
// Each number = sum of the previous two numbers
// APPROACH 1: Iterative (Most Efficient — O(n) time, O(1) space)
public static void printFibonacci(int n) {
int a = 0, b = 1;
System.out.print(a + " " + b + " "); // print first two terms
for (int i = 2; i < n; i++) {
int c = a + b; // next = previous two added together
System.out.print(c + " ");
a = b; // slide the window: a moves forward
b = c; // b moves forward
}
}
// DRY RUN for n=7:
// Print 0 1
// i=2: c=1, a=1,b=1 → print 1
// i=3: c=2, a=1,b=2 → print 2
// i=4: c=3, a=2,b=3 → print 3
// i=5: c=5, a=3,b=5 → print 5
// i=6: c=8, a=5,b=8 → print 8
// Output: 0 1 1 2 3 5 8 ✅
// APPROACH 2: Return Nth term
public static int nthFib(int n) {
if (n <= 1) return n; // fib(0)=0, fib(1)=1
int a = 0, b = 1;
for (int i = 2; i <= n; i++) { int c = a+b; a=b; b=c; }
return b;
}
// Output:
// printFibonacci(8) → 0 1 1 2 3 5 8 13
// nthFib(6) → 8
// Swap means exchange the values of two variables.
// a=5, b=10 → a=10, b=5
// APPROACH 1: Using temp variable (SAFE — Always use this in production)
public static void swapWithTemp(int a, int b) {
System.out.println("Before: a=" + a + ", b=" + b);
int temp = a; // Step 1: save a's value in temp → temp=5
a = b; // Step 2: put b's value into a → a=10
b = temp; // Step 3: put temp's value into b → b=5
System.out.println("After: a=" + a + ", b=" + b);
}
// DRY RUN for a=5, b=10:
// temp=5 → a=10 → b=5 ✅
// Output: Before: a=5, b=10 After: a=10, b=5
// APPROACH 1: Arithmetic – using + and -
public static void swapNoTemp(int a, int b) {
System.out.println("Before: a=" + a + ", b=" + b);
a = a + b; // a = 5+10 = 15 (a now holds the sum)
b = a - b; // b = 15-10 = 5 (subtract original b → get original a)
a = a - b; // a = 15-5 = 10 (subtract original a → get original b)
System.out.println("After: a=" + a + ", b=" + b);
}
// ⚠️ Risk: overflow if a+b exceeds Integer.MAX_VALUE
// APPROACH 2: XOR Bitwise (No overflow risk — Pro technique!)
public static void swapXOR(int a, int b) {
a = a ^ b; // a = a XOR b
b = a ^ b; // b = (a XOR b) XOR b = a (original)
a = a ^ b; // a = (a XOR b) XOR a = b (original)
System.out.println("After XOR: a=" + a + ", b=" + b);
}
// XOR trick: x ^ x = 0 and x ^ 0 = x
// Output: Before: a=5, b=10 After: a=10, b=5
// APPROACH 1: if-else chain (Clear and readable)
public static int largest3(int a, int b, int c) {
if (a >= b && a >= c) return a; // a is largest
else if (b >= c) return b; // b is largest
else return c; // c is largest
}
// APPROACH 2: Nested Math.max (Cleanest)
public static int largest3V2(int a, int b, int c) {
return Math.max(a, Math.max(b, c)); // find max of b&c, then compare with a
}
// APPROACH 3: Ternary operator
public static int largest3V3(int a, int b, int c) {
int ab = (a > b) ? a : b; // max of a and b
return (ab > c) ? ab : c; // max of ab and c
}
// Output:
// largest3(5, 9, 3) → 9 largest3(20, 15, 20) → 20
// largest3(-1, -5, -3) → -1
// Even: divisible by 2 (last digit 0,2,4,6,8)
// Odd: NOT divisible by 2 (last digit 1,3,5,7,9)
// APPROACH 1: Modulo operator (Standard)
public static String evenOdd(int n) {
return (n % 2 == 0) ? n + " is Even" : n + " is Odd";
}
// APPROACH 2: Bitwise AND (Fastest — used in performance-critical code)
// How it works: even numbers end in 0 in binary, odd end in 1
// n & 1 checks the last bit only
public static String evenOddBit(int n) {
return ((n & 1) == 0) ? n + " is Even" : n + " is Odd";
}
// Binary examples:
// 4 = 0100 → 0100 & 0001 = 0 → Even ✅
// 7 = 0111 → 0111 & 0001 = 1 → Odd ✅
// 10 = 1010 → 1010 & 0001 = 0 → Even ✅
// Output:
// evenOdd(4) → Even evenOdd(7) → Odd evenOdd(-6) → Even
// Natural numbers: 1, 2, 3, 4 ... N
// APPROACH 1: Formula — O(1) INSTANT! (Best)
// Sum = N × (N+1) / 2 ← Gauss formula
public static long sumN(int n) {
return (long) n * (n + 1) / 2; // cast to long to prevent overflow
}
// Example: n=100 → 100×101/2 = 5050
// APPROACH 2: For loop — O(n)
public static long sumNLoop(int n) {
long sum = 0;
for (int i = 1; i <= n; i++) sum += i;
return sum;
}
// DRY RUN for n=5:
// i=1:sum=1, i=2:sum=3, i=3:sum=6, i=4:sum=10, i=5:sum=15 ✅
// APPROACH 3: Recursive
public static long sumNRec(int n) {
return (n == 0) ? 0 : n + sumNRec(n - 1);
}
// Output:
// sumN(100) → 5050 sumN(10) → 55 sumN(5) → 15
// APPROACH 1: For loop (Without inbuilt — best for interviews)
public static String reverseStr(String s) {
String result = "";
for (int i = s.length() - 1; i >= 0; i--) {
result += s.charAt(i); // add each char from end to start
}
return result;
}
// APPROACH 2: Two Pointer on char array (Efficient, O(n))
public static String reverseV2(String s) {
char[] arr = s.toCharArray(); // "Hello" → ['H','e','l','l','o']
int left = 0, right = arr.length - 1;
while (left < right) {
char temp = arr[left]; // swap left and right chars
arr[left] = arr[right];
arr[right] = temp;
left++; right--; // move pointers inward
}
return new String(arr);
}
// DRY RUN for "Java":
// arr=[J,a,v,a]
// left=0,right=3 → swap J,a → [a,a,v,J]
// left=1,right=2 → swap a,v → [a,v,a,J]
// Result: "avaJ" ✅
// APPROACH 3: StringBuilder.reverse() (Inbuilt — simplest)
public static String reverseV3(String s) {
return new StringBuilder(s).reverse().toString();
}
// Output:
// reverseStr("Hello") → "olleH" reverseV3("Java") → "avaJ"
// Palindrome: reads same forwards and backwards. "racecar" ✅ "hello" ❌
// APPROACH 1: Two Pointer (Best — O(n), no extra space)
public static boolean isPalindromeStr(String s) {
s = s.toLowerCase(); // make comparison case-insensitive
int left = 0, right = s.length() - 1;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) return false; // mismatch!
left++; right--;
}
return true;
}
// DRY RUN for "racecar":
// l=0,r=6: r==r ✅ l=1,r=5: a==a ✅ l=2,r=4: c==c ✅
// l=3=r=3: pointers meet → stop → return true ✅
// APPROACH 2: Reverse and compare (Simple)
public static boolean isPalindromeV2(String s) {
String low = s.toLowerCase();
return low.equals(new StringBuilder(low).reverse().toString());
}
// Output:
// isPalindromeStr("racecar") → true isPalindromeStr("Madam") → true
// isPalindromeStr("hello") → false
// APPROACH 1: split() with \\s+ (Handles multiple spaces, tabs)
public static int countWords(String s) {
s = s.trim(); // remove leading/trailing spaces
if (s.isEmpty()) return 0;
return s.split("\\s+").length; // \\s+ = one or more whitespace chars
}
// APPROACH 2: Manual loop (Without inbuilt)
public static int countWordsManual(String s) {
int count = 0;
boolean inWord = false;
for (char c : s.toCharArray()) {
if (c != ' ' && !inWord) { // entering a new word
count++;
inWord = true;
} else if (c == ' ') {
inWord = false; // leaving a word
}
}
return count;
}
// DRY RUN for "Hello World Java":
// H→inWord=true,count=1 space→inWord=false
// W→inWord=true,count=2 space→inWord=false
// J→inWord=true,count=3 → end
// Result: 3 ✅
// Output:
// countWords("Hello World Java") → 3 countWords(" Hi there ") → 2
// Vowels: a e i o u Consonants: all other alphabets
public static void countVC(String s) {
s = s.toLowerCase(); // normalize: treat A and a the same
int vowels = 0, consonants = 0;
for (char c : s.toCharArray()) {
if (c >= 'a' && c <= 'z') { // only process alphabet chars
if ("aeiou".indexOf(c) != -1) { // is it a vowel?
vowels++;
} else {
consonants++; // it's a consonant
}
}
// spaces, digits, symbols are simply ignored
}
System.out.println("Vowels: " + vowels + ", Consonants: " + consonants);
}
// DRY RUN for "Hello":
// h→consonant(1) e→vowel(1) l→consonant(2) l→consonant(3) o→vowel(2)
// Vowels=2, Consonants=3 ✅
// Output:
// countVC("Java") → Vowels: 2, Consonants: 2
// countVC("Hello!") → Vowels: 2, Consonants: 3
// APPROACH 1: replaceAll with regex (Easiest — 1 line)
public static String removeSpaces(String s) {
return s.replaceAll("\\s", ""); // \\s matches space, tab, newline — replaces with ""
}
// APPROACH 2: Loop (Without inbuilt — for interview)
public static String removeSpacesManual(String s) {
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
if (c != ' ') sb.append(c); // only keep non-space characters
}
return sb.toString();
}
// APPROACH 3: Stream (Java 8+)
public static String removeSpacesStream(String s) {
return s.chars()
.filter(c -> c != ' ')
.collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
.toString();
}
// Output:
// removeSpaces("Hello World") → "HelloWorld"
// removeSpaces("J a v a") → "Java"
// Input: "Hello World Java"
// Output: "olleH dlroW avaJ" — each word reversed, order stays same
public static String reverseEachWord(String s) {
String[] words = s.split(" "); // split into array of words
StringBuilder result = new StringBuilder();
for (String word : words) {
result.append(new StringBuilder(word).reverse()); // reverse each word
result.append(" "); // add space separator
}
return result.toString().trim(); // remove trailing space
}
// ITERATION for "Hi Java":
// word="Hi" → "iH"
// word="Java" → "avaJ"
// Result: "iH avaJ" ✅
// Output:
// reverseEachWord("Hello World") → "olleH dlroW"
// reverseEachWord("Java is fun") → "avaJ si nuf"
// Input: "Hello World Java"
// Output: "Java World Hello" — word ORDER reversed, each word stays same
// APPROACH 1: Split → reverse array → join
public static String reverseWordOrder(String s) {
String[] words = s.trim().split("\\s+"); // split on any whitespace
int left = 0, right = words.length - 1;
while (left < right) {
String tmp = words[left]; // swap words at left and right positions
words[left] = words[right];
words[right] = tmp;
left++; right--;
}
return String.join(" ", words); // join array back into sentence
}
// DRY RUN for "Java is great":
// words = ["Java","is","great"]
// swap index 0 and 2: ["great","is","Java"]
// Result: "great is Java" ✅
// APPROACH 2: Collections.reverse (One-liner style)
public static String reverseWordOrderV2(String s) {
List list = new ArrayList<>(Arrays.asList(s.trim().split("\\s+")));
Collections.reverse(list);
return String.join(" ", list);
}
// Output:
// reverseWordOrder("Hello World Java") → "Java World Hello"
// APPROACH 1: Manual conversion (Shows how parseInt works internally)
public static int strToInt(String s) {
int result = 0;
boolean negative = false;
int start = 0;
if (s.charAt(0) == '-') { negative = true; start = 1; } // handle sign
for (int i = start; i < s.length(); i++) {
char c = s.charAt(i);
if (c < '0' || c > '9') throw new IllegalArgumentException("Invalid: " + c);
int digit = c - '0'; // ASCII trick: '5'-'0' = 5
result = result * 10 + digit; // build number digit by digit
}
return negative ? -result : result;
}
// DRY RUN for "123":
// c='1': digit=1, result=1
// c='2': digit=2, result=12
// c='3': digit=3, result=123 ✅
// APPROACH 2: Integer.parseInt (Standard)
public static int strToIntBuiltin(String s) {
return Integer.parseInt(s); // throws NumberFormatException if invalid
}
// Output:
// strToInt("123") → 123 strToInt("-45") → -45
// APPROACH 1: Loop (Without inbuilt)
public static boolean isAllDigits(String s) {
if (s == null || s.isEmpty()) return false;
for (char c : s.toCharArray()) {
if (c < '0' || c > '9') return false; // found a non-digit
}
return true;
}
// APPROACH 2: matches() with regex (Simplest)
public static boolean isAllDigitsRegex(String s) {
return s != null && s.matches("\\d+"); // \\d = digit, + = one or more
}
// APPROACH 3: Character.isDigit() with stream (Java 8)
public static boolean isAllDigitsStream(String s) {
return s != null && !s.isEmpty() && s.chars().allMatch(Character::isDigit);
}
// Output:
// isAllDigits("12345") → true isAllDigits("123a5") → false
// APPROACH 1: LinkedHashMap (Best — handles all chars, preserves order)
public static void charFrequency(String s) {
Map map = new LinkedHashMap<>();
for (char c : s.toCharArray()) {
map.put(c, map.getOrDefault(c, 0) + 1); // if key exists add 1, else put 1
}
for (Map.Entry e : map.entrySet()) {
System.out.println("'" + e.getKey() + "' → " + e.getValue());
}
}
// APPROACH 2: int array (Only lowercase a-z — faster, less memory)
public static void charFreqArray(String s) {
int[] freq = new int[26]; // index 0=a, 1=b, ..., 25=z
for (char c : s.toLowerCase().toCharArray()) {
if (c >= 'a' && c <= 'z') freq[c - 'a']++; // map 'a'→0, 'b'→1 etc.
}
for (int i = 0; i < 26; i++) {
if (freq[i] > 0) System.out.println((char)('a' + i) + " → " + freq[i]);
}
}
// DRY RUN for "aabb":
// 'a':map={a:1}, 'a':map={a:2}, 'b':map={a:2,b:1}, 'b':map={a:2,b:2}
// Output: a→2, b→2 ✅
// Output for "programming": p→1, r→2, o→1, g→2, a→1, m→2, i→1, n→1
// First character that appears ONLY once. "aabbcd" → 'c'
// APPROACH 1: LinkedHashMap — two passes (Best)
public static char firstNonRepeating(String s) {
Map map = new LinkedHashMap<>(); // preserves insertion order
for (char c : s.toCharArray())
map.put(c, map.getOrDefault(c, 0) + 1); // pass 1: build frequency map
for (Map.Entry e : map.entrySet())
if (e.getValue() == 1) return e.getKey(); // pass 2: first with count=1
return '_'; // no non-repeating char found
}
// APPROACH 2: int array — two passes (Faster for a-z)
public static char firstNonRepArray(String s) {
int[] freq = new int[256]; // all ASCII characters
for (char c : s.toCharArray()) freq[c]++; // pass 1: count
for (char c : s.toCharArray()) // pass 2: find first with 1
if (freq[c] == 1) return c;
return '_';
}
// DRY RUN for "aabbcd":
// map: a→2, b→2, c→1, d→1
// first entry with value=1 → 'c' ✅
// Output:
// firstNonRepeating("aabbcd") → c
// firstNonRepeating("leetcode") → l
// firstNonRepeating("aabb") → _
// Characters that appear MORE THAN ONCE
// APPROACH 1: HashMap — count then filter
public static void findDuplicates(String s) {
Map map = new LinkedHashMap<>();
for (char c : s.toLowerCase().toCharArray())
map.put(c, map.getOrDefault(c, 0) + 1);
System.out.print("Duplicates: ");
for (Map.Entry e : map.entrySet())
if (e.getValue() > 1)
System.out.print(e.getKey() + "(" + e.getValue() + ") ");
}
// APPROACH 2: Two Sets (seen + duplicates)
public static void findDuplicatesSet(String s) {
Set seen = new HashSet<>();
Set dups = new LinkedHashSet<>();
for (char c : s.toCharArray())
if (!seen.add(c)) dups.add(c); // add() returns false if already present
System.out.println("Duplicates: " + dups);
}
// DRY RUN for "programming":
// seen: p,r→ r again! dup={r} g→ g again! dup={r,g} m→ m again! dup={r,g,m}
// Output:
// findDuplicates("programming") → r(2) g(2) m(2)
// Anagram: same characters, different arrangement. "listen" = "silent" ✅
// APPROACH 1: Sort both and compare (Simple, O(n log n))
public static boolean isAnagram(String a, String b) {
if (a.length() != b.length()) return false; // lengths must match
char[] ca = a.toLowerCase().toCharArray();
char[] cb = b.toLowerCase().toCharArray();
Arrays.sort(ca); Arrays.sort(cb); // sort both
return Arrays.equals(ca, cb); // compare sorted arrays
}
// APPROACH 2: Frequency array (Better, O(n) no sorting)
public static boolean isAnagramV2(String a, String b) {
if (a.length() != b.length()) return false;
int[] freq = new int[26];
for (int i = 0; i < a.length(); i++) {
freq[a.charAt(i) - 'a']++; // +1 for each char in a
freq[b.charAt(i) - 'a']--; // -1 for each char in b
}
for (int f : freq) if (f != 0) return false; // any mismatch → not anagram
return true;
}
// DRY RUN for "listen" vs "silent":
// After sort: "eilnst" == "eilnst" → true ✅
// Output:
// isAnagram("listen","silent") → true
// isAnagram("Anagram","Nagaram") → true
// isAnagram("hello","world") → false
// Keep only FIRST occurrence of each character.
// "programming" → "progamin"
// APPROACH 1: LinkedHashSet (Best — preserves order, auto-removes dups)
public static String removeDups(String s) {
Set seen = new LinkedHashSet<>();
for (char c : s.toCharArray()) seen.add(c); // Set ignores duplicates automatically
StringBuilder sb = new StringBuilder();
for (char c : seen) sb.append(c);
return sb.toString();
}
// APPROACH 2: Manual boolean array (Without collections)
public static String removeDupsManual(String s) {
boolean[] seen = new boolean[256]; // one slot per ASCII character
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
if (!seen[c]) { // first time seeing this char?
sb.append(c); // add it to result
seen[c] = true; // mark as seen
}
}
return sb.toString();
}
// DRY RUN for "aabbcc":
// a:new→add a:seen→skip b:new→add b:seen→skip c:new→add c:seen→skip
// Result: "abc" ✅
// Output:
// removeDups("programming") → "progamin" removeDups("aabbcc") → "abc"
// Largest = word with most characters
// "Java is a programming language" → "programming" (11 chars)
// APPROACH 1: Loop — compare lengths
public static String largestWord(String s) {
String[] words = s.trim().split("\\s+");
String largest = "";
for (String word : words) {
if (word.length() > largest.length()) {
largest = word; // update if this word is longer
}
}
return largest;
}
// ITERATION for "Java is a programming language":
// "Java"(4) > ""(0) → largest="Java"
// "is"(2) < "Java"(4) → skip
// "a"(1) < "Java"(4) → skip
// "programming"(11) > "Java" → largest="programming"
// "language"(8) < "programming" → skip
// Result: "programming" ✅
// APPROACH 2: Stream (Java 8 — elegant)
public static String largestWordStream(String s) {
return Arrays.stream(s.trim().split("\\s+"))
.max(Comparator.comparingInt(String::length))
.orElse("");
}
// Output:
// largestWord("Java is programming") → "programming"
// largestWord("Hi there everyone") → "everyone"
// APPROACH 1: Loop (Without inbuilt — Best for interview)
public static int findLargest(int[] arr) {
int max = arr[0]; // assume first element is largest
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) max = arr[i]; // update max if current is bigger
}
return max;
}
// DRY RUN for [3, 7, 1, 9, 4]:
// max=3 → arr[1]=7>3 → max=7 → arr[2]=1<7 → arr[3]=9>7 → max=9 → arr[4]=4<9
// Result: 9 ✅
// APPROACH 2: Arrays.stream (Java 8 — 1 line)
public static int findLargestStream(int[] arr) {
return Arrays.stream(arr).max().getAsInt();
}
// Output: findLargest([3,7,1,9,4]) → 9
// Same logic as largest — just flip the comparison
public static int findSmallest(int[] arr) {
int min = arr[0]; // assume first element is smallest
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) min = arr[i]; // update if current is smaller
}
return min;
}
// DRY RUN for [3, 7, 1, 9, 4]:
// min=3 → 7>3 skip → 1<3 min=1 → 9>1 skip → 4>1 skip
// Result: 1 ✅
// APPROACH 2: Stream
public static int findSmallestStream(int[] arr) {
return Arrays.stream(arr).min().getAsInt();
}
// APPROACH 1: Single pass — track both max and second max
public static int secondLargest(int[] arr) {
int max = Integer.MIN_VALUE, second = Integer.MIN_VALUE;
for (int n : arr) {
if (n > max) {
second = max; // old max becomes second
max = n; // new max found
} else if (n > second && n != max) {
second = n; // update second if bigger than current second (and not equal to max)
}
}
return second;
}
// DRY RUN for [3, 7, 1, 9, 4]:
// n=3: max=3, second=MIN
// n=7: 7>3 → second=3, max=7
// n=1: 1<7 and 1<3 → skip
// n=9: 9>7 → second=7, max=9
// n=4: 4<9 and 4>7? No. 4>second(7)? No → skip
// Result: 7 ✅
// APPROACH 2: Sort (Simple but O(n log n))
public static int secondLargestSort(int[] arr) {
int[] sorted = Arrays.copyOf(arr, arr.length);
Arrays.sort(sorted); // sort ascending
return sorted[sorted.length - 2]; // second from end
}
// ⚠️ Won't work for duplicates like [5,5,3] — use APPROACH 1 instead
// Output: secondLargest([3,7,1,9,4]) → 7
// APPROACH 1: Two pointer (In-place, no extra space)
public static void reverseArray(int[] arr) {
int left = 0, right = arr.length - 1;
while (left < right) {
int temp = arr[left]; // swap arr[left] and arr[right]
arr[left] = arr[right];
arr[right] = temp;
left++; right--; // move pointers inward
}
}
// DRY RUN for [1, 2, 3, 4, 5]:
// l=0,r=4: swap 1,5 → [5,2,3,4,1]
// l=1,r=3: swap 2,4 → [5,4,3,2,1]
// l=2,r=2: pointers meet → stop
// Result: [5,4,3,2,1] ✅
// APPROACH 2: New array (If you need original preserved)
public static int[] reverseArrayNew(int[] arr) {
int[] result = new int[arr.length];
for (int i = 0; i < arr.length; i++)
result[i] = arr[arr.length - 1 - i]; // fill from end of original
return result;
}
// Sorted (ascending): each element <= next element
// [1,2,3,4,5] ✅ [1,3,2,4] ❌
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) return false; // found a pair out of order
}
return true; // all pairs were in order
}
// DRY RUN for [1, 3, 2, 4]:
// i=0: arr[0]=1 <= arr[1]=3 ✅
// i=1: arr[1]=3 > arr[2]=2 ❌ → return false ✅
// Output:
// isSorted([1,2,3,4,5]) → true isSorted([1,3,2,4]) → false
// Left rotate: first element goes to end
// [1,2,3,4,5] → [2,3,4,5,1]
public static void leftRotateOne(int[] arr) {
int first = arr[0]; // save first element
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = arr[i + 1]; // shift each element one step left
}
arr[arr.length - 1] = first; // put saved element at end
}
// DRY RUN for [1,2,3,4,5]:
// first=1
// arr=[2,3,4,5,5] (shifting)
// arr[4]=1 → [2,3,4,5,1] ✅
// [1,2,3,4,5], K=2 → [3,4,5,1,2]
// APPROACH 1: Reversal Algorithm (Most Efficient — O(n), O(1) space)
// Step 1: Reverse first K elements
// Step 2: Reverse remaining elements
// Step 3: Reverse entire array
public static void leftRotateK(int[] arr, int k) {
int n = arr.length;
k = k % n; // handle k > n (e.g. k=7 on size 5 = k=2)
reverse(arr, 0, k - 1); // reverse [0..k-1]
reverse(arr, k, n - 1); // reverse [k..n-1]
reverse(arr, 0, n - 1); // reverse entire array
}
public static void reverse(int[] arr, int l, int r) {
while (l < r) { int t=arr[l]; arr[l]=arr[r]; arr[r]=t; l++; r--; }
}
// DRY RUN for [1,2,3,4,5], K=2:
// Reverse [0..1]: [2,1,3,4,5]
// Reverse [2..4]: [2,1,5,4,3]
// Reverse all: [3,4,5,1,2] ✅
// APPROACH 2: Using temp array (Simpler to understand)
public static void leftRotateKSimple(int[] arr, int k) {
int n = arr.length; k = k % n;
int[] temp = Arrays.copyOfRange(arr, 0, k); // save first k elements
for (int i = 0; i < n - k; i++) arr[i] = arr[i + k]; // shift left
for (int i = 0; i < k; i++) arr[n - k + i] = temp[i]; // fill end
}
// [1,2,3,4,5], K=2 → [4,5,1,2,3]
// Right rotate K = Left rotate (n-K)
public static void rightRotateK(int[] arr, int k) {
int n = arr.length;
k = k % n; // normalize k
// Right rotation K = Left rotation (n-K)
reverse(arr, 0, n - k - 1); // reverse first (n-k) elements
reverse(arr, n - k, n - 1); // reverse last k elements
reverse(arr, 0, n - 1); // reverse entire array
}
// DRY RUN for [1,2,3,4,5], K=2 (n=5):
// Reverse [0..2]: [3,2,1,4,5]
// Reverse [3..4]: [3,2,1,5,4]
// Reverse all: [4,5,1,2,3] ✅
// Since array is SORTED, duplicates are always adjacent.
// [1,1,2,2,3,4,4] → [1,2,3,4] — return count of unique elements
// Two Pointer technique (O(n), O(1) space — Best)
public static int removeDuplicatesSorted(int[] arr) {
if (arr.length == 0) return 0;
int j = 0; // j = position to place next unique element
for (int i = 1; i < arr.length; i++) {
if (arr[i] != arr[j]) { // found a new unique element
j++; // advance write pointer
arr[j] = arr[i]; // write it to correct position
}
}
return j + 1; // number of unique elements
}
// DRY RUN for [1,1,2,2,3]:
// j=0 (arr[j]=1)
// i=1: arr[1]=1 == arr[j]=1 → skip
// i=2: arr[2]=2 != arr[j]=1 → j=1, arr[1]=2 → [1,2,2,2,3]
// i=3: arr[3]=2 == arr[j]=2 → skip
// i=4: arr[4]=3 != arr[j]=2 → j=2, arr[2]=3 → [1,2,3,2,3]
// return 3 ✅ (first 3 elements: [1,2,3])
// Find all elements that appear more than once
// [1,2,3,2,4,3] → duplicates: 2, 3
// APPROACH 1: HashSet — add and detect duplicates
public static void findDuplicatesArr(int[] arr) {
Set seen = new HashSet<>();
Set dups = new LinkedHashSet<>();
for (int n : arr) {
if (!seen.add(n)) dups.add(n); // add() returns false = already existed
}
System.out.println("Duplicates: " + dups);
}
// APPROACH 2: HashMap frequency count
public static void findDuplicatesMap(int[] arr) {
Map map = new LinkedHashMap<>();
for (int n : arr) map.put(n, map.getOrDefault(n, 0) + 1);
for (Map.Entry e : map.entrySet())
if (e.getValue() > 1) System.out.print(e.getKey() + " ");
}
// DRY RUN for [1,2,3,2,4,3]:
// seen: 1,2,3 → 2 already! dup={2} → 4 → 3 already! dup={2,3}
// Output: 2 3 ✅
// Print how many times each element appears
// [1,2,2,3,3,3] → 1→1, 2→2, 3→3
public static void countFrequency(int[] arr) {
Map map = new LinkedHashMap<>();
for (int n : arr)
map.put(n, map.getOrDefault(n, 0) + 1); // count each element
for (Map.Entry e : map.entrySet())
System.out.println(e.getKey() + " → " + e.getValue());
}
// DRY RUN for [1,2,2,3]:
// 1→1, 2→1 then 2→2, 3→1
// Output: 1→1, 2→2, 3→1 ✅
// [2, 3, 2, 4, 3] → answer is 4 (only 4 appears once)
// APPROACH 1: XOR trick (Most elegant — O(n), O(1) space!)
// Key property: x ^ x = 0 and x ^ 0 = x
// So XOR of all elements — pairs cancel out, leaving the single element
public static int findSingle(int[] arr) {
int result = 0;
for (int n : arr) result ^= n; // XOR all elements
return result;
}
// DRY RUN for [2,3,2,4,3]:
// result = 0^2=2 → 2^3=1 → 1^2=3 → 3^4=7 → 7^3=4
// Result: 4 ✅ (pairs cancel: 2^2=0, 3^3=0)
// APPROACH 2: HashMap (Easier to understand)
public static int findSingleMap(int[] arr) {
Map map = new HashMap<>();
for (int n : arr) map.put(n, map.getOrDefault(n,0)+1);
for (Map.Entry e : map.entrySet())
if (e.getValue() == 1) return e.getKey();
return -1;
}
// Output: findSingle([2,3,2,4,3]) → 4
// Array has n-1 elements from 1 to n, one number missing.
// [1,2,4,5] from 1-5 → missing is 3
// APPROACH 1: Sum formula (Best — O(n), O(1))
// Expected sum of 1 to N = N*(N+1)/2
// Missing = Expected sum − Actual sum
public static int findMissing(int[] arr, int n) {
int expected = n * (n + 1) / 2; // what the sum SHOULD be
int actual = 0;
for (int x : arr) actual += x; // what the sum actually is
return expected - actual; // difference = missing number
}
// DRY RUN for [1,2,4,5], n=5:
// expected = 5*6/2 = 15
// actual = 1+2+4+5 = 12
// missing = 15-12 = 3 ✅
// APPROACH 2: XOR (Handles large numbers without overflow)
public static int findMissingXOR(int[] arr, int n) {
int xor = 0;
for (int i = 1; i <= n; i++) xor ^= i; // XOR all 1 to N
for (int x : arr) xor ^= x; // XOR with all array elements
return xor; // all pairs cancel, leaving the missing number
}
// In binary array, find longest run of consecutive 1s
// [1,1,0,1,1,1] → 3
public static int maxConsecutiveOnes(int[] arr) {
int maxCount = 0; // overall maximum
int count = 0; // current run of ones
for (int n : arr) {
if (n == 1) {
count++; // extend current run
maxCount = Math.max(maxCount, count); // update max if needed
} else {
count = 0; // reset run on seeing 0
}
}
return maxCount;
}
// DRY RUN for [1,1,0,1,1,1]:
// n=1:count=1,max=1 n=1:count=2,max=2 n=0:count=0
// n=1:count=1,max=2 n=1:count=2,max=2 n=1:count=3,max=3
// Result: 3 ✅
// Output:
// maxConsecutiveOnes([1,1,0,1,1,1]) → 3
// maxConsecutiveOnes([0,0,1,0]) → 1
// [1,3,5] + [2,4,6] → [1,2,3,4,5,6]
// Both arrays already sorted — merge without sorting again
public static int[] mergeSorted(int[] a, int[] b) {
int i = 0, j = 0, k = 0;
int[] result = new int[a.length + b.length];
while (i < a.length && j < b.length) {
if (a[i] <= b[j]) result[k++] = a[i++]; // pick smaller element
else result[k++] = b[j++];
}
while (i < a.length) result[k++] = a[i++]; // copy remaining from a
while (j < b.length) result[k++] = b[j++]; // copy remaining from b
return result;
}
// DRY RUN for [1,3,5] and [2,4,6]:
// i=0,j=0: 1<2 → result[0]=1, i=1
// i=1,j=0: 3>2 → result[1]=2, j=1
// i=1,j=1: 3<4 → result[2]=3, i=2
// i=2,j=1: 5>4 → result[3]=4, j=2
// i=2,j=2: 5<6 → result[4]=5, i=3 → loop ends
// copy remaining b: result[5]=6
// Result: [1,2,3,4,5,6] ✅
// Elements that exist in BOTH arrays (no duplicates) // [1,2,3,4] ∩ [3,4,5,6] → [3,4] public static Listintersection(int[] a, int[] b) { Set setA = new HashSet<>(); for (int x : a) setA.add(x); // put all of a into a set Set result = new LinkedHashSet<>(); for (int x : b) { if (setA.contains(x)) result.add(x); // if b's element exists in a → common } return new ArrayList<>(result); } // DRY RUN for [1,2,3,4] and [3,4,5,6]: // setA = {1,2,3,4} // b: 3→in setA! 4→in setA! 5→not in setA. 6→not in setA. // result = {3,4} ✅
// All unique elements from BOTH arrays combined // [1,2,3] ∪ [3,4,5] → [1,2,3,4,5] public static Listunion(int[] a, int[] b) { Set set = new LinkedHashSet<>(); // preserves insertion order for (int x : a) set.add(x); // add all of a for (int x : b) set.add(x); // add all of b (duplicates auto-ignored by Set) return new ArrayList<>(set); } // LinkedHashSet automatically handles: // 1. Removing duplicates (Set property) // 2. Preserving insertion order (Linked property) // Output: // union([1,2,3],[3,4,5]) → [1,2,3,4,5]
// Find if any two elements sum to a target value
// [2,7,4,3], target=9 → 2+7=9 ✅
// APPROACH 1: HashMap (O(n) — Best)
public static void twoSum(int[] arr, int target) {
Map map = new HashMap<>(); // stores value → index
for (int i = 0; i < arr.length; i++) {
int complement = target - arr[i]; // what number do we need?
if (map.containsKey(complement)) {
System.out.println("Pair found: " + complement + " + " + arr[i]);
}
map.put(arr[i], i); // store current element
}
}
// DRY RUN for [2,7,4,3], target=9:
// i=0: need 9-2=7 → not in map. map={2:0}
// i=1: need 9-7=2 → 2 IS in map! → Print: 2+7=9 ✅
// APPROACH 2: Brute force (O(n²) — Easy to understand)
public static void twoSumBrute(int[] arr, int target) {
for (int i = 0; i < arr.length; i++)
for (int j = i+1; j < arr.length; j++)
if (arr[i] + arr[j] == target)
System.out.println(arr[i] + " + " + arr[j]);
}
// Majority element: appears more than n/2 times
// [3,3,4,2,3,3,3] → 3 (appears 5 times in array of 7)
// APPROACH 1: Boyer-Moore Voting Algorithm (O(n), O(1) — Pro!)
public static int majorityElement(int[] arr) {
int candidate = arr[0], votes = 1;
for (int i = 1; i < arr.length; i++) {
if (votes == 0) {
candidate = arr[i]; // reset candidate
votes = 1;
} else if (arr[i] == candidate) {
votes++; // same element — add vote
} else {
votes--; // different element — cancel vote
}
}
return candidate; // candidate with remaining votes is majority
}
// DRY RUN for [3,3,4,2,3,3,3]:
// cand=3,v=1 → 3:v=2 → 4:v=1 → 2:v=0 → reset:cand=3,v=1 → 3:v=2 → 3:v=3
// Result: 3 ✅
// APPROACH 2: HashMap (Simpler to understand)
public static int majorityElementMap(int[] arr) {
Map map = new HashMap<>();
for (int n : arr) map.put(n, map.getOrDefault(n,0)+1);
for (Map.Entry e : map.entrySet())
if (e.getValue() > arr.length/2) return e.getKey();
return -1;
}
// Find contiguous subarray with largest sum
// [-2,1,-3,4,-1,2,1,-5,4] → 6 (subarray [4,-1,2,1])
// Kadane's Algorithm — O(n), O(1)
public static int maxSubarraySum(int[] arr) {
int maxSum = arr[0]; // overall max seen so far
int current = arr[0]; // current running sum
for (int i = 1; i < arr.length; i++) {
// either extend existing subarray OR start fresh from arr[i]
current = Math.max(arr[i], current + arr[i]);
maxSum = Math.max(maxSum, current); // update global max
}
return maxSum;
}
// DRY RUN for [-2,1,-3,4,-1,2,1,-5,4]:
// current=-2, max=-2
// i=1: max(1,-2+1=-1)=1, max=-2→1=1
// i=2: max(-3,1-3=-2)=-2, max=1
// i=3: max(4,-2+4=2)=4, max=4
// i=4: max(-1,4-1=3)=3, max=4
// i=5: max(2,3+2=5)=5, max=5
// i=6: max(1,5+1=6)=6, max=6 ✅
// Result: 6
// Output:
// maxSubarraySum([-2,1,-3,4,-1,2,1,-5,4]) → 6
// maxSubarraySum([-1,-2,-3]) → -1
// Array of 1 to N, one number repeating, one missing
// [1,2,2,4] from 1-4 → repeating=2, missing=3
public static void findRepeatMissing(int[] arr) {
int n = arr.length;
Map map = new HashMap<>();
for (int x : arr) map.put(x, map.getOrDefault(x,0)+1);
int repeating = -1, missing = -1;
for (int i = 1; i <= n; i++) {
int count = map.getOrDefault(i, 0);
if (count == 2) repeating = i; // appears twice = repeating
if (count == 0) missing = i; // never appears = missing
}
System.out.println("Repeating: " + repeating + ", Missing: " + missing);
}
// Output: findRepeatMissing([1,2,2,4]) → Repeating: 2, Missing: 3
// Bubble Sort: repeatedly compare adjacent elements and swap if out of order.
// After each full pass, the LARGEST unsorted element "bubbles up" to its correct position.
// Time: O(n²) Space: O(1)
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) { // i = pass number (n-1 passes total)
boolean swapped = false; // optimization: stop early if no swaps
for (int j = 0; j < n - 1 - i; j++) { // j goes up to (n-1-i) — last i are sorted
if (arr[j] > arr[j + 1]) {
int temp = arr[j]; // swap adjacent elements
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
if (!swapped) break; // if no swap in this pass → already sorted, stop early
}
}
// DRY RUN for [5, 3, 8, 1, 2]:
// Pass 1 (i=0): compare all adjacent pairs
// [5,3] → swap → [3,5,8,1,2]
// [5,8] → ok
// [8,1] → swap → [3,5,1,8,2]
// [8,2] → swap → [3,5,1,2,8] ← 8 is in its final position ✅
// Pass 2 (i=1):
// [3,5] → ok
// [5,1] → swap → [3,1,5,2,8]
// [5,2] → swap → [3,1,2,5,8] ← 5 is in final position ✅
// Pass 3 (i=2):
// [3,1] → swap → [1,3,2,5,8]
// [3,2] → swap → [1,2,3,5,8] ← 3 in final position ✅
// Pass 4 (i=3): no swaps → done!
// Result: [1,2,3,5,8] ✅
// Output: bubbleSort([5,3,8,1,2]) → [1,2,3,5,8]
// Selection Sort: find the MINIMUM element in unsorted part, swap it to front.
// Each pass selects the correct element for position i.
// Time: O(n²) Space: O(1)
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) { // i = position we're filling
int minIdx = i; // assume position i has minimum
for (int j = i + 1; j < n; j++) { // search rest of array for smaller
if (arr[j] < arr[minIdx]) {
minIdx = j; // found a new minimum
}
}
// swap minimum to position i
int temp = arr[minIdx];
arr[minIdx] = arr[i];
arr[i] = temp;
}
}
// DRY RUN for [64, 25, 12, 22, 11]:
// i=0: search [25,12,22,11] → min=11 at idx=4 → swap arr[0]↔arr[4]
// → [11, 25, 12, 22, 64] ← 11 placed ✅
// i=1: search [12,22,64] → min=12 at idx=2 → swap arr[1]↔arr[2]
// → [11, 12, 25, 22, 64] ← 12 placed ✅
// i=2: search [22,64] → min=22 at idx=3 → swap arr[2]↔arr[3]
// → [11, 12, 22, 25, 64] ← 22 placed ✅
// i=3: search [64] → min=25 at idx=3 → no swap needed
// → [11, 12, 22, 25, 64] ✅
// Result: [11,12,22,25,64] ✅
// Insertion Sort: like sorting playing cards in hand.
// Pick one card at a time and INSERT it into the correct position in the sorted part.
// Time: O(n²) worst, O(n) best (already sorted) Space: O(1)
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) { // start from index 1 (first element is "sorted")
int key = arr[i]; // pick this element to insert
int j = i - 1; // j points to end of sorted portion
// shift all sorted elements greater than key one position right
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j]; // move element right
j--; // move left
}
arr[j + 1] = key; // insert key at correct position
}
}
// DRY RUN for [12, 11, 13, 5, 6]:
// i=1: key=11, arr=[12,11,13,5,6]
// j=0: arr[0]=12 > 11 → shift: [12,12,13,5,6], j=-1
// place key: arr[0]=11 → [11,12,13,5,6] ✅
// i=2: key=13
// j=1: arr[1]=12 < 13 → stop
// place key: arr[2]=13 → [11,12,13,5,6] (no change)
// i=3: key=5
// j=2: 13>5 shift → [11,12,13,13,6]
// j=1: 12>5 shift → [11,12,12,13,6]
// j=0: 11>5 shift → [11,11,12,13,6]
// j=-1: stop. arr[0]=5 → [5,11,12,13,6] ✅
// i=4: key=6
// j=3: 13>6 shift, j=2: 12>6 shift, j=1: 11>6 shift, j=0: 5<6 stop
// arr[1]=6 → [5,6,11,12,13] ✅
// Result: [5,6,11,12,13] ✅
// Check EVERY element one by one until target found.
// Works on unsorted AND sorted arrays.
// Time: O(n) Space: O(1)
public static int linearSearch(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) return i; // found! return index
}
return -1; // not found
}
// DRY RUN for arr=[10,25,7,43,1], target=43:
// i=0: arr[0]=10 ≠ 43
// i=1: arr[1]=25 ≠ 43
// i=2: arr[2]=7 ≠ 43
// i=3: arr[3]=43 = 43 → return 3 ✅
// Output:
// linearSearch([10,25,7,43,1], 43) → 3 (found at index 3)
// linearSearch([10,25,7,43,1], 99) → -1 (not found)
// Binary Search: ONLY works on SORTED arrays.
// Each step cuts the search space in HALF → much faster than linear!
// Time: O(log n) Space: O(1)
public static int binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // safe mid: avoids integer overflow
if (arr[mid] == target) return mid; // found at mid!
else if (arr[mid] < target) left = mid + 1; // target is in RIGHT half
else right = mid - 1; // target is in LEFT half
}
return -1; // not found
}
// WHY left + (right-left)/2 instead of (left+right)/2?
// → Prevents integer overflow when left+right exceeds Integer.MAX_VALUE
// DRY RUN for arr=[1,3,5,7,9,11,13,15], target=7:
// left=0, right=7, mid=3, arr[3]=7 == 7 → return 3 ✅
// DRY RUN for target=11:
// left=0,right=7,mid=3: arr[3]=7 < 11 → left=4
// left=4,right=7,mid=5: arr[5]=11 == 11 → return 5 ✅
// Output:
// binarySearch([1,3,5,7,9,11,13], 7) → 3
// binarySearch([1,3,5,7,9,11,13], 99) → -1
// Sorted array may have duplicates — find FIRST (leftmost) index of target
// [1,2,2,2,3,4] target=2 → first occurrence at index 1
public static int firstOccurrence(int[] arr, int target) {
int left = 0, right = arr.length - 1;
int result = -1; // store answer, start with "not found"
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
result = mid; // found a match — save it
right = mid - 1; // but keep searching LEFT for earlier occurrence
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
// DRY RUN for [1,2,2,2,3,4], target=2:
// left=0,right=5,mid=2: arr[2]=2==2 → result=2, right=1
// left=0,right=1,mid=0: arr[0]=1<2 → left=1
// left=1,right=1,mid=1: arr[1]=2==2 → result=1, right=0
// left=1 > right=0 → stop
// Result: 1 ✅ (first 2 is at index 1)
// Find LAST (rightmost) index of target
// [1,2,2,2,3,4] target=2 → last occurrence at index 3
public static int lastOccurrence(int[] arr, int target) {
int left = 0, right = arr.length - 1;
int result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
result = mid; // found — save it
left = mid + 1; // keep searching RIGHT for later occurrence
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
// DRY RUN for [1,2,2,2,3,4], target=2:
// mid=2: arr[2]=2==2 → result=2, left=3
// mid=4: arr[4]=3>2 → right=3
// mid=3: arr[3]=2==2 → result=3, left=4
// left=4>right=3 → stop
// Result: 3 ✅ (last 2 is at index 3)
// HashSet stores UNIQUE elements only — duplicates are automatically ignored
// [1,2,2,3,3,3,4] → {1,2,3,4}
public static void removeDupsHashSet(int[] arr) {
Set set = new LinkedHashSet<>(); // LinkedHashSet preserves insertion order
for (int n : arr) {
set.add(n); // if n already exists, add() does nothing (no error)
}
System.out.println("After removing duplicates: " + set);
}
// ITERATION for [1,2,2,3,3,3,4]:
// add(1) → set={1}
// add(2) → set={1,2}
// add(2) → already exists, ignored!
// add(3) → set={1,2,3}
// add(3) → ignored! add(3) → ignored!
// add(4) → set={1,2,3,4} ✅
// Converting back to array:
public static int[] toDedupedArray(int[] arr) {
return Arrays.stream(arr).distinct().toArray(); // Java 8 stream
}
// Output:
// removeDupsHashSet([1,2,2,3,3,3,4]) → [1,2,3,4]
// Use Set's property: add() returns false if element already present public static SetfindDupsSet(int[] arr) { Set seen = new HashSet<>(); Set duplicates = new LinkedHashSet<>(); for (int n : arr) { if (!seen.add(n)) { // add() returns false = already in set = DUPLICATE! duplicates.add(n); } } return duplicates; } // ITERATION for [1,3,4,2,2,3]: // 1 → seen={1}, dup={} // 3 → seen={1,3}, dup={} // 4 → seen={1,3,4}, dup={} // 2 → seen={1,3,4,2}, dup={} // 2 → add returns false! dup={2} // 3 → add returns false! dup={2,3} // Result: {2,3} ✅
// Count how many times each element appears
// [a,b,a,c,b,a] → a→3, b→2, c→1
public static void countFreqMap(String[] arr) {
Map map = new LinkedHashMap<>();
for (String s : arr) {
// getOrDefault: if key exists return its value, else return 0
map.put(s, map.getOrDefault(s, 0) + 1);
}
for (Map.Entry e : map.entrySet()) {
System.out.println(e.getKey() + " → " + e.getValue());
}
}
// ITERATION for ["a","b","a","c","b","a"]:
// "a" → map={a:1}
// "b" → map={a:1, b:1}
// "a" → map={a:2, b:1}
// "c" → map={a:2, b:1, c:1}
// "b" → map={a:2, b:2, c:1}
// "a" → map={a:3, b:2, c:1} ✅
// Count frequencies using HashMap, then scan original string for count=1
public static char firstNonRepMap(String s) {
Map map = new LinkedHashMap<>(); // LINKED preserves order!
// Pass 1: build frequency map
for (char c : s.toCharArray())
map.put(c, map.getOrDefault(c, 0) + 1);
// Pass 2: find first char with count=1
for (char c : s.toCharArray())
if (map.get(c) == 1) return c;
return '_'; // all characters repeat
}
// WHY LinkedHashMap? Insertion order matters — we want the FIRST unique char.
// Regular HashMap doesn't guarantee order.
// DRY RUN for "swiss":
// map: s→3, w→1, i→1
// Scan "swiss": s→3 skip, w→1 → return 'w' ✅
// Output:
// firstNonRepMap("swiss") → w
// firstNonRepMap("aabbcdd") → c
// int[] cannot use Collections directly — must use Integer[] or Arrays.asList
// For String/Integer arrays:
String[] arr = {"Java", "Python", "C++"};
// METHOD 1: Arrays.asList (Fast, but fixed-size — cannot add/remove)
List list1 = Arrays.asList(arr);
// METHOD 2: new ArrayList (Dynamic — can add/remove freely)
List list2 = new ArrayList<>(Arrays.asList(arr));
list2.add("Go"); // ✅ works
list2.remove("C++"); // ✅ works
// METHOD 3: Stream (Java 8 — for int[] specifically)
int[] nums = {1, 2, 3, 4, 5};
List intList = Arrays.stream(nums)
.boxed() // int → Integer (autoboxing)
.collect(Collectors.toList());
// Output:
// list2 → [Java, Python, Go] (after add and remove)
// Converting List to Set automatically removes duplicates! Listlist = Arrays.asList(1, 2, 2, 3, 3, 3, 4); // HashSet — no order guaranteed Set hashSet = new HashSet<>(list); // LinkedHashSet — preserves insertion order (usually preferred) Set linkedSet = new LinkedHashSet<>(list); // TreeSet — automatically sorts elements Set treeSet = new TreeSet<>(list); System.out.println("Original: " + list); // [1,2,2,3,3,3,4] System.out.println("LinkedSet: " + linkedSet); // [1,2,3,4] System.out.println("TreeSet: " + treeSet); // [1,2,3,4] (sorted)
Listnums = Arrays.asList(5, 2, 8, 1, 9, 3); // METHOD 1: Collections.sort — ascending (natural order) Collections.sort(nums); System.out.println("Ascending: " + nums); // [1,2,3,5,8,9] // METHOD 2: Collections.sort with Comparator — descending Collections.sort(nums, Collections.reverseOrder()); System.out.println("Descending: " + nums); // [9,8,5,3,2,1] // METHOD 3: List.sort with lambda (Java 8) nums.sort((a, b) -> a - b); // ascending nums.sort((a, b) -> b - a); // descending // Sorting list of Strings: List words = Arrays.asList("banana","apple","cherry"); Collections.sort(words); System.out.println(words); // [apple, banana, cherry] // Sorting by length: words.sort(Comparator.comparingInt(String::length)); System.out.println(words); // [apple, banana, cherry]
Mapmap = new LinkedHashMap<>(); map.put("Alice", 90); map.put("Bob", 85); map.put("Charlie", 92); // METHOD 1: entrySet() — access both key AND value (Most common) for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + " → " + entry.getValue()); } // METHOD 2: keySet() — access only keys, then get value for (String key : map.keySet()) { System.out.println(key + " scored " + map.get(key)); } // METHOD 3: values() — only values, no keys for (int value : map.values()) { System.out.println(value); } // METHOD 4: forEach with lambda (Java 8 — cleanest) map.forEach((key, value) -> System.out.println(key + ": " + value)); // Output (METHOD 1): // Alice → 90 // Bob → 85 // Charlie → 92
// Recursion idea: print N AFTER calling for N-1
// We go DEEP first (base case), then print on the way BACK UP
public static void print1toN(int n) {
if (n == 0) return; // BASE CASE: stop recursion when n=0
print1toN(n - 1); // recursive call with n-1 FIRST
System.out.print(n + " "); // print AFTER returning from recursion
}
// WHY print AFTER the call? Because we want 1 printed before 2, 3, etc.
// The call goes deep (n=3 → 2 → 1 → 0), then prints while returning (1, 2, 3)
// CALL STACK for n=3:
// print1toN(3) → calls print1toN(2)
// print1toN(2) → calls print1toN(1)
// print1toN(1) → calls print1toN(0)
// print1toN(0) → returns (base case)
// ← prints 1
// ← prints 2
// ← prints 3
// Output: 1 2 3 ✅
// Print BEFORE the recursive call → print on the way DOWN
public static void printNto1(int n) {
if (n == 0) return; // BASE CASE
System.out.print(n + " "); // print FIRST (before calling)
printNto1(n - 1); // then recurse with n-1
}
// CALL STACK for n=3:
// printNto1(3): print 3 → calls printNto1(2)
// printNto1(2): print 2 → calls printNto1(1)
// printNto1(1): print 1 → calls printNto1(0)
// printNto1(0): return (base case)
// Output: 3 2 1 ✅
// KEY DIFFERENCE from RE1:
// RE1 (1 to N): print AFTER recursive call → numbers print smallest first
// RE2 (N to 1): print BEFORE recursive call → numbers print largest first
// n! = n × (n-1)! with base case: 0! = 1 and 1! = 1
public static long factRec(int n) {
if (n == 0 || n == 1) return 1; // BASE CASE: stop here
return n * factRec(n - 1); // RECURSIVE CASE: n × factorial(n-1)
}
// CALL STACK for n=5:
// factRec(5) = 5 × factRec(4)
// 5 × 4 × factRec(3)
// 5 × 4 × 3 × factRec(2)
// 5 × 4 × 3 × 2 × factRec(1)
// 5 × 4 × 3 × 2 × 1 ← base case returns 1
// UNWINDING the stack:
// factRec(1) = 1
// factRec(2) = 2×1 = 2
// factRec(3) = 3×2 = 6
// factRec(4) = 4×6 = 24
// factRec(5) = 5×24 = 120 ✅
// Output: factRec(5) → 120 factRec(0) → 1
// fib(n) = fib(n-1) + fib(n-2) base cases: fib(0)=0, fib(1)=1
public static int fibRec(int n) {
if (n == 0) return 0; // BASE CASE 1
if (n == 1) return 1; // BASE CASE 2
return fibRec(n - 1) + fibRec(n - 2); // RECURSIVE CASE
}
// CALL TREE for fibRec(4):
// fib(4)
// / \
// fib(3) fib(2)
// / \ / \
// fib(2) fib(1) fib(1) fib(0)
// / \
// fib(1) fib(0)
// = (1+0) + 1 + 1 + 0 = ... = 3 ✅
// ⚠️ Recursive fibonacci is O(2^n) — very slow for large n!
// Use iterative version for n > 30.
// Output:
// fibRec(0)→0 fibRec(1)→1 fibRec(5)→5 fibRec(7)→13
// Compare characters from both ends, move inward recursively
public static boolean isPalinRec(String s, int left, int right) {
if (left >= right) return true; // BASE CASE: pointers met or crossed
if (s.charAt(left) != s.charAt(right)) return false; // mismatch → not palindrome
return isPalinRec(s, left + 1, right - 1); // move both pointers inward
}
// Helper method to call easily:
public static boolean isPalindrome(String s) {
return isPalinRec(s.toLowerCase(), 0, s.length() - 1);
}
// CALL STACK for "racecar":
// isPalinRec("racecar", 0, 6): r==r ✅ → call(1,5)
// call(1,5): a==a ✅ → call(2,4)
// call(2,4): c==c ✅ → call(3,3)
// call(3,3): left(3) >= right(3) → return true (BASE CASE)
// ← returns true
// ← returns true
// ← returns true ✅
// Output:
// isPalindrome("racecar") → true isPalindrome("hello") → false
// For n=5, output:
// *
// * *
// * * *
// * * * *
// * * * * *
// Logic: Row i → print i stars (i goes from 1 to n)
public static void rightTriangle(int n) {
for (int i = 1; i <= n; i++) { // outer loop: each row
for (int j = 1; j <= i; j++) { // inner loop: print i stars
System.out.print("* ");
}
System.out.println(); // move to next line after each row
}
}
// ITERATION TABLE for n=4:
// Row i=1: j runs 1 to 1 → print 1 star → *
// Row i=2: j runs 1 to 2 → print 2 stars → * *
// Row i=3: j runs 1 to 3 → print 3 stars → * * *
// Row i=4: j runs 1 to 4 → print 4 stars → * * * *
// For n=5, output:
// * * * * *
// * * * *
// * * *
// * *
// *
// Logic: Row i → print (n - i + 1) stars
public static void reverseTriangle(int n) {
for (int i = n; i >= 1; i--) { // outer loop: starts from n, goes down to 1
for (int j = 1; j <= i; j++) { // inner loop: print i stars
System.out.print("* ");
}
System.out.println();
}
}
// ITERATION TABLE for n=4:
// Row i=4: print 4 stars → * * * *
// Row i=3: print 3 stars → * * *
// Row i=2: print 2 stars → * *
// Row i=1: print 1 star → *
// For n=4, output:
// *
// * *
// * * *
// * * * *
// (spaces before stars to center them)
// Logic: Each row i has (n-i) spaces then i stars
public static void pyramid(int n) {
for (int i = 1; i <= n; i++) {
// print leading spaces: (n-i) spaces for row i
for (int j = 1; j <= n - i; j++) {
System.out.print(" ");
}
// print stars: i stars for row i
for (int j = 1; j <= i; j++) {
System.out.print("* ");
}
System.out.println();
}
}
// ITERATION TABLE for n=4:
// i=1: 3 spaces, 1 star → *
// i=2: 2 spaces, 2 stars → * *
// i=3: 1 space, 3 stars → * * *
// i=4: 0 spaces, 4 stars → * * * *
// For n=4, output:
// 1
// 1 2
// 1 2 3
// 1 2 3 4
// Logic: In row i, print numbers 1 through i
public static void numberPyramid(int n) {
for (int i = 1; i <= n; i++) { // outer loop: each row
for (int j = 1; j <= i; j++) { // inner loop: print 1 to i
System.out.print(j + " ");
}
System.out.println();
}
}
// ITERATION TABLE for n=4:
// i=1: j=1 → 1
// i=2: j=1,2 → 1 2
// i=3: j=1,2,3 → 1 2 3
// i=4: j=1,2,3,4 → 1 2 3 4
// BONUS — Floyd's Triangle (continuous numbers):
// 1
// 2 3
// 4 5 6
public static void floydsTriangle(int n) {
int num = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= i; j++) System.out.print(num++ + " ");
System.out.println();
}
}
| Method | Purpose | Has Body? | Idempotent? | Example |
|---|---|---|---|---|
| GET | Retrieve data | No | ✓ Yes | Get user by ID |
| POST | Create new resource | Yes | ✗ No | Create new user |
| PUT | Replace entire resource | Yes | ✓ Yes | Update all user details |
| PATCH | Update specific fields | Yes | ✓ Yes | Update only email |
| DELETE | Delete a resource | No | ✓ Yes | Delete user by ID |
| Type | How it works | Security |
|---|---|---|
| Basic Auth | Username + password encoded Base64 in every request | Least secure |
| Bearer Token | Login once → get token → pass in Authorization header | Common in REST |
| OAuth 2.0 | Token issued by auth server for third-party access | Most robust |
| Scenario | Expected Code | What to check |
|---|---|---|
| Missing required field | 400 | Error message — field name + reason |
| No token / expired token | 401 | “Unauthorized” message |
| Valid token, wrong role | 403 | “Forbidden” message |
| Wrong ID in URL | 404 | “Not Found” message |
| Invalid data type | 400 | Validation error message |
| Server crash | 500 | Error logged, not exposed to user |
| Question | Answer |
|---|---|
| What is Idempotent? | Same request multiple times = same result. GET, PUT, DELETE = Yes. POST = No. |
| 401 vs 403? | 401 = Not authenticated. 403 = Authenticated but no permission. |
| PUT vs PATCH? | PUT replaces entire resource. PATCH updates specific fields only. |
| Auth vs Authorization? | Authentication = Who are you? Authorization = What can you do? |
| Serialization? | Java Object → JSON before sending request. |
| Deserialization? | JSON → Java Object after receiving response. |
| Why POJO? | Readability + maintainability. Avoids hardcoded JSON strings. |
| Why RequestSpecification? | Reusability — avoid repeating base URI and headers in every test. |
| How to log in Rest Assured? | given().log().all() and then().log().all() |
| Dynamic values? | response.jsonPath().getString("field") — extract and reuse in next request. |
git stash — Save current changesgit stash pop — Restore the most recent stashgit stash list — See all stashed changesgit stash drop — Delete a stashgit log — Full commit history with author, date, and messagegit log --oneline — Compact one-line format — easiest to readgit log --oneline --graph — Visual branch history as ASCII treegit log --author="Akshay" — Commits by a specific persongit log filename.java — History of a specific filegit blame filename.java — Shows who wrote each line and whengit cherry-pick commitIDBefore learning DevOps tools, you MUST be strong in these fundamentals. Every DevOps engineer uses these daily.
Every single DevOps pipeline starts with a git push. You must know Git inside out.
Docker is the #1 skill interviewers ask about. Master it completely before moving to Kubernetes.
This is the HEART of DevOps. Automate the entire path from code to production.
90% of companies use AWS. Learn AWS first. Azure/GCP after.
Stop clicking in AWS console. Manage everything as code. This separates good DevOps engineers from great ones.
K8s is the most in-demand skill in DevOps right now. Takes time to master but huge salary jump.
Production is live. Now you need to KNOW what is happening at all times and keep it secure.
At this level you design systems, lead teams, and drive architecture decisions.
DevOps = Development + Operations working together as one team.
Old way: Dev team wrote code → threw it over to Ops team → Ops deployed it → lots of finger-pointing when things broke.
DevOps way: Both teams work together from day one. Code is written, tested, and deployed automatically and continuously.
🍽️ Restaurant analogy: Earlier chefs (Dev) cooked food and just handed it to waiters (Ops). With DevOps, chefs and waiters work in the same kitchen — same goal, serve customer fast!
Agile = HOW developers plan and build software (Scrum, Sprints, User Stories)
DevOps = HOW software gets built, tested, and deployed automatically end-to-end
✅ Agile fixes the development process. DevOps fixes the delivery process. They work together.
Think of it this way: Agile = how the car is designed. DevOps = how the car gets manufactured and delivered.
Almost all servers, Docker containers, and Kubernetes nodes run on Linux. Whether it is AWS, GCP, or Azure — everything runs on Linux underneath. A DevOps engineer MUST be comfortable with Linux commands.
These are the most basic Linux commands every DevOps engineer uses dozens of times a day.
lsls -lapwdcd /var/logmkdir myapprm old-backup.logrm -rf /tmp/old-release/cp nginx.conf nginx.conf.bakmv app.jar /opt/myapp/touch /var/log/myapp.logcat /etc/nginx/nginx.confless /var/log/app.logtail -50 /var/log/app.logtail -f /var/log/app.log
Understanding permissions is critical — wrong permissions cause deployment failures and security vulnerabilities.
Permission math: r=4, w=2, x=1 → rwx=7, rw-=6, r-x=5, r--=4
chmod +x deploy.shchmod 700 secret-script.shchmod 644 index.htmlchmod 755 /opt/myappchown ubuntu:ubuntu /opt/myapp/app.jarchown -R ubuntu:www-data /var/www/htmlls -la /opt/myapp/Every DevOps engineer needs to manage running processes and system services confidently.
ps auxtopkill -9 1234pkill -f myappsystemctl start nginxsystemctl stop nginxsystemctl restart nginxsystemctl enable nginxsystemctl status nginxjournalctl -u nginx -fNetworking commands help you debug connectivity issues, test endpoints, and manage server access.
ping google.comcurl http://localhost:8080/healthcurl -I https://myapp.comcurl -X POST http://api/users -H "Content-Type: application/json" -d '{"name":"test"}'wget https://releases.example.com/myapp-v2.jarnetstat -tulpnss -tulpn | grep 8080ssh ubuntu@192.168.1.10scp app.jar ubuntu@server:/opt/myapp/nslookup myapp.company.comgrep = Global Regular Expression Print. The #1 tool for log analysis and debugging in production.
grep 'ERROR' /var/log/app.loggrep -i 'error' app.loggrep -n 'NullPointerException' app.loggrep -c 'ERROR' app.loggrep -v 'DEBUG' app.loggrep -r 'DB_PASSWORD' /opt/myapp/ps aux | grep nginxtail -f app.log | grep 'ERROR'grep -E 'ERROR|WARN|FATAL' app.loggrep -C 3 'OutOfMemoryError' app.loggrep -rl 'Connection refused' /var/log/
awk processes text column by column. Think of it as Excel formulas for the command line.
Key variables: $1=col1, $2=col2, $NF=last col, NR=row number, FS=field separator
ps aux | awk '{print $1, $2}'df -h | awk '{print $1, $5}'awk -F: '{print $1}' /etc/passwdawk -F, '{print $2}' servers.csvps aux | awk '$3 > 5.0 {print $1,$2,$3,$11}'awk 'NR==42' config.propertiesdu -sh /var/log/* | awk '{sum += $1} END {print "Total: " sum "MB"}'docker stats --no-stream | awk 'NR>1 {print $1, $3}'grep 'ERROR' app.log | awk '{print $1, $2}'awk '{print $9}' access.log | sort | uniq -c | sort -rnsed edits files non-interactively — perfect for automated config changes in CI/CD pipelines.
sed -i 's/DB_HOST=localhost/DB_HOST=prod-db.company.com/' .envsed -i 's/version: 1.0.0/version: 2.1.0/g' config.yamlsed -n '10,20p' app.logsed '/^$/d' config.txtsed -i '1i # Auto-generated — do not edit manually' config.yamlThese commands are the first thing you run when a server is slow or running out of resources.
df -hdu -sh /var/log/* | sort -rh | head -10free -hvmstat 2 5uptimeuname -ahistory | grep dockerGit is a distributed version control system. It tracks every change to your code — who changed what, when, and why.
📝 Simple analogy: Git is like Google Docs revision history for your code. Every save (commit) is remembered forever. You can see all history, compare versions, and go back to any point in time.
GitHub / GitLab / Bitbucket = cloud hosting for your Git repositories so teams can collaborate.
These are the commands you use every single day. Master these first.
git initgit clone https://github.com/company/backend.gitgit statusgit add .git add src/UserService.javagit commit -m "fix: resolve login timeout for inactive users"git push origin maingit pull origin maingit log --onelineBranching is how teams work on features simultaneously without breaking each other's code.
git branchgit checkout -b feature/user-authenticationgit checkout developgit merge feature/user-authenticationgit branch -d feature/user-authenticationgit branch -agit push -u origin feature/user-authentication⚠️ Know which commands are SAFE (revert) vs DANGEROUS (reset --hard) before using them on shared branches.
git revert abc123git reset --soft HEAD~1git reset --hard HEAD~1git stashgit stash popgit checkout -- src/UserService.javagit diff main..feature/my-featureCI = Continuous Integration
Every developer push automatically triggers: code checkout → build → unit tests → report. Developers get instant feedback on broken code.
CD = Continuous Delivery / Deployment
After CI passes, code is automatically packaged and deployed to staging or production.
🏭 Factory analogy: CI/CD is an automated assembly line. Raw materials (code) enter one end → automatically tested at every stage → finished product (deployed app) comes out the other end. No manual steps!
Docker packages an application and ALL its dependencies (libraries, configs, runtime) into a single portable unit called a container.
🔥 The problem it solves: "It works on my machine!" — Docker eliminates this forever. The container runs identically on any machine that has Docker installed.
📦 Shipping container analogy: Before containers, goods were packed differently for every ship and had to be repacked at each port. With standard shipping containers — pack once, ship anywhere. Docker does the same for software.
| Feature | Docker Container | Virtual Machine |
|---|---|---|
| Size | MBs | GBs |
| Startup Time | Seconds | Minutes |
| OS | Shares host OS kernel | Full OS inside |
| Performance | Near native | 10-20% overhead |
| Isolation | Process level | Full hardware level |
These commands are asked in every DevOps interview. Know each one with its real use case.
docker build -t myapp:1.0 .docker build -t myapp:prod -f Dockerfile.production .docker run -d -p 8080:8080 --name myapp myapp:1.0docker run -d -e DB_HOST=localhost -e DB_PASS=secret myapp:1.0docker psdocker ps -adocker logs myappdocker logs -f myappdocker exec -it myapp bashdocker exec myapp cat /app/config/application.propertiesdocker stop myappdocker rm myappdocker imagesdocker rmi myapp:0.9docker statsWithout volumes, ALL data is lost when a container is deleted. Always use volumes for databases and persistent data.
docker volume create postgres-datadocker run -d -v postgres-data:/var/lib/postgresql/data postgres:15docker run -d -v $(pwd)/src:/app/src myapp:devdocker volume lsdocker network create myapp-networkdocker run -d --network myapp-network --name postgres postgres:15docker network lsDocker Compose manages multiple containers together as one application stack.
Kubernetes (K8s) is a container orchestration platform. It automatically manages, scales, and heals Docker containers across a cluster of servers.
🚛 Fleet management analogy: Docker is one truck delivering goods. Kubernetes is the entire logistics company — managing thousands of trucks, deciding routes, replacing broken trucks automatically, and scaling the fleet up or down based on delivery demand.
Key features: Auto-scaling · Self-healing (restarts crashed containers) · Load balancing · Rolling updates with zero downtime · Service discovery · Secret management
Control Plane (Master) — The Brain:
Worker Nodes — The Muscle:
kubectl is your primary tool for working with Kubernetes. These commands cover 90% of daily DevOps work.
kubectl get pods -n productionkubectl get pods -o wide -n productionkubectl get all -n productionkubectl describe pod myapp-xyz-abc -n productionkubectl logs myapp-xyz-abc -n productionkubectl logs -f myapp-xyz-abc -n productionkubectl logs myapp-xyz-abc --previous -n productionkubectl exec -it myapp-xyz-abc -n production -- bashkubectl apply -f deployment.yamlkubectl delete -f deployment.yamlkubectl scale deployment myapp --replicas=5 -n productionkubectl set image deployment/myapp myapp=myrepo/myapp:v2.1 -n productionkubectl rollout status deployment/myapp -n productionkubectl rollout undo deployment/myapp -n productionkubectl rollout history deployment/myapp -n productionIaC means defining and managing your infrastructure (servers, networks, databases) using code files instead of manual clicks or commands.
Old way: SSH into server → run 15 manual commands → forget what you did → next server is slightly different → bugs and inconsistencies everywhere.
IaC way: Write a YAML or HCL file → run one command → infrastructure is created identically every single time. Version controlled, repeatable, reviewable.
| Feature | Ansible | Terraform |
|---|---|---|
| Category | Configuration Management | Infrastructure Provisioning |
| What it manages | Software on existing servers | Creates cloud resources |
| Language | YAML (Playbooks) | HCL (HashiCorp) |
| Agent on server? | No — uses SSH | No |
| Real example | Install Java, configure nginx | Create EC2, VPC, RDS on AWS |
✅ In practice: Terraform creates the cloud infrastructure first, then Ansible configures the servers.
Cloud computing means renting compute resources (servers, storage, networking) over the internet instead of owning physical hardware.
💡 Power grid analogy: You do not build your own power plant — you just plug in and pay for what you use. Cloud is the same for computing: rent as much as you need, pay only for usage, scale up or down instantly.
Top 3 Cloud Providers:
"You cannot fix what you cannot see."
Without monitoring, you find out about problems when customers complain. With monitoring, you know before customers do.
The 3 pillars of observability:
How it works end-to-end:
/metrics endpointReal scenario: Production error occurs. Open Kibana → search for the error ID → instantly see: which server, which user, full stack trace, all related events — in seconds instead of SSH-ing into 20 servers manually.
Interviewers ask scenario questions to see if you can think systematically under pressure. Always follow this structure:
A clear step-by-step path from zero to job-ready Automation Engineer. Click any phase to jump to that topic.
🎯 Scenario Question:
You are assigned to test a login page. What scenarios will you cover?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“While testing a login page, I first verify that the user can log in successfully with valid credentials. Then I test negative scenarios such as incorrect username or password, and empty fields, to confirm proper validation messages are shown. I also check boundary values on input lengths and verify the password field is always masked.”
🎯 Scenario Question:
How would you test a user registration page?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify that a user can register with valid details. I test duplicate email submission to ensure the system rejects it. I check mandatory field validation, email format, and password strength rules like minimum length.”
🎯 Scenario Question:
How would you test the forgot password functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify a reset link is sent to a registered email. I test with an unregistered email to confirm an appropriate message is shown. I also check the link expires after the defined time and the user can set a new password.”
🎯 Scenario Question:
How would you test logout functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify clicking logout ends the session and redirects to the login page. I press the browser back button to confirm the user cannot re-enter secured pages without logging in again.”
🎯 Scenario Question:
How would you test search functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify that valid keyword searches return accurate results. I test partial keywords and special characters to confirm the system handles them without errors. I also check that a clear message appears when no results are found.”
🎯 Scenario Question:
What UI elements do you check while testing?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify that all components like buttons, input fields, and images are properly aligned. I check fonts and colors match the design. I also test responsiveness across mobile, tablet, and desktop screen sizes.”
🎯 Scenario Question:
How would you test file upload functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify a supported file type uploads and a confirmation message is displayed. I test invalid formats and files above the size limit to ensure the system rejects them with a clear error message.”
🎯 Scenario Question:
How would you test a payment gateway?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify the full payment flow with valid card details. I test invalid CVV, expired card, and insufficient balance. I also simulate a network interruption to verify the system does not double-charge and handles failure gracefully.”
🎯 Scenario Question:
How would you test session timeout functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I stay idle beyond the configured timeout period after logging in. I verify the system automatically logs out the user and redirects to the login page with no session data remaining.”
🎯 Scenario Question:
How do you test form validations?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I leave mandatory fields empty and submit the form, verifying proper validation messages appear next to each field. I also test email and phone number format validation.”
🎯 Scenario Question:
How would you test a dropdown list?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify all dropdown options display in the correct order. I select each option and confirm the system responds as expected. For dependent dropdowns, I check selecting one updates the other correctly.”
🎯 Scenario Question:
How do you test pagination?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify Next and Previous buttons navigate correctly. I check the correct number of records appears per page, and boundary buttons are disabled correctly on the first and last pages.”
🎯 Scenario Question:
How do you validate error messages?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify error messages appear next to the relevant field in clear, simple language. I also check that no system-level technical details are exposed to the user.”
🎯 Scenario Question:
How would you test for broken links?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click every link in the application and verify it redirects correctly. I check for 404 errors and confirm external links open in a new tab.”
🎯 Scenario Question:
How do you test browser compatibility?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I open the application in Chrome, Firefox, Edge, and Safari. I verify the UI looks consistent and all features work correctly across each browser.”
🎯 Scenario Question:
How would you test mandatory fields?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I submit the form with all mandatory fields empty and verify the system shows appropriate validation messages. I confirm mandatory fields are clearly marked.”
🎯 Scenario Question:
How would you test a navigation menu?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click each menu item and confirm it loads the correct page. I verify the active state is highlighted and no links are broken.”
🎯 Scenario Question:
How would you verify that data is saved correctly?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“After submitting a form, I navigate to the data display area and verify all entered information is saved accurately and matches the input.”
🎯 Scenario Question:
How do you test duplicate data prevention?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I create a record then attempt to create another with the same unique identifier. I verify the system prevents it and shows a meaningful error message.”
🎯 Scenario Question:
How do you test search filters?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I apply each filter one by one and verify results match the criteria. I also combine multiple filters and confirm results narrow down correctly.”
🎯 Scenario Question:
How do you test sorting functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click each sortable column header and verify data sorts ascending. I click again to confirm descending order, and check that the data sequence is accurate.”
🎯 Scenario Question:
How would you test a date picker?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I select a valid date from the picker and verify it is accepted. I test date restrictions like disabling past dates. I also type a date manually and confirm format validation works.”
🎯 Scenario Question:
How do you test notifications?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I perform an action that should trigger a notification and verify the correct message appears. I dismiss it and confirm it does not reappear unexpectedly.”
🎯 Scenario Question:
How do you test email notifications?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I trigger an event that sends an email notification. I verify the email is received, check the subject and body content, and confirm any links work correctly.”
🎯 Scenario Question:
How would you test editing functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I open an existing record, modify the data, and save changes. I then verify the updated information is correctly displayed in the application.”
🎯 Scenario Question:
How do you test delete functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click delete and verify a confirmation dialog appears. I confirm the deletion and check the record is removed. I also click cancel and confirm the record is retained.”
🎯 Scenario Question:
How do you test a reset button?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click the reset button and verify all input fields are cleared. If default values are expected, I confirm they are restored.”
🎯 Scenario Question:
How do you test image upload functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I upload supported image formats and verify they are accepted with a preview. I test unsupported formats and oversized files to confirm the system rejects them with a proper message.”
🎯 Scenario Question:
How do you test data export functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I trigger the export and verify the file downloads in the expected format. I open the file and compare data against the system to confirm accuracy.”
🎯 Scenario Question:
How do you test a print feature?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I open the print preview and verify all data is visible and properly formatted. I check for any cut-off content or layout issues.”
🎯 Scenario Question:
How do you test accessibility?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I navigate the application using only the keyboard to verify all features are accessible. I check for alt text on images and confirm color contrast meets accessibility standards.”
🎯 Scenario Question:
How do you test input boundaries?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I test minimum and maximum allowed values in each input field to confirm they are accepted. I also test values just outside the boundary to ensure the system rejects them properly.”
🎯 Scenario Question:
How do you validate URLs?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I test URL input fields with valid and invalid formats. I also check that sensitive data such as passwords or tokens are not exposed in the URL.”
🎯 Scenario Question:
How do you test multiple users accessing the system simultaneously?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I log in with multiple accounts at the same time and perform actions simultaneously. I verify each user sees only their own data and no conflicts or race conditions occur.”
🎯 Scenario Question:
What happens if a user refreshes the page during form submission?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I refresh the page mid-submission and verify duplicate records are not created. I also check whether form data is preserved and the action is confirmed only once.”
🎯 Scenario Question:
How do you test error recovery?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I simulate errors such as network timeout or invalid input and verify the system shows a clear message without crashing. I confirm the user can retry successfully.”
🎯 Scenario Question:
How do you test a cancel button?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I click cancel and verify the user is redirected without saving changes. I confirm no partial data is stored.”
🎯 Scenario Question:
How do you test user roles and permissions?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I log in with different user roles and verify each has access only to permitted features. I also try accessing restricted pages via direct URL to confirm access is blocked.”
🎯 Scenario Question:
How do you test password change functionality?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I verify the old password is required before setting a new one. I test rule enforcement. After changing, I log in with the new password to confirm it works.”
🎯 Scenario Question:
How do you test data consistency across modules?
📋 Key Testing Points:
🎤 Ready-to-Say Interview Answer:
“I update data in one module and navigate to all related modules to verify the same data is reflected correctly everywhere.”
Alert alert = driver.switchTo().alert();
alert.accept();Select dropdown = new Select(driver.findElement(By.id("country")));
dropdown.selectByVisibleText("India");driver.switchTo().frame("loginFrame");
driver.findElement(By.id("username")).sendKeys("test");
driver.switchTo().defaultContent();Set windows = driver.getWindowHandles();
driver.switchTo().window(windowID); WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("login")));driver.findElement(By.xpath("//button[contains(@id,'login')]")).click();File src = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);driver.findElement(By.id("upload")).sendKeys("C:\\file.pdf");if(!checkbox.isSelected()){
checkbox.click();
}driver.findElement(By.id("username")).sendKeys("test");
driver.findElement(By.id("password")).sendKeys("123");
driver.findElement(By.id("login")).click();List rows = driver.findElements(By.xpath("//table/tr")); driver.findElement(By.id("next")).click();Actions action = new Actions(driver);
action.moveToElement(menu).perform();action.dragAndDrop(source, target).perform();JavascriptExecutor js = (JavascriptExecutor) driver;
js.executeScript("window.scrollBy(0,500)");File file = new File("C:\\Downloads\\invoice.pdf");
if(file.exists()){
System.out.println("Download successful");
}driver.navigate().refresh();
driver.findElement(By.id("login")).click();Actions action = new Actions(driver);
action.moveToElement(menu).perform();Set cookies = driver.manage().getCookies(); driver.get("https://user:password@website.com");wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("product")));driver.navigate().back();
driver.navigate().forward();driver.findElement(By.xpath("//td[text()='25']")).click();List links = driver.findElements(By.tagName("a")); String title = driver.getTitle();driver.getCurrentUrl();js.executeScript("window.scrollTo(0, document.body.scrollHeight)");options.addArguments("--headless");// Configure parallel="methods" in testng.xml// Read data from Excel and loop through test casesURL url = new URL("http://localhost:4444/wd/hub");
RemoteWebDriver driver = new RemoteWebDriver(url, new ChromeOptions());@Test(retryAnalyzer = RetryAnalyzer.class)driver.findElement(By.id("upload")).sendKeys("C:\\file.pdf");List prices = driver.findElements(By.xpath("//table//td[2]")); driver.switchTo().frame("frame1");
driver.switchTo().frame("frame2");LogEntries logs = driver.manage().logs().get("browser");try{
driver.switchTo().alert().accept();
}catch(Exception e){}driver.findElement(By.id("next")).click();driver.findElement(By.id("logout")).click();// CAPTCHA disabled in test environment by configurationdriver.get("verification_link");driver.navigate().refresh();Thread.sleep(600000);// POM + TestNG + Utility classespublic void login(){
username.sendKeys("test");
}wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("state")));File file = new File("report.pdf");List rows = driver.findElements(By.xpath("//table/tr")); options.addArguments("--disable-notifications");wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("data")));🎯 Scenario:
You are testing GET /users/{id}. What scenarios will you cover?
📋 Key Points:
🎤 Answer:
“I test valid user ID first and verify 200 with correct JSON fields. Then invalid ID for 404 with error message. Boundary values like 0 and negative IDs. I validate the response schema checking every field type. I also check response time is within SLA.”
❓ Counter Q: How do you validate the response schema?
✅ Counter A: I use Postman Test Scripts with pm.response.json() to check each field. In RestAssured I use JsonSchemaValidator.matchesJsonSchemaInClasspath() to validate against a JSON schema file.
🎯 Scenario:
You need to test POST /orders that creates a new order. What do you test?
📋 Key Points:
🎤 Answer:
“I test with valid payload and verify 201 Created with new order ID in response. Then missing mandatory fields like user_id to confirm 400 with specific field errors. Invalid data types such as a string in quantity field. I also check duplicate order handling.”
❓ Counter Q: What is the difference between 400 and 422?
✅ Counter A: 400 Bad Request means the server cannot understand the request syntax. 422 Unprocessable Entity means syntax is correct but values are semantically invalid — for example a valid JSON body with an invalid date range. 422 is more specific.
🎯 Scenario:
An API requires Bearer token. How do you test authentication scenarios?
📋 Key Points:
🎤 Answer:
“I test with valid token first. Then no Authorization header — verify 401. Expired token — verify 401 with message. Invalid token string. Most importantly I test IDOR — use User A token to access User B resource URL — should return 403 Forbidden.”
❓ Counter Q: What is the difference between 401 and 403?
✅ Counter A: 401 means not authenticated — no or invalid token. 403 means authenticated but no permission. Logged in as regular user trying admin endpoint — that is 403, not 401.
🎯 Scenario:
You need to test DELETE /products/{id}. What scenarios do you cover?
📋 Key Points:
🎤 Answer:
“I delete a valid product and verify 204 No Content. Then GET the deleted product and verify 404. Try to delete same product again and check 404 or 409. Test deleting non-existent ID. Test that unauthenticated users get 401.”
❓ Counter Q: Why test GET after DELETE?
✅ Counter A: Because DELETE endpoint may return success even if backend did not actually delete. The only way to confirm real deletion is GET immediately after and verify 404. This catches silent failures in the delete implementation.
🎯 Scenario:
How do you systematically test all expected status codes for your API?
📋 Key Points:
🎤 Answer:
“I create a test matrix mapping each endpoint and scenario to its expected code. For example POST /orders valid=201, missing field=400, unauthorized=401. I write a test for each cell. I also verify error response body always contains a message field explaining the error.”
❓ Counter Q: What status code should a rate-limited request return?
✅ Counter A: 429 Too Many Requests. Response should also include Retry-After header telling client how many seconds to wait. I test rate limiting by sending rapid repeated requests until I hit the limit and confirm 429 with the header.
🎯 Scenario:
How do you test API response time in your project?
📋 Key Points:
🎤 Answer:
“In Postman I add a test: pm.expect(pm.response.responseTime).to.be.below(2000) for a 2-second SLA. In RestAssured I use .time(lessThan(2000L), TimeUnit.MILLISECONDS). I also check the SLA defined in requirements — for payment APIs it may be 500ms, for reports it may be 5 seconds.”
❓ Counter Q: What do you do when an API is consistently slow?
✅ Counter A: I isolate whether slowness is in the API or database. I check if adding a database index helps. I look at the query with EXPLAIN. I check if caching can help for read-heavy endpoints. I raise it as a performance defect with before/after response times as evidence.
🎯 Scenario:
How do you test API request headers and Content-Type validation?
📋 Key Points:
🎤 Answer:
“I test without Content-Type and check response. Then Content-Type: application/xml for a JSON API — verify 415 Unsupported Media Type. I verify the response always returns Content-Type: application/json. I test custom headers like X-API-Key or X-Correlation-ID.”
❓ Counter Q: What is the difference between Content-Type and Accept headers?
✅ Counter A: Content-Type tells the server what format the request body is in. Accept tells the server what format the client wants in response. If I set Accept: application/xml but server only supports JSON it should return 406 Not Acceptable.
🎯 Scenario:
An API returns paginated results like GET /products?page=1&limit=10. What do you test?
📋 Key Points:
🎤 Answer:
“I test page 1 with limit 10 and verify 10 items with metadata like total_count. I test the last page for remaining items. Page 999 when total is 5 pages — verify empty array or 404. Edge cases limit=0 and limit=-1.”
❓ Counter Q: How do you verify no items are missed or duplicated across pages?
✅ Counter A: I collect all item IDs from all pages and check total count matches total_count from API. I check for duplicates using a Set — if set size differs from list size there are duplicates. This comprehensive check ensures pagination is correct.
🎯 Scenario:
How do you validate that an API response matches the expected schema?
📋 Key Points:
🎤 Answer:
“I create a JSON schema file defining all expected fields, types, and required constraints. In Postman I use Newman with JSON schema assertions. In RestAssured I use JsonSchemaValidator. I check every field type — string, integer, boolean, array. Required fields must never be null in success responses.”
❓ Counter Q: What happens when an API adds a new field?
✅ Counter A: If the new field is not in my schema and I use strict validation, the test fails. This is a good safety net — it catches undocumented API changes. I discuss with the team if the new field should be added to the schema or indicates an unintended change.
🎯 Scenario:
How do you apply boundary value analysis to API testing?
📋 Key Points:
🎤 Answer:
“For a username accepting 3-50 characters: I test length 3 (minimum valid), length 2 (just below — invalid), length 50 (maximum valid), length 51 (just above — invalid). I test empty string, whitespace-only, and null. For numeric fields I test max integer value to check overflow handling.”
❓ Counter Q: Why is boundary testing especially important for APIs?
✅ Counter A: The UI may prevent boundary violations through frontend validation. But attackers bypass the UI and call the API directly. API boundary testing ensures server-side validation exists independent of the frontend — critical for security and data integrity.
🎯 Scenario:
How do you test a complete CRUD flow for a resource?
📋 Key Points:
🎤 Answer:
“I test the full lifecycle. Step 1: POST to create user — save returned user_id. Step 2: GET user by that ID — verify all fields match. Step 3: PATCH to update email. Step 4: GET again — verify only email changed. Step 5: DELETE user. Step 6: GET user — verify 404.”
❓ Counter Q: Why not test each CRUD operation in isolation?
✅ Counter A: Isolated tests miss integration failures. POST may work and GET may work individually but GET may not return what POST created. The end-to-end CRUD flow tests the system as a whole and catches data persistence and consistency issues.
🎯 Scenario:
Some APIs require output from one API as input to another. How do you handle this?
📋 Key Points:
🎤 Answer:
“In Postman I extract the token: pm.environment.set(‘authToken’, pm.response.json().token). Then subsequent requests use {{authToken}}. In RestAssured I store the value in a Java variable and pass it to the next request. I always verify the extracted value is not null before using it.”
❓ Counter Q: What happens if the first API in a chain fails?
✅ Counter A: All dependent tests should be skipped, not failed. In TestNG I use dependsOnMethods. This gives a clear signal — the root cause is in the first API. Failing all dependents instead of skipping would hide the real issue.
🎯 Scenario:
How do you organize and run API tests in Postman for a project?
📋 Key Points:
🎤 Answer:
“I organize into a Collection per module — Auth, Users, Orders. Inside each I group by resource. Environment Variables for base URL and auth tokens — same collection runs against QA and staging by switching environment. Every request has Tests tab assertions for status code, response time, and key fields. I export and run with Newman in Jenkins.”
❓ Counter Q: How do you share a Postman Collection with your team?
✅ Counter A: I export the collection as JSON and commit it to the Git repository. Team members import it into their Postman. I also use Postman Workspaces for real-time sharing. Environment files are committed without sensitive tokens — tokens are stored locally by each team member.
🎯 Scenario:
What basic security tests do you perform on APIs as a QA engineer?
📋 Key Points:
🎤 Answer:
“I test SQL injection by sending ' OR 1=1 -- in string fields. I test IDOR with User A token to access User B resource URL. I check every authenticated endpoint by calling it without a token. I verify response bodies never expose passwords or full card numbers. I confirm HTTP redirects to HTTPS.”
❓ Counter Q: How deep should a QA engineer go in security testing?
✅ Counter A: As QA I focus on functional security gaps — IDOR, missing auth, sensitive data exposure, basic injection. Deep penetration testing is a security engineer role. But catching these basic issues early saves significant cost. I always include security scenarios in my API test plan for critical modules like auth and payments.
🎯 Scenario:
You receive a new APK build. What do you test first?
📋 Key Points:
🎤 Answer:
“I install on minimum supported Android version and latest Android. Verify app icon appears. Open app and verify launch without crash. Navigate main screens. Check permissions dialog — app should only request what it actually needs.”
❓ Counter Q: Difference between debug APK and release APK?
✅ Counter A: Debug APK is not optimized, contains debug symbols, can be installed directly. Release APK is ProGuard-optimized, signed with release key. Always test the release APK for final sign-off — debug APK behavior may differ in crash reporting and network behavior.
🎯 Scenario:
How do you test for app crashes systematically?
📋 Key Points:
🎤 Answer:
“I test all main user flows. I rotate screen mid-flow to trigger orientation change handling. Rapidly switch between apps to test background-foreground transitions. Simulate low battery. Trigger a phone call while app is active and verify app resumes correctly after the call.”
❓ Counter Q: How do you capture crash logs from an Android device?
✅ Counter A: I use adb logcat to capture device logs during testing. When a crash occurs the log shows FATAL EXCEPTION with stack trace. I filter with adb logcat | grep FATAL. I include the full stack trace in my bug report so developers can identify the exact line causing the crash.
🎯 Scenario:
How do you test a mobile app under different network conditions?
📋 Key Points:
🎤 Answer:
“I establish WiFi baseline first. Then test on 4G and 3G by switching network settings. Enable airplane mode and verify app shows proper error instead of crashing. Test switching from WiFi to mobile data mid-flow. Use Developer Options to limit network speed to Slow 3G for timeout testing.”
❓ Counter Q: How do you simulate slow network without a real slow network?
✅ Counter A: Android Developer Options has network speed throttling settings. I also use Charles Proxy on my laptop to intercept and throttle traffic from the phone. For automated testing the Android Emulator has network condition simulation.
🎯 Scenario:
How do you test app permissions on Android?
📋 Key Points:
🎤 Answer:
“I test granting each required permission and verify the feature works. I deny camera permission and verify the photo upload feature shows a proper message instead of crashing. I grant permission, use the feature, then revoke from Settings and return to app to verify graceful handling.”
❓ Counter Q: Difference between install-time and runtime permissions?
✅ Counter A: Install-time permissions granted at installation — example INTERNET. Runtime permissions must be requested while app is running and can be granted or denied individually — example CAMERA, LOCATION. All dangerous permissions are runtime permissions since Android 6.
🎯 Scenario:
How do you test the UI across different screen sizes?
📋 Key Points:
🎤 Answer:
“I test on both a small 5-inch device and a large 6.7-inch device. I verify text is fully visible and not cut off. I check all buttons are large enough — minimum 48dp. I switch between portrait and landscape and verify layout adjusts correctly.”
❓ Counter Q: What is a 48dp touch target and why does it matter?
✅ Counter A: 48dp is the Google Material Design minimum size for touch targets. Elements smaller than 48dp are hard to tap accurately causing user frustration. I verify any interactive element — buttons, checkboxes, links — meets the 48dp minimum.
🎯 Scenario:
How do you test login and session management on a mobile app?
📋 Key Points:
🎤 Answer:
“I verify valid login stores the session token in EncryptedSharedPreferences or Keystore — not plain text. I background the app for 5 minutes and reopen — session should persist. I wait for session timeout and verify redirect to login with appropriate message. I logout and verify all user data cleared.”
❓ Counter Q: Where should a mobile app store authentication tokens?
✅ Counter A: Android Keystore for sensitive tokens — hardware-backed and secure. EncryptedSharedPreferences for smaller data. Never plain SharedPreferences, plain files, or external storage as other apps and users can read them.
🎯 Scenario:
How do you test how the app behaves with no internet connection?
📋 Key Points:
🎤 Answer:
“I turn off WiFi and mobile data and attempt all main actions. Actions requiring internet should show a friendly offline message — not crash. Previously loaded cached data should still display. When I reconnect I verify any pending actions sync correctly.”
❓ Counter Q: What is graceful degradation vs crash on no network?
✅ Counter A: Graceful degradation means the app detects no network and shows cached data or a friendly error — user experience maintained. A crash means unhandled exception when the network call fails. Graceful degradation is expected. Any crash on network loss is Critical.
🎯 Scenario:
How do you test push notifications?
📋 Key Points:
🎤 Answer:
“I test notifications in all three app states. Foreground: in-app banner appears. Background: system notification in notification bar. Closed: notification still arrives. I tap each notification and verify it deep-links to the correct screen. I verify notification title, body, and badge count are correct.”
❓ Counter Q: How do you trigger a push notification for testing?
✅ Counter A: I ask the backend team to send a test push via the Firebase Console. I also use Postman to hit the FCM API directly with the device token. For automated testing some teams use mock push servers that simulate notification delivery.
🎯 Scenario:
How do you test the app update from an old version to new?
📋 Key Points:
🎤 Answer:
“I install the previous release APK and create data — login, add items, configure settings. Then update to the new APK. I verify all existing user data is preserved — login session, preferences, saved items. I test all new features. I run regression on existing features to verify no regressions.”
❓ Counter Q: What is a data migration bug?
✅ Counter A: A data migration bug occurs when updating the app changes the database schema but existing data from the old schema is not correctly migrated. I test this by creating data in the old version and verifying it displays correctly after update.
🎯 Scenario:
How do you test deep links in a mobile app?
📋 Key Points:
🎤 Answer:
“I trigger deep links from browser, WhatsApp, and email. I verify the correct screen opens with parameters correctly passed. For example app://products/123 should open product screen with ID 123. I test with invalid product ID for graceful error. I test the deep link with the app closed.”
❓ Counter Q: Difference between a deep link and an App Link?
✅ Counter A: A deep link is a custom URI scheme like myapp://screen — any app can register to handle it causing conflicts. An App Link is verified using HTTPS and Digital Asset Links file — verified by Google and opens directly in the intended app without disambiguation. App Links are the modern preferred approach.
🎯 Scenario:
How do you test that the app stores data securely?
📋 Key Points:
🎤 Answer:
“I use adb shell to inspect the app data directory: adb shell run-as com.app.package ls data. I check SharedPreferences files for plain-text passwords or tokens. I verify the database file is encrypted if it contains sensitive data. I logout and re-inspect to verify data directory is cleared.”
❓ Counter Q: What is adb and how do you use it for mobile testing?
✅ Counter A: ADB is Android Debug Bridge — a command-line tool to communicate with Android devices. I use it for: adb logcat to read logs, adb install app.apk to install APKs, adb shell to run commands on the device, adb bugreport for full diagnostic report. Essential tool for Android testing.
🎯 Scenario:
How do you ensure the app works across different Android versions and brands?
📋 Key Points:
🎤 Answer:
“I test on minimum supported Android version and latest. I test on popular Indian brands — Samsung with One UI, OnePlus with OxygenOS, Realme. These manufacturers customize Android differently and can cause compatibility issues. I verify UI and features are consistent across all devices.”
❓ Counter Q: What bugs are specific to certain manufacturers?
✅ Counter A: Samsung often has stricter background process killing affecting push notifications. OnePlus has aggressive battery optimization that can prevent background tasks. Some manufacturers override system fonts breaking text rendering. This is why testing on multiple real devices or cloud device farms like BrowserStack is essential.
🎯 Scenario:
How do you test the performance of a mobile app?
📋 Key Points:
🎤 Answer:
“I measure cold start time — first launch after force-stop. I expect under 3 seconds for most apps. Warm start — reopening from background — should be under 1 second. I check scrolling for jank with Debug GPU Overdraw in Developer Options. I monitor memory in Android Studio Profiler to detect leaks.”
❓ Counter Q: Difference between cold start, warm start, and hot start?
✅ Counter A: Cold start: app process does not exist — all resources load from scratch, slowest. Warm start: app process exists but Activity was destroyed — faster than cold. Hot start: app is in memory and Activity is alive — just brought to foreground, fastest. I test all three.
🎯 Scenario:
How do you test notification permission on Android 13 and above?
📋 Key Points:
🎤 Answer:
“From Android 13 apps must request POST_NOTIFICATIONS permission at runtime. I test: first launch shows notification permission dialog, granting permission enables notifications, denying shows appropriate in-app message explaining what users miss. I also test the setting where user grants, then revokes from Settings app.”
❓ Counter Q: What changed about notification permissions in Android 13?
✅ Counter A: Before Android 13 notifications were enabled by default. From Android 13 they require explicit user permission — similar to camera and location. This is a breaking change for apps targeting API 33. Any app not requesting the permission on Android 13 will silently not show notifications even if user never explicitly denied.
🎯 Scenario:
Before approving an APK for release, what is your sign-off checklist?
📋 Key Points:
🎤 Answer:
“My release sign-off: all Critical and High bugs fixed and verified. Full regression on all modules. Smoke test on minimum and maximum supported Android versions. Performance and memory benchmarks met. Release APK tested — not debug APK. App does not crash during 30 minutes normal usage. Push notifications working. No regression in last 3 releases’ bug fixes.”
❓ Counter Q: Why test release APK and not debug APK?
✅ Counter A: Release APK has ProGuard code minification which can introduce new crashes not present in debug. Release APK also has different logging, network security config, and no debug-only flags. Teams have approved debug APK only to find crashes in the release build. Always sign off on the actual release artifact.
🎯 Scenario:
Write a query to find all duplicate email addresses in the Users table.
📋 Key Points:
🎤 Answer:
“SELECT email, COUNT(*) as count FROM Users GROUP BY email HAVING COUNT(*) > 1. This gives all emails appearing more than once. To see the user IDs: SELECT u.user_id, u.email FROM Users u JOIN (SELECT email FROM Users GROUP BY email HAVING COUNT(*) > 1) d ON u.email=d.email ORDER BY u.email.”
❓ Counter Q: How would you delete duplicates keeping only the latest?
✅ Counter A: DELETE FROM Users WHERE user_id NOT IN (SELECT MAX(user_id) FROM Users GROUP BY email). This keeps the highest user_id for each email. Always run a SELECT first to verify which rows will be deleted before running DELETE in production.
🎯 Scenario:
Write a query to find the second highest salary from the Employees table.
📋 Key Points:
🎤 Answer:
“Using DENSE_RANK: SELECT salary FROM (SELECT salary, DENSE_RANK() OVER (ORDER BY salary DESC) as rnk FROM Employees) t WHERE rnk=2. Or subquery: SELECT MAX(salary) FROM Employees WHERE salary < (SELECT MAX(salary) FROM Employees). I use DENSE_RANK because it handles ties correctly.”
❓ Counter Q: Why DENSE_RANK instead of RANK?
✅ Counter A: With RANK if two people tie for 1st, the next is rank 3 not rank 2. WHERE rnk=2 returns nothing even though a second distinct salary exists. DENSE_RANK gives consecutive ranks 1,1,2,3 so rank 2 always means second distinct value.
🎯 Scenario:
Write a query to get total sales per month for the current year.
📋 Key Points:
🎤 Answer:
“SELECT DATE_FORMAT(created_at, '%Y-%m') as month, SUM(total_amount) as revenue, COUNT(*) as order_count FROM Orders WHERE YEAR(created_at)=YEAR(NOW()) AND status='completed' GROUP BY DATE_FORMAT(created_at, '%Y-%m') ORDER BY month.”
❓ Counter Q: How do you include months with zero sales?
✅ Counter A: I create a reference table with all 12 months and LEFT JOIN with sales data. COALESCE(SUM(total_amount), 0) converts NULL to 0 for months with no orders.
🎯 Scenario:
Find all customers who have never placed an order.
📋 Key Points:
🎤 Answer:
“SELECT c.customer_id, c.name FROM Customers c LEFT JOIN Orders o ON c.customer_id=o.customer_id WHERE o.order_id IS NULL. LEFT JOIN includes all customers — those without orders have NULL in the order columns.”
❓ Counter Q: Why LEFT JOIN with NULL preferred over NOT IN?
✅ Counter A: NOT IN has a trap — if the subquery returns any NULL value, the entire NOT IN returns no results due to SQL three-valued logic. LEFT JOIN with IS NULL is safe and predictable. NOT EXISTS is also safe and often faster.
🎯 Scenario:
Find the highest paid employee in each department.
📋 Key Points:
🎤 Answer:
“SELECT name, department, salary FROM (SELECT name, department, salary, ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as rn FROM Employees) t WHERE rn=1. Returns exactly one employee per department. Use RANK instead of ROW_NUMBER to include all tied top earners.”
❓ Counter Q: Difference between PARTITION BY and GROUP BY?
✅ Counter A: GROUP BY reduces multiple rows into one — you lose individual row data. PARTITION BY keeps all rows but adds a calculated value. GROUP BY answers what is the max salary per department. PARTITION BY answers what is this employee’s rank within their department.
🎯 Scenario:
Calculate the running total of sales by date.
📋 Key Points:
🎤 Answer:
“SELECT order_date, daily_sales, SUM(daily_sales) OVER (ORDER BY order_date) as running_total FROM (SELECT DATE(created_at) as order_date, SUM(total_amount) as daily_sales FROM Orders GROUP BY DATE(created_at)) daily ORDER BY order_date.”
❓ Counter Q: How is a window function different from an aggregate?
✅ Counter A: Aggregate with GROUP BY collapses rows into groups. Window function performs calculation across rows related to the current row WITHOUT collapsing them. Every input row produces one output row. This is why running totals and ranks require window functions.
🎯 Scenario:
The Orders table should have sequential IDs. How do you find missing IDs?
📋 Key Points:
🎤 Answer:
“SELECT o1.order_id+1 as missing_id FROM Orders o1 LEFT JOIN Orders o2 ON o1.order_id+1=o2.order_id WHERE o2.order_id IS NULL AND o1.order_id < (SELECT MAX(order_id) FROM Orders). Finds every ID where ID+1 does not exist.”
❓ Counter Q: Why would there be gaps in sequential IDs?
✅ Counter A: Common reasons: auto-increment IDs consumed by rolled-back transactions. Deleted records leave gaps. Bulk inserts may skip ranges. Gaps are normal and expected in auto-increment columns — not a bug.
🎯 Scenario:
Find the top 3 best-selling products in each category.
📋 Key Points:
🎤 Answer:
“SELECT category, product_name, total_sold, rnk FROM (SELECT p.category, p.product_name, SUM(oi.quantity) as total_sold, DENSE_RANK() OVER (PARTITION BY p.category ORDER BY SUM(oi.quantity) DESC) as rnk FROM Order_Items oi JOIN Products p ON oi.product_id=p.product_id GROUP BY p.category, p.product_id) ranked WHERE rnk <= 3 ORDER BY category, rnk.”
❓ Counter Q: Why DENSE_RANK instead of ROW_NUMBER for top N?
✅ Counter A: If products at position 3 are tied, ROW_NUMBER arbitrarily picks one. DENSE_RANK correctly includes all tied products at position 3. The result may have more than 3 per category but that is the correct business answer.
🎯 Scenario:
Revenue report returning incorrect totals because some amounts are NULL. How do you fix it?
📋 Key Points:
🎤 Answer:
“SUM(total_amount - COALESCE(discount, 0)) as net_revenue. Without COALESCE, any row with NULL discount makes the subtraction NULL and SUM is lower than expected. COALESCE converts NULL to 0. Always check for NULLs before writing any aggregate query.”
❓ Counter Q: Does COUNT(*) count NULL values?
✅ Counter A: COUNT(*) counts all rows regardless of NULL. COUNT(column_name) skips NULL values and counts only rows where that column is not NULL. This critical difference causes many reporting bugs when developers use COUNT(column) thinking it counts all rows.
🎯 Scenario:
A query is running slowly in production. Walk through debugging and fixing it.
📋 Key Points:
🎤 Answer:
“I run EXPLAIN SELECT * FROM Orders WHERE created_at > '2026-01-01'. Type: ALL means full table scan. Key: NULL means no index used. I add: CREATE INDEX idx_orders_created ON Orders(created_at). Re-run EXPLAIN and verify type changed from ALL to range and rows dropped dramatically.”
❓ Counter Q: What are the different type values in EXPLAIN?
✅ Counter A: From worst to best: ALL=full scan, index=full index scan, range=index range scan, ref=index lookup by non-unique key, eq_ref=index lookup by unique key, const=single row by primary key (best). In well-indexed production queries you should see ref, eq_ref, or range. ALL on a large table is a performance emergency.
🎯 Scenario:
A multi-step database operation fails halfway. How do transactions protect data integrity?
📋 Key Points:
🎤 Answer:
“Without transaction: INSERT into Orders succeeds, UPDATE stock fails — now there is an order but stock was not decremented. With transaction: BEGIN; INSERT Orders; UPDATE Products SET stock=stock-1; if UPDATE returns 0 rows affected ROLLBACK — both changes undone. COMMIT only if both succeed.”
❓ Counter Q: What is SAVEPOINT?
✅ Counter A: SAVEPOINT creates a checkpoint inside a transaction. You can ROLLBACK to the savepoint without rolling back the entire transaction. Useful in bulk imports where a failed row should be skipped but previous rows should be kept.
🎯 Scenario:
Transform row data into columns — show monthly sales across one row per year.
📋 Key Points:
🎤 Answer:
“SELECT YEAR(created_at) as year, SUM(CASE WHEN MONTH(created_at)=1 THEN total_amount ELSE 0 END) as Jan, SUM(CASE WHEN MONTH(created_at)=2 THEN total_amount ELSE 0 END) as Feb FROM Orders WHERE status='completed' GROUP BY YEAR(created_at).”
❓ Counter Q: Advantage of conditional aggregation over application-level pivoting?
✅ Counter A: SQL pivot means one query and the database returns exactly the format the UI needs. Application-level pivoting requires fetching all rows and processing in memory — much slower for large datasets.
🎯 Scenario:
Employees table has manager_id referencing itself. How do you get all subordinates of a manager?
📋 Key Points:
🎤 Answer:
“WITH RECURSIVE subordinates AS (SELECT employee_id, name, manager_id, 0 as level FROM Employees WHERE employee_id=1 UNION ALL SELECT e.employee_id, e.name, e.manager_id, s.level+1 FROM Employees e JOIN subordinates s ON e.manager_id=s.employee_id) SELECT * FROM subordinates.”
❓ Counter Q: Why recursive CTE instead of a simple JOIN?
✅ Counter A: A simple JOIN handles one level. If hierarchy has 5 levels you need 5 JOINs and must know the depth upfront. Recursive CTE handles arbitrary depth — keeps joining until no more matches. Essential for org charts, category trees, and any self-referencing hierarchical data.
🎯 Scenario:
Detect overlapping hotel bookings for the same room.
📋 Key Points:
🎤 Answer:
“SELECT b1.booking_id, b2.booking_id as conflict FROM Bookings b1 JOIN Bookings b2 ON b1.room_id=b2.room_id AND b1.booking_id < b2.booking_id AND b1.check_in < b2.check_out AND b2.check_in < b1.check_out.”
❓ Counter Q: Logic behind the date range overlap formula?
✅ Counter A: Two ranges [A_start, A_end) and [B_start, B_end) overlap if A_start < B_end AND B_start < A_end. The overlap formula is the negation of both non-overlap conditions.
🎯 Scenario:
How do you write SQL to validate data quality in a production database?
📋 Key Points:
🎤 Answer:
“SELECT 'NULL user_id' as issue, COUNT(*) FROM Orders WHERE user_id IS NULL UNION ALL SELECT 'Invalid status', COUNT(*) FROM Orders WHERE status NOT IN ('pending','completed','cancelled') UNION ALL SELECT 'Negative amount', COUNT(*) FROM Orders WHERE total_amount < 0. One report showing all data quality issues.”
❓ Counter Q: How often should data quality checks run?
✅ Counter A: Critical checks like NULL mandatory fields daily as a scheduled job. Referential integrity weekly. If any issue count exceeds zero the job sends an email alert to the DBA and QA team. Proactive data quality monitoring prevents reporting bugs from bad data.
🎯 Scenario:
You find a bug that appears randomly and cannot be reproduced consistently. How do you handle it?
📋 Key Points:
🎤 Answer:
“When I encounter a random bug I immediately capture screenshot and video. I note every detail — exact steps, browser version, OS, network speed, time. I try to reproduce it 10 times and note frequency. I raise it with all evidence marked as Intermittent and ask the developer to add logging so the next occurrence can be traced.”
❓ Counter Q: Should you raise a bug you cannot reproduce?
✅ Counter A: Yes. Intermittent bugs often indicate race conditions or memory issues. Never ignore a bug just because it does not reproduce consistently. Mark it Intermittent with all evidence. The cost of intermittent bugs in production is extremely high.
🎯 Scenario:
The developer says your bug is actually a feature working as designed. How do you handle this?
📋 Key Points:
🎤 Answer:
“I go back to the requirements document and find the specific acceptance criteria. If behavior contradicts the written requirement I cite the exact section and reopen the bug with reference. If requirements are ambiguous I escalate to the Product Owner for a clear decision. I never argue based on opinion.”
❓ Counter Q: What if requirements are silent on the behavior?
✅ Counter A: I document my expectation clearly and escalate to the Product Owner. Their decision is recorded in Jira and becomes the source of truth. Requirements gaps discovered through testing are valuable project deliverables.
🎯 Scenario:
A bug was fixed and verified. Now in a new build it reappeared. What do you do?
📋 Key Points:
🎤 Answer:
“I verify the behavior matches the original bug exactly. I reopen the original Jira ticket rather than creating a new one — this preserves history. I add a comment noting which builds passed and failed. I check Git commit history between the two builds to find what changed. This is a regression and is a serious quality signal.”
❓ Counter Q: What does a high regression rate indicate?
✅ Counter A: Poor code modularity or insufficient unit test coverage. Fixes in one area breaking other areas. I flag regression trends in weekly status reports. If regressions consistently hit the same module that module needs code review and refactoring.
🎯 Scenario:
Users report payment is failing in production. How do you respond?
📋 Key Points:
🎤 Answer:
“I immediately try to reproduce on production URL. I check if it affects all users or specific accounts, all browsers or one browser. I check for recent deployments. I raise a Critical Blocker in Jira with all evidence and directly message my Test Lead and Release Manager on Slack.”
❓ Counter Q: Difference between Severity and Priority?
✅ Counter A: Severity is impact on functionality — payment failure is Critical. Priority is urgency — also P1 because every minute costs the business money. Usually Critical severity means High Priority but not always — a typo seen by millions can be Low severity but High Priority.
🎯 Scenario:
You run 200 test cases and 30 fail. How do you triage?
📋 Key Points:
🎤 Answer:
“I separate 30 failures into categories — environment issues, test data issues, genuine bugs. Of genuine failures I group by module. If login is broken and 10 tests fail because of it that is one root cause. I raise one bug for the root cause rather than 10 separate bugs.”
❓ Counter Q: How do you prevent false failures affecting metrics?
✅ Counter A: I maintain separate status: Passed, Failed-Genuine-Bug, Failed-Environment-Issue, Blocked. This gives management an accurate picture. A day with 30 failures due to server downtime should not count the same as 30 genuine regressions.
🎯 Scenario:
What makes a bug report effective? Walk through writing one for a payment failure.
📋 Key Points:
🎤 Answer:
“Title: Checkout — Payment fails with credit card on Chrome, returns 500. Steps: 1. Login. 2. Add product. 3. Checkout. 4. Enter card. 5. Click Pay. Expected: Order confirmation. Actual: 500 error page. Environment: Chrome 120, Windows 11, Build B14. Attached: screenshot and console log.”
❓ Counter Q: Why are Steps to Reproduce the most critical part?
✅ Counter A: A bug that cannot be reproduced cannot be fixed. Precise numbered steps mean the developer runs them once and sees the bug immediately — saving hours of investigation.
🎯 Scenario:
You have 2 hours to test a feature before release. How do you prioritize?
📋 Key Points:
🎤 Answer:
“With 2 hours I test in priority order: happy path first — if it fails nothing else matters. Then critical negative scenarios. Then most likely edge cases based on recent changes. I document what was tested and what was skipped and send a risk summary to my lead.”
❓ Counter Q: How do you decide what is high risk?
✅ Counter A: Recently changed code, complex business logic, areas with previous bugs, and critical flows like payment and login are always high risk. Risk-based testing is a skill that grows with experience.
🎯 Scenario:
How do you approach exploratory testing when there are no formal test cases?
📋 Key Points:
🎤 Answer:
“I define a charter: Explore the order tracking feature as a customer who placed an order 2 days ago. I spend 60-90 minutes exploring freely but guided by the charter. I take notes and raise bugs as I go. After the session I document new test cases discovered. The key is having a goal.”
❓ Counter Q: Difference between exploratory and ad-hoc testing?
✅ Counter A: Exploratory has a charter, time-box, note-taking, and output. Ad-hoc is random with no planning. Exploratory is a professional technique; ad-hoc is informal and results are hard to document.
🎯 Scenario:
How do you decide what to include in a regression test suite?
📋 Key Points:
🎤 Answer:
“My regression suite includes tests that caught bugs before, happy path for all core modules — login, cart, checkout, profile — and complex business rules. I automate stable tests in Selenium. Every sprint I review — add tests for new features, remove obsolete ones. Regression must evolve with the product.”
❓ Counter Q: How do you handle regression when release has 50 new features?
✅ Counter A: Risk-rank the features. Top 10 get full regression attention. Others get smoke test only. Always run full regression on existing features. Use impact analysis — which modules did the new code touch? Prioritize testing those modules.
🎯 Scenario:
You find a Critical bug on the last day before release. What do you do?
📋 Key Points:
🎤 Answer:
“I immediately raise in Jira and directly message my Test Lead and Release Manager. I state clearly: This bug prevents users from completing checkout. I recommend delaying release. My job is to report and recommend — the business decision belongs to management. But I make the risk crystal clear.”
❓ Counter Q: What if management releases despite the Critical bug?
✅ Counter A: I document my recommendation in writing in the bug ticket and release sign-off. If the bug causes production issues I can show the risk was communicated. I never silently approve a release with open Critical bugs.
🎯 Scenario:
How do you measure whether your testing is sufficient?
📋 Key Points:
🎤 Answer:
“I track four metrics: requirements coverage via traceability matrix, execution rate, pass rate, and defect detection efficiency — bugs caught in QA divided by all bugs. High detection efficiency means QA is catching bugs before users see them. I report these in my weekly status update.”
❓ Counter Q: What is defect leakage?
✅ Counter A: Bugs that escaped QA and were found in production. Formula: production bugs / (QA bugs + production bugs) * 100. High leakage means QA is not effective. I analyze each production bug — was it testable? This analysis improves the test suite for the next release.
🎯 Scenario:
You are asked to test a feature but there are no requirements. What do you do?
📋 Key Points:
🎤 Answer:
“I ask the developer or BA to explain the feature verbally and take notes. I document my understanding and share it for confirmation before writing test cases. If my understanding is wrong it is corrected early rather than after writing 50 wrong test cases.”
❓ Counter Q: What risks do you take testing without requirements?
✅ Counter A: Risk of testing what was built rather than what was needed. Also cannot confirm completeness — you do not know what scenarios you missed. Undocumented features often become disputed bugs later.
🎯 Scenario:
A bug appears in Firefox but not Chrome. How do you handle cross-browser bugs?
📋 Key Points:
🎤 Answer:
“I test across Firefox versions and verify Chrome and Edge work correctly. I raise the bug noting exact browser versions and include screenshots from both browsers side-by-side. I note whether it is a rendering or functional bug. The developer will investigate browser-specific CSS or JavaScript behavior.”
❓ Counter Q: When is a cross-browser difference a bug vs expected?
✅ Counter A: Completely broken functionality in one browser is always a bug. Slight visual differences may be acceptable depending on the compatibility matrix in the test plan. Most companies require identical appearance across Chrome, Firefox, and Edge.
🎯 Scenario:
How do you verify the UI is displaying data that matches the API response?
📋 Key Points:
🎤 Answer:
“I call GET /orders/123 in Postman and note values: total 1500.50, status completed, date 2026-01-15. I verify UI shows Rs 1,500.50 formatted correctly, status as Completed, date as 15 Jan 2026. Discrepancies between API and UI are data display bugs. I also check NULL handling — null delivery_date should show Not yet shipped.”
❓ Counter Q: Why test both API and UI independently?
✅ Counter A: API may return correct data but UI displays it wrong — frontend bug. API may return wrong data — backend bug. Independent testing lets me precisely identify which layer has the issue and assign to the correct team.
🎯 Scenario:
The same types of bugs keep appearing sprint after sprint. What do you do?
📋 Key Points:
🎤 Answer:
“I collect data: in 4 sprints 15 bugs were related to missing API validation. I present this in the retrospective with evidence and suggest adding unit test requirements for validation rules. I track the next 4 sprints to measure improvement. QA is not just finding bugs — it is improving the process.”
❓ Counter Q: What is the Definition of Done?
✅ Counter A: Team's agreed criteria for when a story is truly complete. QA contributes the testing criteria — tested on required browsers, all test cases passed, no open Critical bugs. When QA signs off the story is marked done.
🎯 Scenario:
Jenkins CI pipeline failed on test stage. How do you investigate?
📋 Key Points:
🎤 Answer:
“I open Jenkins build log and find the failing stage. I read the first red error line and full stack trace. I check which specific test method failed. I try to reproduce locally. If the test passed for weeks and suddenly fails without code changes it may be flaky. If it fails consistently after a code change it is a genuine regression.”
❓ Counter Q: What is a flaky test and how do you handle it?
✅ Counter A: Passes and fails intermittently without code changes. Short-term: retry logic up to 3 times. Long-term: add explicit waits, make tests independent, mock external dependencies.
🎯 Scenario:
How does your Git branching workflow look for a release?
📋 Key Points:
🎤 Answer:
“We use GitFlow. Developers create feature branches from develop. When done they raise a Pull Request — minimum one reviewer approves. Merging to develop triggers Jenkins to deploy to QA. Before release a release branch is created from develop. After QA sign-off release merges to main triggering production deployment.”
❓ Counter Q: What is a Pull Request and why is code review important?
✅ Counter A: A Pull Request is a request to merge code with a review step. Code review catches bugs before QA. As QA I sometimes review PRs checking for test coverage — if a new feature has no new test cases I comment asking for them. This is shift-left testing.
🎯 Scenario:
A deployment went to production and users report errors. How do you rollback?
📋 Key Points:
🎤 Answer:
“I confirm errors started immediately after the 2 PM deployment. In Kubernetes: kubectl rollout undo deployment/myapp. I verify the health check endpoint returns 200. I monitor error rate for 10 minutes to confirm recovery. I raise a Critical bug and document the incident timeline.”
❓ Counter Q: How do you prevent the same deployment failure again?
✅ Counter A: Post-mortem: what was root cause, what test would have caught it. If a missing test scenario I add it to the test suite. If environment-specific I add that environment to the test matrix.
🎯 Scenario:
App works in QA but fails in Staging. How do you investigate?
📋 Key Points:
🎤 Answer:
“Most common cause is configuration difference. I compare config files for QA and Staging. I check database connection strings, API endpoint URLs, environment variables. I check network access — staging may have stricter firewall rules.”
❓ Counter Q: Best way to manage configuration differences?
✅ Counter A: Use environment variables for all configuration — never hardcode. Store secrets in secrets manager. Same application code across all environments — only configuration changes.
🎯 Scenario:
Your CI pipeline takes 45 minutes. How do you reduce it?
📋 Key Points:
🎤 Answer:
“Build 5 min, unit tests 3 min, integration 12 min, Selenium 25 min. Selenium is the bottleneck. I parallelize using TestNG parallel and Selenium Grid — 5 threads reduces it from 25 to 6 minutes. I split tests into smoke (10 critical) and full regression. PRs run only smoke in 8 minutes total.”
❓ Counter Q: Risks of parallelizing test execution?
✅ Counter A: Tests sharing test data can interfere. Solution: test data isolation — each test creates unique data with UUID. Tests must be order-independent. ThreadLocal WebDriver prevents cross-thread interference.
🎯 Scenario:
After every production deployment, what smoke tests do you run?
📋 Key Points:
🎤 Answer:
“My production smoke suite has 15 tests running in 8 minutes. Covers login, homepage load under 2 seconds, product search, checkout flow with test payment, user profile, admin access. If any fail I immediately alert the Release Manager for rollback decision.”
❓ Counter Q: Why run smoke tests after production deployment?
✅ Counter A: Production has real infrastructure, real third-party integrations, real SSL. Staging mimics production but is never identical. An 8-minute smoke test catches environment-specific issues before users report them.
🎯 Scenario:
Your app is deployed in Docker. How does this change your testing approach?
📋 Key Points:
🎤 Answer:
“I test using the actual Docker image built by CI — not source code on my machine. This ensures I test the exact artifact being deployed. I verify environment variables are injected correctly by checking startup logs. I test the health check endpoint.”
❓ Counter Q: Why is Docker the solution to works on my machine?
✅ Counter A: Docker packages the application with all dependencies into an image that runs identically everywhere. Same Docker image tested locally is the exact image deployed to production. Eliminates environment-dependent bugs.
🎯 Scenario:
A user reports an error but cannot describe what happened. How do you use logs?
📋 Key Points:
🎤 Answer:
“I ask the user for exact time. I go to Kibana and search: timestamp:[14:30 TO 14:35] AND user_id:12345 AND level:ERROR. I find relevant log entries and read the full stack trace. I trace backward — what API endpoint was called, what parameters, where did it fail.”
❓ Counter Q: What information should every log entry contain?
✅ Counter A: Timestamp, log level, correlation ID, user ID, operation being performed, error message and stack trace. A good log: 2026-01-15 ERROR [req:abc123] [user:12345] PaymentService: Connection timeout. Tells what, when, for whom, and where.
🎯 Scenario:
What is your QA sign-off checklist before a production release?
📋 Key Points:
🎤 Answer:
“My release sign-off: all test cases executed — 0 skipped without justification. All Critical bugs closed. All High bugs closed or accepted with written risk sign-off. Regression suite passed. Build version tested matches what will be deployed. I send sign-off email with test coverage summary.”
❓ Counter Q: What if there is not enough time for the full checklist?
✅ Counter A: Communicate the gap immediately. State clearly what was tested and what was not. Document the known risk in writing. Never silently cut scope.
🎯 Scenario:
Monitoring sends 100 alerts daily but most are false positives. How do you improve this?
📋 Key Points:
🎤 Answer:
“I analyze 2 weeks of alerts. 40% fire for latency spikes self-resolving in 30 seconds — I add a 2-minute for duration. 30% fire for individual pod restarts handled automatically — I set threshold to 3+ restarts in 10 minutes. Daily alerts drop from 100 to 12, all actionable.”
❓ Counter Q: What is alert fatigue and why is it dangerous?
✅ Counter A: Teams receive so many alerts they start ignoring them — including real ones. Alert fatigue is responsible for many major incidents. Good alerting means every alert requires human action.
🎯 Scenario:
A Critical bug is found in production. How does a hotfix get tested and deployed?
📋 Key Points:
🎤 Answer:
“Developer creates hotfix branch from main. Only the specific fix is made. QA tests the exact failing scenario plus adjacent cases — all card types, different amounts. If critical scenarios pass I sign off within 2-3 hours. Hotfix merges to main AND to develop so the fix is not lost.”
❓ Counter Q: Why must hotfix branch from main and not develop?
✅ Counter A: Develop may have unreleased features. Branching from develop would accidentally include those in the hotfix and deploy them to production unexpectedly. Branch from main means starting from exact code running in production.
🎯 Scenario:
How would you integrate a security scan into a CI pipeline?
📋 Key Points:
🎤 Answer:
“I integrate three scan types. SonarQube analyzes code for SQL injection, XSS, hardcoded secrets — if Quality Gate fails pipeline stops. OWASP Dependency Check scans library dependencies against CVE database. Trivy scans Docker image before push to registry.”
❓ Counter Q: Difference between SAST and DAST?
✅ Counter A: SAST analyzes source code without running the app — finds code logic vulnerabilities. DAST tests the running app — sends malicious inputs and analyzes responses. SAST in CI pipeline. DAST against deployed staging. Both together provide better coverage.
🎯 Scenario:
How do you manage test data for automated tests in CI pipelines?
📋 Key Points:
🎤 Answer:
“For unit and API tests: each test runs inside a transaction rolled back at the end — no data persists. For integration tests: test data factory creates specific scenarios programmatically. For UI tests: dedicated test database restored from clean snapshot before every CI run. Never use production data.”
❓ Counter Q: What is test data pollution?
✅ Counter A: One test creates data affecting another test. If Test A creates user test@test.com and does not delete it, Test B registering same email fails. Solution: each test creates unique data using UUID. Tests are completely independent.
🎯 Scenario:
Your team uses blue-green deployment. How does QA fit in?
📋 Key Points:
🎤 Answer:
“When new version is deployed to Green I run complete smoke test suite against Green URL. I also run API comparison — same endpoints on both Blue and Green with same request and compare responses. If smoke passes I give go-ahead to switch the load balancer. If any smoke fails load balancer stays on Blue — zero user impact.”
❓ Counter Q: How is blue-green safer than direct production deployment?
✅ Counter A: Traffic switch is instant. Allows full testing in production-identical environment before any user sees it. Rollback is instant — switch load balancer back to Blue in seconds. Direct deployment rollback requires re-deploying old version which takes time.
🎯 Scenario:
How has understanding DevOps made you a better QA engineer?
📋 Key Points:
🎤 Answer:
“DevOps knowledge changed how I test. I think about not just does this feature work but how does it behave when deployed at 3 AM with a rollback. I integrate tests into the CI pipeline so they catch bugs before code merges. I check deployment logs when something fails in QA instead of just saying it does not work.”
❓ Counter Q: What is the most valuable DevOps concept for a QA engineer to learn first?
✅ Counter A: CI/CD pipeline integration — getting your tests running automatically on every code change. When tests run in the pipeline quality feedback is immediate — bugs caught in minutes instead of days. Start with Jenkins or GitHub Actions.
Articles on software testing, interview tips and career advice for Indian IT professionals.