Skip to main content

Introduction to Sequential Data(Arrays & Strings)

Description

In programming, we often need to store and work with collections of data rather than individual values. Sequential data structures are collections where elements are stored one after another in a specific, predictable order. The two most foundational sequential data structures are Arrays and Strings.

Arrays let you store multiple values of the same type under a single name, arranged in consecutive memory positions. Instead of creating separate variables for each item (like score1, score2, score3), you can group them into one array (scores) and access any element instantly by its position number (called an index).

Strings are sequences of characters — essentially specialized arrays where every element is a character (a letter, digit, symbol, or whitespace). Strings represent text: names, sentences, file paths, DNA sequences, and more.

Understanding arrays and strings is the first step in learning data structures because nearly every other structure (stacks, queues, hash maps, trees) either uses arrays internally or operates on similar sequential principles.

Examples

Example 1 — Integer Array

Scenario: Store the daily temperatures for a week.

Array: temps = [72, 75, 68, 70, 73, 80, 77]

Explanation: The array temps holds 7 integer values. Each value sits at a numbered position:

  • temps[0] = 72 (Monday)
  • temps[1] = 75 (Tuesday)
  • temps[2] = 68 (Wednesday)
  • ... and so on up to temps[6] = 77 (Sunday)

Indexing starts at 0, not 1. This is a universal convention in most programming languages.

Example 2 — String as a Character Sequence

Scenario: Store the word "HELLO".

String: greeting = "HELLO"

Explanation: Under the hood, greeting is a sequence of 5 characters:

  • greeting[0] = 'H'
  • greeting[1] = 'E'
  • greeting[2] = 'L'
  • greeting[3] = 'L'
  • greeting[4] = 'O'

Just like an array, each character has an index. The key difference from a general array is that strings are often immutable in languages like Python, Java, and JavaScript — meaning once created, individual characters cannot be changed in place.

Example 3 — Accessing and Modifying an Array

Input: arr = [10, 20, 30, 40, 50]

Operation: Change the element at index 2 to 99.

Result: arr = [10, 20, 99, 40, 50]

Explanation: Arrays support random access — you can jump directly to any index in constant time O(1). We went straight to position 2 and replaced 30 with 99 without touching any other element.

Constraints

This is a conceptual topic. There is no single problem with fixed constraints. However, the following general constraints apply to arrays and strings in competitive programming and real-world applications:

  • Array sizes typically range from 0 to 10^6 (or 10^7 in some cases)
  • String lengths typically range from 0 to 10^5 or 10^6
  • Array elements can be integers, floating-point numbers, characters, or references to objects
  • Strings in most languages use ASCII (256 characters) or Unicode
  • Fixed-size arrays have a predetermined length that cannot change after creation
  • Dynamic arrays (vectors, lists) can grow and shrink at runtime
  • Strings are immutable in Python, Java, JavaScript, and C# — but mutable in C and C++

Editorial

What Is an Array?

Intuition

Think of an array as a row of numbered lockers in a school hallway. Each locker has a fixed position (its index), and inside each locker you can store exactly one item of the same type — say, a textbook. If you know the locker number, you can walk straight to it and open it instantly. You do not need to check every locker before it.

This is the core superpower of an array: random access in O(1) time. Give it the index, and it hands you the value immediately. This is possible because all elements (or their references) are stored at contiguous memory locations — the computer can calculate the exact memory address of any element using a simple formula:

address(arr[i]) = base_address + i × size_of_each_element

Arrays come in two flavors:

  • Fixed-size arrays: You declare the size upfront, and it cannot change. Examples include C/C++ native arrays and Java primitive arrays.
  • Dynamic arrays: They resize automatically when you add more elements than the current capacity. Examples include C++ vector, Python list, and Java ArrayList.

Step-by-Step Explanation

Let's walk through how an array stores and accesses data using arr = [10, 20, 30, 40, 50]:

Step 1: When you create the array, the system allocates a contiguous block of memory large enough to hold 5 elements. Each slot is the same size (e.g., 4 bytes for an integer).

Step 2: The elements are placed sequentially:

  • Index 0 → memory address 1000 → stores 10
  • Index 1 → memory address 1004 → stores 20
  • Index 2 → memory address 1008 → stores 30
  • Index 3 → memory address 1012 → stores 40
  • Index 4 → memory address 1016 → stores 50

Step 3: To access arr[3], the computer computes: 1000 + 3 × 4 = 1012, and directly reads the value 40 from that address. No scanning required — this is O(1).

Step 4: To update arr[2] = 99, the computer computes: 1000 + 2 × 4 = 1008, and writes 99 at that address. The array becomes [10, 20, 99, 40, 50].

Step 5: To insert a new element at index 1, every element from index 1 onward must shift right by one position to make room. This means indices 1→2, 2→3, 3→4, 4→5 all shift. Then 25 is placed at index 1. This takes O(n) time because of the shifting.

Step 6: To delete the element at index 2, every element from index 3 onward shifts left by one position. This is also O(n) time.

Array Memory Layout and Access — Watch how an array stores elements in contiguous memory and how accessing, updating, and inserting operations work step by step.

Algorithm

Key Array Operations and Their Time Complexities:

  1. Access by index → O(1): Compute address = base + index × element_size
  2. Update by index → O(1): Jump to address, overwrite value
  3. Search (unsorted) → O(n): Must check each element linearly
  4. Search (sorted) → O(log n): Binary search halves the search space each step
  5. Insert at end → O(1) amortized for dynamic arrays; O(1) if space exists for fixed arrays
  6. Insert at position → O(n): Shift all subsequent elements right
  7. Delete at position → O(n): Shift all subsequent elements left
  8. Delete at end → O(1): Simply reduce the logical size

Code

#include <iostream>
#include <vector>
using namespace std;

int main() {
    // Fixed-size array
    int fixedArr[5] = {10, 20, 30, 40, 50};
    
    // Access element at index 2
    cout << "fixedArr[2] = " << fixedArr[2] << endl; // 30
    
    // Update element at index 2
    fixedArr[2] = 99;
    cout << "After update, fixedArr[2] = " << fixedArr[2] << endl; // 99
    
    // Dynamic array (vector)
    vector<int> dynamicArr = {10, 20, 30, 40, 50};
    
    // Append to end - O(1) amortized
    dynamicArr.push_back(60);
    
    // Insert at position 1 - O(n)
    dynamicArr.insert(dynamicArr.begin() + 1, 25);
    
    // Delete at position 3 - O(n)
    dynamicArr.erase(dynamicArr.begin() + 3);
    
    // Traverse and print
    cout << "Dynamic array: ";
    for (int val : dynamicArr) {
        cout << val << " ";
    }
    cout << endl;
    
    // Size of array
    cout << "Size: " << dynamicArr.size() << endl;
    
    return 0;
}
# Python uses lists as dynamic arrays
arr = [10, 20, 30, 40, 50]

# Access element at index 2 - O(1)
print(f"arr[2] = {arr[2]}")  # 30

# Update element at index 2 - O(1)
arr[2] = 99
print(f"After update, arr[2] = {arr[2]}")  # 99

# Append to end - O(1) amortized
arr.append(60)

# Insert at position 1 - O(n)
arr.insert(1, 25)

# Delete at position 3 - O(n)
del arr[3]

# Traverse and print
print(f"Array: {arr}")

# Size of array
print(f"Size: {len(arr)}")

# Negative indexing (Python-specific)
print(f"Last element: {arr[-1]}")
print(f"Second to last: {arr[-2]}")
import java.util.ArrayList;
import java.util.Arrays;

public class ArrayIntro {
    public static void main(String[] args) {
        // Fixed-size array
        int[] fixedArr = {10, 20, 30, 40, 50};
        
        // Access element at index 2 - O(1)
        System.out.println("fixedArr[2] = " + fixedArr[2]); // 30
        
        // Update element at index 2 - O(1)
        fixedArr[2] = 99;
        System.out.println("After update: " + fixedArr[2]); // 99
        
        // Dynamic array (ArrayList)
        ArrayList<Integer> dynamicArr = new ArrayList<>(Arrays.asList(10, 20, 30, 40, 50));
        
        // Append to end - O(1) amortized
        dynamicArr.add(60);
        
        // Insert at position 1 - O(n)
        dynamicArr.add(1, 25);
        
        // Delete at position 3 - O(n)
        dynamicArr.remove(3);
        
        // Traverse and print
        System.out.println("Dynamic array: " + dynamicArr);
        
        // Size
        System.out.println("Size: " + dynamicArr.size());
    }
}

Complexity Analysis

Array Operation Complexities:

OperationTime ComplexityWhy
Access by indexO(1)Direct address calculation: base + index × size
Update by indexO(1)Same address calculation, then overwrite
Search (unsorted)O(n)Must scan each element in the worst case
Insert at endO(1) amortizedDynamic arrays occasionally resize (copy all elements to a new, larger block), but this happens infrequently enough that the average cost per insertion is O(1)
Insert in middleO(n)All elements after the insertion point must shift right
Delete from middleO(n)All elements after the deletion point must shift left

Space Complexity: O(n) — an array of n elements requires space proportional to n.

Why contiguous memory matters: Because elements sit side by side in RAM, accessing sequential elements is extremely fast due to CPU cache lines. The processor loads a chunk of nearby memory into cache, so iterating through an array benefits from this cache locality — a performance advantage that linked structures (like linked lists) do not have.

What Is a String?

Intuition

A string is simply an array of characters with some special properties. Think of a string as a sentence written on a strip of paper where each square holds exactly one character.

The key distinction between strings and general arrays is that strings operate on a very small element set. A lowercase English string uses only 26 possible characters. ASCII covers 256 characters. This small alphabet size enables powerful optimizations:

  • Frequency counting with fixed-size arrays of 26 or 256 slots
  • Hashing based on character codes for fast lookups
  • Sorting in O(n) time using counting sort instead of comparison-based O(n log n)

Another critical property: in many languages (Python, Java, JavaScript, C#), strings are immutable. Once you create a string, you cannot change any character in place. Any "modification" actually creates a brand-new string. This has important performance implications — concatenating strings in a loop can be O(n²) if done naively because each concatenation creates a new copy.

In C and C++, strings are mutable — you can modify characters in place using character arrays or the std::string class.

Step-by-Step Explanation

Let's explore how strings work using s = "HELLO":

Step 1: The string is stored as a character array: ['H', 'E', 'L', 'L', 'O']. In C/C++, there is also a null terminator '\0' at the end, making the internal array ['H', 'E', 'L', 'L', 'O', '\0'].

Step 2: Accessing s[1] returns 'E' in O(1) time, identical to array access.

Step 3: Getting the length: len(s) returns 5 in Python (O(1) since the length is cached), s.length() in C++/Java.

Step 4: Concatenation — s + " WORLD" produces a new string "HELLO WORLD". In Python/Java, the original s is unchanged because strings are immutable. A new 11-character string is allocated and both pieces are copied into it.

Step 5: Substring — s[1:4] in Python returns "ELL" (characters at indices 1, 2, 3). This creates a new string object.

Step 6: Comparison — "HELLO" == "HELLO" compares character by character: H==H, E==E, L==L, L==L, O==O. All match, so the result is true. String comparison is O(n) where n is the length of the shorter string.

String as a Character Sequence — Watch how a string stores characters sequentially, how indexing works, and how common operations like concatenation and substring create new strings.

Algorithm

Key String Operations and Their Time Complexities:

  1. Access character by index → O(1): Direct address calculation
  2. Get length → O(1): Length is typically stored/cached (O(n) in C using strlen)
  3. Concatenation → O(n + m): Must create a new string and copy both originals
  4. Substring extraction → O(k): Copy k characters into a new string
  5. Comparison → O(min(n, m)): Compare character by character until mismatch or end
  6. Search for character → O(n): Linear scan
  7. Search for pattern → O(n × m) naive; O(n + m) with KMP or Rabin-Karp
  8. Reverse → O(n): Visit each character once
  9. Convert case → O(n): Process each character

Code

#include <iostream>
#include <string>
using namespace std;

int main() {
    // String creation
    string s = "HELLO";
    
    // Access character at index 1 - O(1)
    cout << "s[1] = " << s[1] << endl; // 'E'
    
    // Length - O(1)
    cout << "Length: " << s.length() << endl; // 5
    
    // Strings ARE mutable in C++
    s[0] = 'J';
    cout << "After mutation: " << s << endl; // "JELLO"
    
    // Concatenation - O(n + m)
    string greeting = s + " WORLD";
    cout << "Concatenated: " << greeting << endl;
    
    // Substring - O(k)
    string sub = s.substr(1, 3); // from index 1, take 3 chars
    cout << "Substring: " << sub << endl; // "ELL"
    
    // Find character - O(n)
    size_t pos = s.find('L');
    cout << "First 'L' at index: " << pos << endl; // 2
    
    // Comparison - O(n)
    cout << "Equal? " << (s == "JELLO") << endl; // 1 (true)
    
    // Reverse
    string rev = string(s.rbegin(), s.rend());
    cout << "Reversed: " << rev << endl; // "OLLEJ"
    
    return 0;
}
# String creation
s = "HELLO"

# Access character at index 1 - O(1)
print(f"s[1] = {s[1]}")  # 'E'

# Length - O(1)
print(f"Length: {len(s)}")  # 5

# Strings are IMMUTABLE in Python
# s[0] = 'J'  # This would raise TypeError!

# To "modify", create a new string
s_modified = 'J' + s[1:]
print(f"Modified: {s_modified}")  # "JELLO"
print(f"Original unchanged: {s}")  # "HELLO"

# Concatenation - O(n + m)
greeting = s + " WORLD"
print(f"Concatenated: {greeting}")

# Substring (slicing) - O(k)
sub = s[1:4]
print(f"Substring: {sub}")  # "ELL"

# Find character - O(n)
pos = s.find('L')
print(f"First 'L' at index: {pos}")  # 2

# Comparison - O(n)
print(f"Equal? {s == 'HELLO'}")  # True

# Reverse - O(n)
rev = s[::-1]
print(f"Reversed: {rev}")  # "OLLEH"

# Efficient concatenation with join - O(total_length)
parts = ["Hello", " ", "World"]
result = "".join(parts)
print(f"Joined: {result}")
public class StringIntro {
    public static void main(String[] args) {
        // String creation
        String s = "HELLO";
        
        // Access character at index 1 - O(1)
        System.out.println("s.charAt(1) = " + s.charAt(1)); // 'E'
        
        // Length - O(1)
        System.out.println("Length: " + s.length()); // 5
        
        // Strings are IMMUTABLE in Java
        // Cannot modify characters in place
        
        // To "modify", use StringBuilder (mutable)
        StringBuilder sb = new StringBuilder(s);
        sb.setCharAt(0, 'J');
        String modified = sb.toString();
        System.out.println("Modified: " + modified); // "JELLO"
        System.out.println("Original: " + s); // "HELLO"
        
        // Concatenation - O(n + m)
        String greeting = s + " WORLD";
        System.out.println("Concatenated: " + greeting);
        
        // Substring - O(k)
        String sub = s.substring(1, 4);
        System.out.println("Substring: " + sub); // "ELL"
        
        // Find character - O(n)
        int pos = s.indexOf('L');
        System.out.println("First 'L' at: " + pos); // 2
        
        // Comparison - O(n)
        System.out.println("Equal? " + s.equals("HELLO")); // true
        
        // Reverse using StringBuilder - O(n)
        String rev = new StringBuilder(s).reverse().toString();
        System.out.println("Reversed: " + rev); // "OLLEH"
        
        // Efficient concatenation with StringBuilder
        StringBuilder builder = new StringBuilder();
        builder.append("Hello");
        builder.append(" ");
        builder.append("World");
        System.out.println("Built: " + builder.toString());
    }
}

Complexity Analysis

String Operation Complexities:

OperationTime ComplexityWhy
Access by indexO(1)Same as array — direct address calculation
Get lengthO(1)Stored as metadata in most languages (exception: C strlen is O(n))
ConcatenationO(n + m)Must allocate new string and copy both originals
SubstringO(k)Copy k characters to new string
ComparisonO(min(n, m))Character-by-character until mismatch or end
Search for characterO(n)Linear scan in worst case
ReverseO(n)Must visit every character

Space Complexity: O(n) for storing a string of length n.

Immutability trap: In Python/Java, building a string by repeated concatenation in a loop (s = s + char) is O(n²) because each concatenation creates a new copy. Use StringBuilder (Java) or "".join(list) (Python) for O(n) string building.

Arrays vs Strings — Key Differences

Intuition

While strings are built on top of arrays, there are important differences that affect how you use them in practice:

FeatureArrayString
Element typeAny single type (int, float, object, etc.)Characters only
MutabilityMutable in all languagesImmutable in Python, Java, JS, C#; mutable in C/C++
Element set sizeUnlimitedSmall (26 lowercase, 256 ASCII, ~150K Unicode)
Common optimizationsSorting, binary search, two pointersFrequency arrays, hashing, sliding window
Concatenation costAppend to end is O(1) amortizedCreates new string — O(n + m)
Null terminatorNot usedUsed in C/C++ to mark end of string

When to think "array": Numerical computations, sorting problems, sliding window on numbers, dynamic programming with numeric states.

When to think "string": Text processing, pattern matching, palindromes, anagrams, encoding/decoding, parsing.

When both apply: Many string problems use array techniques (two pointers on characters, sliding window on substrings, frequency counting with arrays of size 26).

Step-by-Step Explanation

Let's compare a practical operation — reversing — on both an array and a string to highlight the mutability difference:

Array reversal (in-place, mutable):

Step 1: Given arr = [1, 2, 3, 4, 5], set two pointers: left = 0, right = 4.

Step 2: Swap arr[0] and arr[4]: array becomes [5, 2, 3, 4, 1]. Move pointers: left = 1, right = 3.

Step 3: Swap arr[1] and arr[3]: array becomes [5, 4, 3, 2, 1]. Move pointers: left = 2, right = 2.

Step 4: Pointers meet — done. Array reversed in-place with O(1) extra space.

String reversal (Python — immutable, must create new):

Step 5: Given s = "ABCDE", we cannot swap characters in place.

Step 6: Instead, we build a new string by reading characters from right to left: s[::-1] produces "EDCBA". This requires O(n) extra space for the new string.

Step 7: The original string "ABCDE" still exists unchanged in memory.

Array vs String: In-Place Reversal Comparison — Watch how an array can be reversed in-place using two pointers (O(1) space), while a string must create a new copy (O(n) space) due to immutability.

Algorithm

Summary of Key Differences:

  1. Mutability: Arrays can be modified in place; strings cannot (in most languages)
  2. Element diversity: Arrays hold any type; strings hold only characters
  3. Small alphabet advantage: Strings benefit from frequency counting with fixed-size arrays
  4. Concatenation: Array append is O(1) amortized; string concatenation is O(n + m)
  5. Common patterns: Arrays use sorting, binary search, sliding window; strings add pattern matching, palindrome checks, and character frequency techniques
  6. Memory: Modifying arrays uses O(1) extra space; "modifying" strings requires O(n) for the new copy

Code

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    // === ARRAY: Mutable, in-place reversal ===
    vector<int> arr = {1, 2, 3, 4, 5};
    
    // Reverse in-place using two pointers
    int left = 0, right = arr.size() - 1;
    while (left < right) {
        swap(arr[left], arr[right]);
        left++;
        right--;
    }
    // arr is now {5, 4, 3, 2, 1} — modified in place
    
    cout << "Reversed array: ";
    for (int x : arr) cout << x << " ";
    cout << endl;
    
    // === STRING: Mutable in C++ (unlike Python/Java) ===
    string s = "ABCDE";
    
    // Can reverse in-place in C++
    reverse(s.begin(), s.end());
    cout << "Reversed string: " << s << endl; // "EDCBA"
    
    // Frequency counting using small alphabet
    string text = "hello world";
    int freq[26] = {0};
    for (char c : text) {
        if (c >= 'a' && c <= 'z') {
            freq[c - 'a']++;
        }
    }
    cout << "Frequency of 'l': " << freq['l' - 'a'] << endl; // 3
    
    return 0;
}
# === ARRAY (list): Mutable, in-place reversal ===
arr = [1, 2, 3, 4, 5]

# Reverse in-place using two pointers
left, right = 0, len(arr) - 1
while left < right:
    arr[left], arr[right] = arr[right], arr[left]
    left += 1
    right -= 1
# arr is now [5, 4, 3, 2, 1] — modified in place
print(f"Reversed array: {arr}")

# Or simply: arr.reverse() or arr[::-1]

# === STRING: Immutable in Python ===
s = "ABCDE"

# Cannot modify in place: s[0] = 'X'  → TypeError!
# Must create new string
reversed_s = s[::-1]
print(f"Reversed string: {reversed_s}")  # "EDCBA"
print(f"Original unchanged: {s}")  # "ABCDE"

# Frequency counting using small alphabet
text = "hello world"
freq = [0] * 26
for c in text:
    if c.isalpha():
        freq[ord(c.lower()) - ord('a')] += 1
print(f"Frequency of 'l': {freq[ord('l') - ord('a')]}")  # 3

# Or use Counter for convenience
from collections import Counter
print(Counter(text))  # {'l': 3, 'o': 2, ...}
import java.util.*;

public class ArrayVsString {
    public static void main(String[] args) {
        // === ARRAY: Mutable, in-place reversal ===
        int[] arr = {1, 2, 3, 4, 5};
        
        // Reverse in-place using two pointers
        int left = 0, right = arr.length - 1;
        while (left < right) {
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
        // arr is now {5, 4, 3, 2, 1}
        System.out.println("Reversed array: " + Arrays.toString(arr));
        
        // === STRING: Immutable in Java ===
        String s = "ABCDE";
        
        // Cannot modify in place
        // Must use StringBuilder for reversal
        String reversedS = new StringBuilder(s).reverse().toString();
        System.out.println("Reversed string: " + reversedS); // "EDCBA"
        System.out.println("Original unchanged: " + s); // "ABCDE"
        
        // Frequency counting using small alphabet
        String text = "hello world";
        int[] freq = new int[26];
        for (char c : text.toCharArray()) {
            if (Character.isLetter(c)) {
                freq[Character.toLowerCase(c) - 'a']++;
            }
        }
        System.out.println("Frequency of 'l': " + freq['l' - 'a']); // 3
    }
}

Complexity Analysis

Reversal Comparison:

AspectArray (Mutable)String (Immutable)
TimeO(n) — n/2 swapsO(n) — copy all characters
SpaceO(1) — swap in placeO(n) — new string required
Original modified?YesNo

Frequency Counting:

  • Time: O(n) where n is the length of the text — one pass through all characters
  • Space: O(1) — the frequency array is always size 26 (or 256 for ASCII), regardless of input size. This fixed-size auxiliary space is a direct benefit of the small character set.

Key takeaway: Arrays and strings share the same O(1) random access and O(n) traversal properties. The critical difference is mutability: arrays can be modified cheaply in place, while string modifications in most languages require creating new copies, costing extra memory.

Common Patterns and When to Use Each

Intuition

Now that you understand what arrays and strings are, here is a roadmap of the most common algorithmic patterns built on top of them. Each pattern exploits a specific property of sequential data:

Array Patterns:

  1. Two Pointers — Use two index variables moving toward each other or in the same direction. Exploits sorted order or structural symmetry. Example: finding a pair that sums to a target in a sorted array.
  2. Sliding Window — Maintain a window [left, right] that slides across the array. Exploits the ability to efficiently add/remove elements from the window edges. Example: longest substring without repeating characters.
  3. Prefix Sums — Precompute cumulative sums so any range sum can be answered in O(1). Exploits the additive property of contiguous elements.
  4. Sorting + Binary Search — Sort the array to enable O(log n) lookups. Trades O(n log n) preprocessing for fast queries.
  5. Kadane's Algorithm — Find the maximum sum subarray in O(n) by tracking the best sum ending at each position.

String Patterns:

  1. Frequency Counting — Use a size-26 (or 256) array to count character occurrences. Foundation for anagram checks, character matching, and histogram problems.
  2. Palindrome Detection — Use two pointers from both ends moving inward, or expand from center. Exploits character symmetry.
  3. Pattern Matching — KMP, Rabin-Karp, or Z-algorithm for finding a pattern within a text efficiently.
  4. StringBuilder Pattern — Accumulate characters in a mutable buffer to avoid O(n²) repeated concatenation.

All of these patterns will be covered in detail in subsequent lessons. Understanding arrays and strings is the prerequisite for all of them.

Step-by-Step Explanation

Here is a quick decision guide for choosing the right data structure:

Step 1: Is your data a sequence of characters (text, words, DNA)? → Use a string.

Step 2: Is your data a sequence of numbers, objects, or mixed types? → Use an array.

Step 3: Do you need frequent insertions/deletions in the middle? → Consider a linked list instead (O(1) insert/delete, but O(n) access).

Step 4: Do you need O(1) lookup by value (not index)? → Consider a hash set/map built on top of arrays.

Step 5: Do you need FIFO or LIFO ordering? → Use a queue or stack, which are typically implemented using arrays internally.

Step 6: Do you need to maintain sorted order with fast insert? → Consider a balanced BST or heap.

Arrays and strings are the building blocks. Nearly every other data structure is either built from arrays or solves a weakness of arrays (like slow middle insertion).

Algorithm

Summary — When to Use Arrays vs Strings:

  1. Use arrays when storing collections of numbers, objects, or data needing in-place modification
  2. Use strings when working with text, character sequences, or pattern-based problems
  3. Both support O(1) random access — this is their core strength
  4. Both suffer O(n) insertion/deletion in the middle — this is their core weakness
  5. Strings benefit from small alphabet optimizations (frequency arrays, hashing)
  6. Arrays benefit from sorting-based techniques (binary search, two pointers on sorted data)
  7. For mutable character manipulation in Python/Java, convert the string to a list/char array, modify, then convert back

Code

#include <iostream>
#include <vector>
#include <string>
using namespace std;

// Demonstrates: Traversal, the most fundamental operation
void traverseArray(vector<int>& arr) {
    cout << "Array traversal: ";
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i];
        if (i < arr.size() - 1) cout << ", ";
    }
    cout << endl;
}

void traverseString(const string& s) {
    cout << "String traversal: ";
    for (int i = 0; i < s.length(); i++) {
        cout << s[i];
        if (i < s.length() - 1) cout << ", ";
    }
    cout << endl;
}

int main() {
    vector<int> nums = {10, 20, 30, 40, 50};
    string text = "HELLO";
    
    traverseArray(nums);
    traverseString(text);
    
    // Both use the same index-based access pattern
    // Both iterate in O(n) time
    // The syntax is nearly identical — that's the beauty
    // of sequential data structures
    
    return 0;
}
# Demonstrates: Traversal, the most fundamental operation
def traverse_array(arr):
    print("Array traversal:", end=" ")
    for i in range(len(arr)):
        print(arr[i], end="")
        if i < len(arr) - 1:
            print(", ", end="")
    print()

def traverse_string(s):
    print("String traversal:", end=" ")
    for i in range(len(s)):
        print(s[i], end="")
        if i < len(s) - 1:
            print(", ", end="")
    print()

nums = [10, 20, 30, 40, 50]
text = "HELLO"

traverse_array(nums)
traverse_string(text)

# Pythonic traversal (for-each style)
for num in nums:
    print(num, end=" ")
print()

for char in text:
    print(char, end=" ")
print()

# Both use identical patterns — sequential data is sequential data
public class TraversalDemo {
    // Demonstrates: Traversal, the most fundamental operation
    public static void traverseArray(int[] arr) {
        System.out.print("Array traversal: ");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
            if (i < arr.length - 1) System.out.print(", ");
        }
        System.out.println();
    }
    
    public static void traverseString(String s) {
        System.out.print("String traversal: ");
        for (int i = 0; i < s.length(); i++) {
            System.out.print(s.charAt(i));
            if (i < s.length() - 1) System.out.print(", ");
        }
        System.out.println();
    }
    
    public static void main(String[] args) {
        int[] nums = {10, 20, 30, 40, 50};
        String text = "HELLO";
        
        traverseArray(nums);
        traverseString(text);
        
        // Enhanced for-each loop
        for (int num : nums) {
            System.out.print(num + " ");
        }
        System.out.println();
        
        for (char c : text.toCharArray()) {
            System.out.print(c + " ");
        }
        System.out.println();
    }
}

Complexity Analysis

Traversal Complexity (applies to both arrays and strings):

Time Complexity: O(n)

We visit each of the n elements exactly once. Each visit involves an O(1) access by index, so the total work is n × O(1) = O(n).

Space Complexity: O(1)

Traversal uses only a loop counter variable. No additional memory proportional to the input size is needed.

Final Summary of Complexities:

OperationArrayString (Immutable)
Access [i]O(1)O(1)
Update [i] = xO(1)O(n) — must create new string
AppendO(1) amortizedO(n) — must create new string
Insert at middleO(n)O(n)
Delete from middleO(n)O(n)
SearchO(n) unsorted, O(log n) sortedO(n)
TraverseO(n)O(n)
SpaceO(n)O(n)