Part 2 of 4 · Ch 4–6 · P29–P61
C++ Mastery · Complete Edition

Arrays · Strings
Pattern Printing

Chapters 4 through 6. Arrays with two-pointer and sliding window strategies. Strings with frequency, parsing, and manipulation. Pattern printing with the universal 3-question method. Every problem verified and cleaned.

Ch 4 · Array Problems · P29–P40 Ch 5 · String Problems · P41–P53 Ch 6 · Pattern Printing · P54–P61
Chapter 04
Array Problems — The Most Common Interview Territory

Two Pointer Template Mastery Concept

When to use Two Pointers

Use when the array is sorted and you need to find pairs, triplets, or check symmetric properties. One pointer starts at the left, one at the right. They move toward each other. Signal words: "sorted array", "pair with sum", "palindrome check", "in-place reversal".

// Generalized Two Pointer Template
int left=0, right=n-1;
while(left < right){
    if(condition_met){
        // process this pair
        left++; right--;
    }
    else if(need_larger) left++;
    else right--;
}

P29 Sum of Odd and Even Elements Beginner

Given an array, find the sum of all odd elements and the sum of all even elements separately.

Input[1,2,3,4,5]
OutputOdd=9, Even=6
Dry Run: [1, 2, 3, 4, 5]
x=1 (odd):  oddSum=1
x=2 (even): evenSum=2
x=3 (odd):  oddSum=4
x=4 (even): evenSum=6
x=5 (odd):  oddSum=9
Result: Odd=9, Even=6
void oddEvenSum(vector<int>& a){
    long long oddSum=0, evenSum=0;
    for(int x:a){
        if(x&1) oddSum  += x;
        else    evenSum += x;
    }
    cout << "Odd="  << oddSum
         << " Even=" << evenSum << "\n";
}
Insight & Complexity

Time: O(n) | Space: O(1). Use x & 1 instead of x % 2 — it works correctly for negative numbers too (negative % 2 can be −1 in C++).

P30 Product of Array Elements Beginner

Find the product of all elements in an array.

Overflow Warning

20 elements each equal to 10 gives a product of 10²⁰ — this overflows even long long (max ≈ 9.2×10¹⁸). Always check constraints. Use modular arithmetic if required.

Dry Run: [2, 3, 4]
prod=1 × 2 = 2
prod=2 × 3 = 6
prod=6 × 4 = 24
long long arrayProduct(vector<int>& a){
    long long prod=1;
    for(int x:a) prod *= x;
    return prod;
}
// STL: accumulate(a.begin(),a.end(),1LL,multiplies<long long>())
Insight & Complexity

Time: O(n) | Space: O(1). Initialize accumulator as 1LL (long long literal) — starting with plain 1 causes int overflow before the first multiplication completes.

P31 Print Alternate Elements Beginner

Print elements at even indices (0, 2, 4…) and elements at odd indices (1, 3, 5…) on separate lines.

Input[10,20,30,40,50]
OutputEven: 10 30 50 | Odd: 20 40
void alternateElements(vector<int>& a){
    cout << "Even indices: ";
    for(int i=0; i<(int)a.size(); i+=2) cout << a[i] << " ";
    cout << "\nOdd  indices: ";
    for(int i=1; i<(int)a.size(); i+=2) cout << a[i] << " ";
    cout << "\n";
}
Insight & Complexity

Time: O(n) | Space: O(1). Cast a.size() to (int) when comparing with an int loop variable — size() returns an unsigned type and comparing int vs unsigned causes subtle bugs.

P32 Palindromic Array Two Pointer

Check if an array reads the same forwards and backwards. [1,2,3,2,1] → Yes. [1,2,3,4] → No.

Approach — Two Pointers from Both Ends

Place a left pointer at index 0 and a right pointer at the last index. If elements match, move both inward. If any pair mismatches, it's not a palindrome. Stop when left meets or crosses right.

Array: [1, 2, 3, 2, 1]
l=0, r=4: a[0]=1 == a[4]=1 ✓  →  l=1, r=3
l=1, r=3: a[1]=2 == a[3]=2 ✓  →  l=2, r=2
l=2, r=2: l < r is false  →  PALINDROME ✓
bool isPalindromeArr(vector<int>& a){
    int l=0, r=(int)a.size()-1;
    while(l < r){
        if(a[l] != a[r]) return false;
        l++; r--;
    }
    return true;
}
Insight & Complexity

Time: O(n) | Space: O(1). This exact two-pointer pattern also solves "palindrome string" — just replace array index access with string character access.

P33 Second & Third Largest — Single Pass Single Pass

Find the second and third largest distinct values in an array in a single O(n) pass.

Approach — Track Three Variables

Maintain first, second, third (all initialised to INT_MIN). For each element, update from the top down. Critical: use strict inequality and a distinct check (x != first) to avoid treating duplicates as separate ranks.

Array: [3, 1, 4, 1, 5, 9, 2, 6]
x=3: f=3
x=1: 1<f(3), 1>s(MIN) → s=1
x=4: 4>f(3) → t=s(1), s=f(3), f=4
x=1: 1==s(3)? No. 1==t(1)? Yes → skip (distinct check)
x=5: 5>f(4) → t=3, s=4, f=5
x=9: 9>f(5) → t=4, s=5, f=9
x=2: 2>t(4)? No → skip
x=6: 6>s(5), 6<f(9) → t=5, s=6
Result: 1st=9  2nd=6  3rd=5
void topThree(vector<int>& a){
    int f=INT_MIN, s=INT_MIN, t=INT_MIN;
    for(int x : a){
        if(x > f)              { t=s;  s=f;  f=x; }
        else if(x > s && x!=f) { t=s;  s=x;       }
        else if(x > t && x!=s) { t=x;              }
    }
    cout << "1st="<<f << " 2nd="<<s << " 3rd="<<t << "\n";
}
Insight & Complexity

Time: O(n) | Space: O(1). The distinct check x != f (and x != s) is what separates "k-th largest value" from "k-th largest element counting duplicates". Know which one the problem wants.

P34 Perfect Number Array Check Definition Check

Check if an array is "perfect" — every element is a perfect number. A perfect number equals the sum of its proper divisors (6 = 1+2+3 ✓, 28 = 1+2+4+7+14 ✓).

Approach

For each element, sum all divisors from 1 to √n (using the pair-counting trick from P09). Exclude n itself (proper divisors only). Check if sum equals n.

isPerfect(6)
i=1: 6%1==0 → sum += 1 + 6/1=6? No, only add i (not n itself)
     divisors of 6: 1, 2, 3  →  sum = 1+2+3 = 6 == n ✓
bool isPerfectNum(int n){
    if(n < 2) return false;
    int sum = 1;               // 1 is always a proper divisor
    for(int i=2; i*i<=n; i++){
        if(n%i==0){
            sum += i;
            if(i != n/i) sum += n/i;
        }
    }
    return sum == n;
}
bool isPerfectArray(vector<int>& a){
    for(int x : a) if(!isPerfectNum(x)) return false;
    return true;
}
Insight & Complexity

Time: O(n√m) where m is max element | Space: O(1). Only four perfect numbers exist below 10,000: 6, 28, 496, 8128. This property is surprisingly rare.

P35 Check if Array is Sorted Beginner

Determine if an array is sorted in non-decreasing order.

Input[1,2,2,4,5]
OutputYes
Array: [1, 3, 2, 4]
i=1: a[1]=3 >= a[0]=1  ✓
i=2: a[2]=2 <  a[1]=3  →  NOT sorted ✗
bool isSorted(vector<int>& a){
    for(int i=1; i<(int)a.size(); i++)
        if(a[i] < a[i-1]) return false;
    return true;
}
// STL: is_sorted(a.begin(), a.end())
Insight & Complexity

Time: O(n) | Space: O(1). For strictly increasing (no duplicates), use a[i] <= a[i-1] as the fail condition instead of <.

P36 Intersection & Union of Two Sorted Arrays Two Pointer

Find the intersection (common elements) and union (all unique elements) of two sorted arrays without using sets.

Approach — Merge-Style Two Pointers

Intersection: If a[i]==b[j], it's common — include and advance both. If a[i]<b[j], advance i. Else advance j.

Union: At each step take the smaller element; on equal take one copy and advance both. Append remaining elements at end.

a=[1,3,4,5,7] b=[2,3,5,6]
Intersection: 3==3 ✓, 5==5 ✓  →  [3, 5]
Union:        [1,2,3,4,5,6,7]
vector<int> intersection(vector<int>& a, vector<int>& b){
    vector<int> res;
    int i=0, j=0;
    while(i<(int)a.size() && j<(int)b.size()){
        if     (a[i]==b[j]){ res.push_back(a[i]); i++; j++; }
        else if(a[i]<b[j])  i++;
        else               j++;
    }
    return res;
}

vector<int> unionArr(vector<int>& a, vector<int>& b){
    vector<int> res;
    int i=0, j=0;
    while(i<(int)a.size() && j<(int)b.size()){
        if     (a[i]<b[j])  res.push_back(a[i++]);
        else if(a[i]>b[j])  res.push_back(b[j++]);
        else{ res.push_back(a[i]); i++; j++; }  // equal: take once
    }
    while(i<(int)a.size()) res.push_back(a[i++]);
    while(j<(int)b.size()) res.push_back(b[j++]);
    return res;
}
Insight & Complexity

Time: O(n+m) | Space: O(n+m) for result. This merge-pointer technique is the same logic used inside merge sort — understanding it deeply pays dividends.

Fixed Sliding Window Template Mastery Concept

When to use Fixed Sliding Window

Use when you need the max/min/sum of a contiguous subarray of exactly size K. Signal words: "subarray of size K", "contiguous window". The key insight: instead of recomputing the sum from scratch each time, add the new element entering the window and subtract the element leaving it.

// Fixed size K sliding window — max sum
int maxSumK(vector<int>& arr, int k){
    int n=arr.size(), wsum=0, maxSum=INT_MIN;
    // Build first window
    for(int i=0; i<k; i++) wsum += arr[i];
    maxSum = wsum;
    // Slide: add new tail, remove old head
    for(int i=k; i<n; i++){
        wsum += arr[i] - arr[i-k];
        maxSum = max(maxSum, wsum);
    }
    return maxSum;
}

P37 Minimum Difference in K Elements Sorting + Window

Given an array and integer K, find the minimum difference between the max and min of any K elements chosen from the array.

Key Insight — Sort First

After sorting, the K elements with minimum range must be contiguous in the sorted array. Any non-contiguous K elements would have a larger or equal spread. So sort, then slide a window of size K and track a[i+K-1] − a[i].

Array=[10,100,300,200,1000,20,30], K=3
Sorted: [10, 20, 30, 100, 200, 300, 1000]

Window[10,20,30]:   300-10=20  → diff=20
Window[20,30,100]:  100-20=80
Window[30,100,200]: 200-30=170
Window[100,200,300]:300-100=200
...
Minimum difference = 20
int minDiffK(vector<int>& a, int k){
    sort(a.begin(), a.end());
    int minDiff = INT_MAX;
    for(int i=0; i+k-1<(int)a.size(); i++)
        minDiff = min(minDiff, a[i+k-1] - a[i]);
    return minDiff;
}
Insight & Complexity

Time: O(n log n) dominated by sort | Space: O(1). The sort + window trick appears constantly in problems asking about "optimal selection of K elements".

P38 Maximum Product of Two Numbers Single Pass

Find the maximum product of any two elements in an array. Important: two large-magnitude negatives may give the largest product.

Approach — Track Four Extremes

The answer is either (two largest positives) or (two most negative numbers). Track max1, max2 (two largest) and min1, min2 (two smallest) in one pass. Return max(max1×max2, min1×min2).

Array: [-10, -3, 5, 6, -2]
After pass: max1=6, max2=5, min1=-10, min2=-3
max1*max2 = 6*5   =  30
min1*min2 = (-10)*(-3) = 30  (tie, either is answer)
long long maxProduct(vector<int>& a){
    long long max1=LLONG_MIN, max2=LLONG_MIN;
    long long min1=LLONG_MAX, min2=LLONG_MAX;
    for(int x : a){
        if(x > max1)      { max2=max1; max1=x; }
        else if(x > max2) { max2=x; }
        if(x < min1)      { min2=min1; min1=x; }
        else if(x < min2) { min2=x; }
    }
    return max(max1*max2, min1*min2);
}
Insight & Complexity

Time: O(n) | Space: O(1). The naive O(n²) nested loop works for small n, but this single-pass approach is the one to demonstrate in interviews.

P39 Find the Odd-Occurring Element XOR Logic

Every element in the array appears an even number of times except one. Find that element in O(n) time and O(1) space.

XOR Magic

XOR of a number with itself is 0. XOR of any number with 0 is the number itself. XOR all elements together — all even-frequency pairs cancel out, leaving only the odd-count element.

Array: [2, 3, 5, 3, 2]
Start: xor=0
xor ^ 2 = 2
xor ^ 3 = 1  (2^3 = 01^10 = 11 = 3... wait: 2=010, 3=011, XOR=001=1)
xor ^ 5 = 4  (1^5 = 001^101 = 100 = 4)
xor ^ 3 = 7  (4^3 = 100^011 = 111 = 7)
xor ^ 2 = 5  (7^2 = 111^010 = 101 = 5)
Answer: 5 (appears once; 2 and 3 each appear twice and cancel)
int oddOccurring(vector<int>& a){
    int result=0;
    for(int x : a) result ^= x;
    return result;
}
Insight & Complexity

Time: O(n) | Space: O(1). This is one of the most elegant tricks in programming. XOR is commutative and associative — order doesn't matter, all pairs cancel regardless of position.

P40 Extract All Integers from a Mixed String Parsing

Given a string like "ab12cd34ef5", extract all integers: [12, 34, 5].

Approach — Digit State Machine

Walk the string character by character. When you hit a digit, keep consuming digits and build the number (num = num×10 + digit). When you hit a non-digit after accumulating, save the number and reset.

"ab12cd34"
a → skip
b → skip
1 → digit, num=1
2 → digit, num=12
c → non-digit, save 12, reset num=0
d → skip
3 → digit, num=3
4 → digit, num=34
end → save 34
Result: [12, 34]
vector<int> extractIntegers(string s){
    vector<int> nums;
    int i=0, n=s.size();
    while(i < n){
        if(isdigit(s[i])){
            int num=0;
            while(i<n && isdigit(s[i]))
                num = num*10 + (s[i++]-'0');
            nums.push_back(num);
        } else i++;
    }
    return nums;
}
Insight & Complexity

Time: O(n) | Space: O(k) for k numbers found. The inner while loop never backtracks — the total work across both loops is still O(n).

↑ back to top
Chapter 05
String Problems — Frequency, Parsing, Manipulation & Classic Checks

P41 Remove Spaces Beginner

Remove all space characters from a string. "hello world""helloworld".

"hello world"
h e l l o   w o r l d
         ↑ skip space
Result: "helloworld"
string removeSpaces(string s){
    string res;
    for(char c : s)
        if(c != ' ') res += c;
    return res;
}
// In-place STL: s.erase(remove(s.begin(),s.end(),' '),s.end());
Insight & Complexity

Time: O(n) | Space: O(n) for result. The erase-remove idiom is a classic C++ pattern — remove shifts non-space chars to the front and returns an iterator to the new end; erase then trims the tail.

P42 Remove Vowels Beginner

Remove all vowels (a,e,i,o,u) from a string, case-insensitive. "Hello World""Hll Wrld".

"Hello"
H → not vowel, keep
e → vowel, skip
l → keep
l → keep
o → vowel, skip
Result: "Hll"
string removeVowels(string s){
    string vowels = "aeiouAEIOU";
    string res;
    for(char c : s)
        if(vowels.find(c) == string::npos) res += c;
    return res;
}
Insight & Complexity

Time: O(n) | Space: O(n). For performance-critical code, use an unordered_set<char> for O(1) lookup instead of linear string::find.

P43 Reverse Only the Vowels Two Pointer

Reverse only the vowel characters in a string, leaving all other characters in place. "hello""holle".

Approach — Two Pointers, Skip Non-Vowels

Left pointer starts at 0, right at end. Move left forward past consonants. Move right backward past consonants. When both are on vowels, swap them and advance both inward.

"hello" → vowels at idx 1(e), 4(o)
l=0 'h' → not vowel, l++
l=1 'e' → vowel ✓    r=4 'o' → vowel ✓
swap → "holle"
l=2, r=3: l < r but no more vowels inside
Result: "holle"
string reverseVowels(string s){
    auto isVowel = [](char c){
        return string("aeiouAEIOU").find(c) != string::npos;
    };
    int l=0, r=(int)s.size()-1;
    while(l < r){
        while(l<r && !isVowel(s[l])) l++;
        while(l<r && !isVowel(s[r])) r--;
        if(l < r) swap(s[l++], s[r--]);
    }
    return s;
}
Insight & Complexity

Time: O(n) | Space: O(1) in-place. The inner while loops only ever advance pointers forward — total work across both is still O(n), not O(n²).

P44 Count Character Types Frequency

Count uppercase letters, lowercase letters, digits, and special characters in a string.

Input"Hello W0rld!"
OutputUp=2 Lo=7 Dig=1 Sp=1
void countCharTypes(string s){
    int up=0, lo=0, dig=0, sp=0;
    for(char c : s){
        if     (isupper(c)) up++;
        else if(islower(c)) lo++;
        else if(isdigit(c)) dig++;
        else                sp++;
    }
    cout << "Upper="   << up
         << " Lower="  << lo
         << " Digits=" << dig
         << " Special="<< sp << "\n";
}
Insight & Complexity

Time: O(n) | Space: O(1). isupper, islower, isdigit are from <cctype>. Pass them (unsigned char)c to be safe with signed char implementations.

P45 Maximum Occurring Character Frequency

Find the character that appears most frequently in a string. On a tie, return the lexicographically smallest.

Approach — Frequency Array

Use an int array of size 256 (all ASCII values). Count each character. Then scan 0→255 to find the maximum frequency. Because we scan in ASCII order (A before B, a before b…), the first maximum found is automatically the lexicographically smallest.

"aababc"
freq['a']=3, freq['b']=2, freq['c']=1
Scan 0→255: first max is 'a' (freq=3)
Result: 'a'
char maxOccChar(string s){
    int freq[256] = {0};
    for(char c : s) freq[(unsigned char)c]++;
    int maxF=0; char res='\0';
    for(int i=0; i<256; i++){
        if(freq[i] > maxF){
            maxF = freq[i];
            res  = (char)i;
        }
    }
    return res;
}
Insight & Complexity

Time: O(n + 256) = O(n) | Space: O(1) — 256 is a constant. Scanning in ASCII order gives tie-breaking for free, eliminating the need for a secondary comparison.

P46 First Repeating Character Frequency

Find the first character in a string that appears more than once. "abcdaef"'a'.

Two-Pass Approach

Pass 1: Build frequency array. Pass 2: Walk the string again left to right — the first character with frequency > 1 is the answer. This preserves the order of first occurrence.

"abcba"
freq: a=2, b=2, c=1
Walk: a → freq[a]=2 > 1  →  Answer: 'a'
char firstRepeating(string s){
    int freq[256] = {0};
    for(char c : s) freq[(unsigned char)c]++;
    for(char c : s)
        if(freq[(unsigned char)c] > 1) return c;
    return '\0';   // no repeating character
}
Insight & Complexity

Time: O(n) | Space: O(1). Do not return in the first pass — you need the full frequency table built before you can judge "first occurrence".

P47 Uncommon Characters Between Two Strings Set Difference

Find all characters present in exactly one of the two strings (not in both). "aab" and "bba" → only 'a' appears in one not the other (actually both have 'a' and 'b', result is "" for this). Better example: "abc" and "bcd""ad".

"abc" vs "bcd"
fa: a=1, b=1, c=1   fb: b=1, c=1, d=1
'a': fa>0, fb==0  →  include 'a'
'b': both >0      →  skip
'c': both >0      →  skip
'd': fa==0, fb>0  →  include 'd'
Result: "ad"
string uncommonChars(string a, string b){
    int fa[26]={0}, fb[26]={0};
    for(char c : a) fa[c-'a']++;
    for(char c : b) fb[c-'a']++;
    string res;
    for(int i=0; i<26; i++)
        if((fa[i]>0) != (fb[i]>0))  // XOR: one but not both
            res += (char)('a'+i);
    return res;
}
Insight & Complexity

Time: O(n+m+26) = O(n+m) | Space: O(1). The condition (fa[i]>0) != (fb[i]>0) is boolean XOR — true only when exactly one side has the character.

P48 Remove Consecutive Duplicates String Logic

Remove consecutive duplicate characters. "aabbcc""abc". "aababab""ababab" (only consecutive runs are collapsed).

"aabbcc"
res="a"
s[1]='a' == s[0]='a'  →  skip
s[2]='b' != s[1]='a'  →  append 'b'
s[3]='b' == s[2]='b'  →  skip
s[4]='c' != s[3]='b'  →  append 'c'
s[5]='c' == s[4]='c'  →  skip
Result: "abc"
string removeConsDups(string s){
    if(s.empty()) return s;
    string res;
    res += s[0];
    for(int i=1; i<(int)s.size(); i++)
        if(s[i] != s[i-1]) res += s[i];
    return res;
}
Insight & Complexity

Time: O(n) | Space: O(n). Compare each character only with its immediate predecessor — this is the minimal comparison needed to detect a consecutive run.

P49 Count Words in camelCase String String Logic

Count the number of words encoded in a camelCase string. "helloWorldFoo" → 3.

Logic

The first character always starts a word. Every uppercase letter after position 0 starts a new word. Count = 1 + (number of uppercase letters in positions 1 to n−1).

"helloWorldFoo"
Start count=1
pos 5: 'W' is uppercase  →  count=2
pos 10: 'F' is uppercase →  count=3
Result: 3 words
int camelCaseCount(string s){
    if(s.empty()) return 0;
    int cnt=1;
    for(int i=1; i<(int)s.size(); i++)
        if(isupper(s[i])) cnt++;
    return cnt;
}
Insight & Complexity

Time: O(n) | Space: O(1). Pattern: when a property change (uppercase transition) marks a boundary, count boundaries + 1.

P50 Is Digit-Sum a Palindrome? Compose Logic

Compute the digit sum of n, then check if that sum (as a string) is itself a palindrome. Example: n=121 → sum=4 → "4" → Yes. n=199 → sum=19 → "19" → No.

n = 1991
digit sum = 1+9+9+1 = 20
"20" reversed = "02"
"20" != "02"  →  Not palindrome

n = 10001
digit sum = 1+0+0+0+1 = 2
"2" reversed = "2"
Palindrome ✓
bool digitSumPalindrome(int n){
    int sum=0, tmp=abs(n);
    while(tmp){ sum += tmp%10; tmp /= 10; }
    string s = to_string(sum);
    string rev = s;
    reverse(rev.begin(), rev.end());
    return s == rev;
}
Insight & Complexity

Time: O(d) | Space: O(d). This problem demonstrates function composition — break it into two clean sub-problems: compute digit sum, then check palindrome.

P51 Characters Vector to String Beginner

Given a vector<char> like ['h','e','l','l','o'], build the string "hello".

string charsToString(vector<char>& chars){
    return string(chars.begin(), chars.end());
}
// Equivalently: string(chars.data(), chars.size())
// Or: for(char c:chars) s+=c;
Insight & Complexity

Time: O(n) | Space: O(n). The range constructor string(begin, end) works for any iterator pair over char — cleaner than a manual loop.

P52 Merge Two Strings Alternately Beginner

Interleave two strings character by character. Append the remaining part of the longer string at the end. "abc" + "xy""axbyc".

"abc" + "xy"
i=0,j=0: 'a','x'  →  "ax"
i=1,j=1: 'b','y'  →  "axby"
i=2:     'c' only →  "axbyc"
Result: "axbyc"
string mergeAlternate(string a, string b){
    string res;
    int i=0, j=0;
    while(i<(int)a.size() || j<(int)b.size()){
        if(i < (int)a.size()) res += a[i++];
        if(j < (int)b.size()) res += b[j++];
    }
    return res;
}
Insight & Complexity

Time: O(n+m) | Space: O(n+m). The two if statements (not if/else) ensure both characters are appended in the same iteration when both strings still have characters.

P53 Longest Name from a List Beginner

Given a list of names, find the longest one. On a tie in length, return the lexicographically smallest.

Custom Comparator Logic

Primary sort key: length (longer wins). Secondary key: lexicographic order (smaller wins on tie). Encode this in a lambda comparator for max_element.

["Alice", "Bob", "Charlie", "Dave", "Eve"]
Lengths: 5, 3, 7, 4, 3
Max length = 7 → "Charlie"
No tie → Answer: "Charlie"
int n; cin>>n;
vector<string> names(n);
for(auto& s : names) cin>>s;

auto best = max_element(names.begin(), names.end(),
    [](const string& a, const string& b){
        if(a.size() != b.size()) return a.size() < b.size();
        return a > b;   // lex smaller wins tie, so b "beats" a when a > b
    });
cout << *best << "\n";
Insight & Complexity

Time: O(n) | Space: O(1). max_element's comparator means "is a less than b?" — so returning a > b on a tie means the lexicographically smaller string is "greater" and wins.

↑ back to top
Chapter 06
Pattern Printing — Systematic Visual Logic

The Universal Pattern Method Must Read First

3 Questions — Answer These, Two Loops Write Themselves

For each row i (1 to n), ask:
1. How many leading spaces?
2. How many characters to print?
3. What is each character — star, row number, column number, a formula?

Once you answer these three questions on paper, the nested for loops follow directly. Never start coding a pattern without this analysis.

P54 Right Triangle & Inverted Triangle Beginner

Print a right-angled triangle of stars growing from 1 to n rows, then the inverted version shrinking from n to 1.

Right Triangle (n=4)     |     Inverted Triangle (n=4)
*               |  * * * *
* *             |  * * *
* * *           |  * *
* * * *         |  *

Row i: 0 spaces, i stars  |  Row i: 0 spaces, (n−i+1) stars
// Right Triangle
for(int i=1; i<=n; i++){
    for(int j=1; j<=i; j++) cout << "* ";
    cout << "\n";
}

// Inverted Triangle
for(int i=n; i>=1; i--){
    for(int j=1; j<=i; j++) cout << "* ";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). The outer loop controls rows; the inner loop controls columns. Inversion is just reversing the outer loop direction — the inner loop formula stays the same.

P55 Pyramid & Diamond Intermediate Pattern

Print a centered pyramid, then a full diamond (pyramid + inverted pyramid joined at the widest row).

Pyramid (n=4)
   *           Row i: (n−i) spaces,  (2i−1) stars
  ***
 *****
*******
Diamond (n=4) — pyramid rows 1..n then n-1..1
   *
  ***
 *****
*******
 *****
  ***
   *
// Pyramid
for(int i=1; i<=n; i++){
    for(int s=1; s<=n-i; s++) cout << " ";
    for(int j=1; j<=2*i-1; j++) cout << "*";
    cout << "\n";
}

// Diamond = Pyramid top + Pyramid bottom
for(int i=1; i<=n; i++){                     // top half
    for(int s=1; s<=n-i; s++) cout << " ";
    for(int j=1; j<=2*i-1; j++) cout << "*";
    cout << "\n";
}
for(int i=n-1; i>=1; i--){                  // bottom half
    for(int s=1; s<=n-i; s++) cout << " ";
    for(int j=1; j<=2*i-1; j++) cout << "*";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). Spaces = n−i, stars = 2i−1. Memorise this pair — it's the foundation of every centered pattern. The diamond is just two pyramids sharing no common row.

P56 Half Diamond Pattern

Print a half diamond — a right triangle growing from 1 to n rows, then shrinking back from n−1 to 1.

Half Diamond (n=4)
*
* *
* * *
* * * *
* * *
* *
*
for(int i=1; i<=n; i++){                  // grow
    for(int j=1; j<=i; j++) cout << "* ";
    cout << "\n";
}
for(int i=n-1; i>=1; i--){              // shrink
    for(int j=1; j<=i; j++) cout << "* ";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). The total rows printed is 2n−1. The bottom half starts at i=n−1 (not n) to avoid printing the widest row twice.

P57 Floyd's Triangle Beginner

Print Floyd's triangle: a right-angled triangle where cells are filled with consecutive integers starting from 1. Row i has i numbers.

Floyd's Triangle (n=4)
1
2  3
4  5  6
7  8  9  10
int num=1;
for(int i=1; i<=n; i++){
    for(int j=1; j<=i; j++) cout << num++ << " ";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). The running counter num is independent of the loop variables — it just increments every time an inner-loop cell is printed.

P58 Number Patterns — Two Variations Number Patterns

Pattern 1: each row i prints numbers 1 to i. Pattern 2: each row i prints numbers i to n (descending width).

Pattern 1 (n=4)     |     Pattern 2 (n=4)
1              |  1 2 3 4
1 2            |  1 2 3
1 2 3          |  1 2
1 2 3 4        |  1

Row i: j from 1..i  |  Row i: j from 1..(n−i+1)
// Pattern 1: 1..i per row
for(int i=1; i<=n; i++){
    for(int j=1; j<=i; j++) cout << j << " ";
    cout << "\n";
}

// Pattern 2: 1..(n-i+1) per row (inverted width, content starts at 1)
for(int i=1; i<=n; i++){
    for(int j=1; j<=n-i+1; j++) cout << j << " ";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). Keep track of two things separately: how many numbers per row (loop bound) and what value to print (j, or a formula). Conflating them is the most common pattern bug.

P59 Alphabet Triangle & Vowel Pyramid Letter Patterns

Print a letter triangle (A, AB, ABC…) and a vowel pyramid (A, AE, AEI…) centered.

Letter Triangle (n=4)    |    Vowel Pyramid (n=5, only 5 vowels)
A               |      A
A B             |     A E
A B C           |    A E I
A B C D         |   A E I O
                |  A E I O U
// Letter Triangle: row i prints first i letters
for(int i=1; i<=n; i++){
    for(int j=0; j<i; j++) cout << (char)('A'+j) << " ";
    cout << "\n";
}

// Vowel Pyramid
string vowels = "AEIOU";
for(int i=1; i<=5; i++){
    for(int s=0; s<5-i; s++) cout << " ";
    for(int j=0; j<i; j++) cout << vowels[j] << " ";
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). Character arithmetic: 'A' + j gives the j-th uppercase letter. Index into a vowel string for non-sequential characters like AEIOU.

P60 Hollow Square & Hollow Rectangle Advanced Pattern

Print a hollow square of size n — stars on the border, spaces inside. Then a hollow rectangle of r rows and c columns.

Hollow Square (n=5)
* * * * *
*       *
*       *
*       *
* * * * *

Print * only if: first row, last row, first col, or last col
// Hollow Square
for(int i=1; i<=n; i++){
    for(int j=1; j<=n; j++){
        if(i==1 || i==n || j==1 || j==n)
            cout << "* ";
        else
            cout << "  ";
    }
    cout << "\n";
}

// Hollow Rectangle (r rows, c cols)
for(int i=1; i<=r; i++){
    for(int j=1; j<=c; j++){
        if(i==1 || i==r || j==1 || j==c)
            cout << "* ";
        else
            cout << "  ";
    }
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). The condition i==1 || i==n || j==1 || j==n is the canonical "border check". This same logic generalises to hollow triangles, hollow diamonds — always think: "when am I on the border?"

P61 Sandglass & X Pattern Advanced Pattern

Print a sandglass (inverted pyramid joined to an upright pyramid at the tip). Also print an X pattern where stars appear only on the two diagonals.

Sandglass (n=4)          |     X Pattern (n=5)
* * * * * * *    |  *       *
  * * * * *      |    *   *
    * * *        |      *
      *          |    *   *
    * * *        |  *       *
  * * * * *
* * * * * * *

Sandglass row i (top half): (n−i) spaces, (2i−1) stars  [i: n→1]
Bottom half mirrors, starting at 2           [i: 2→n]
X: print * if j==i  OR  j==n−i+1
// Sandglass
for(int i=n; i>=1; i--){              // top half (shrinking)
    for(int s=0; s<n-i; s++) cout << " ";
    for(int j=0; j<2*i-1; j++) cout << "*";
    cout << "\n";
}
for(int i=2; i<=n; i++){              // bottom half (growing, skip tip row)
    for(int s=0; s<n-i; s++) cout << " ";
    for(int j=0; j<2*i-1; j++) cout << "*";
    cout << "\n";
}

// X Pattern (print * on either diagonal)
for(int i=1; i<=n; i++){
    for(int j=1; j<=n; j++){
        if(j==i || j==n-i+1) cout << "* ";
        else                  cout << "  ";
    }
    cout << "\n";
}
Insight & Complexity

Time: O(n²) | Space: O(1). X pattern tip: for an odd n, when i==n/2+1 both conditions j==i and j==n−i+1 are true simultaneously — that's the center cross point. It prints one star correctly.

↑ back to top