Strings & Matrices
String immutability, UTF-8 vs UTF-16, row-major vs column-major matrix flattening, cache-friendly traversals, and sparse matrices.
Topics Covered:
22. Strings & Character Sequence Architecture • 23. Matrices, 2D Arrays & Memory Orderings (Row-Major vs Column-Major)
Character sequences and multi-dimensional matrices represent two vital linear abstractions mapped directly onto hardware memory buses. While strings translate binary code points into structured text with significant mutability and encoding considerations, matrices flatten multi-dimensional Cartesian coordinates into linear RAM addresses. This chapter explores character memory architectures, UTF-8 variable-width encodings, row-major versus column-major address calculations, cache line stride penalties, in-place matrix rotations, and staircase search paradigms.
Learning Objectives
#- Compute byte offsets and memory layouts for C-style null-terminated strings and modern length-prefixed string slice headers.
- Contrast ASCII, UTF-8, and UTF-16 encoding trade-offs and evaluate memory overheads of string immutability in runtime garbage collectors.
- Derive physical memory address equations for multi-dimensional arrays in row-major and column-major orderings.
- Quantify CPU cache line miss penalties caused by non-contiguous matrix traversals and design cache-optimal algorithms.
- Implement in-place matrix transpose, clockwise rotation, spiral traversal, and Young tableau staircase search.
Topic 22: Strings & Character Sequence Architecture
#1. Conceptual & Physical Memory Representation
At the physical hardware layer, a string is a specialized contiguous array of numeric integers representing character code points. The memory address of any character is calculated via pointer arithmetic:
Two primary architectural models govern how strings are demarcated in memory:
A. C-Style Null-Terminated Strings
C-style strings (char*) are unbroken arrays of 1-byte ASCII values terminated by a special sentinel byte: the null terminator ('\0', numerical value 0x00).
- Memory Footprint: A string of length requires physical bytes.
- Length Calculation: Determining string length requires scanning every byte until
'\0'is encountered, executing in time.
B. Modern Length-Prefixed Strings
Modern systems (Rust String, Go string, Java String, C++ std::string) store strings as a compact stack-allocated descriptor referencing a heap-allocated buffer.
- Descriptor Layout: Contains a pointer to the backing buffer (
8 bytes), explicit length (8 bytes), and capacity (8 bytes). - Length Calculation: Reading the length is an instant header inspection.
Memory Layout Comparison: Storing "DSA"
| Architectural Model | Location | Offset / Address | Value | Hex / Encoding | Semantic Role |
|---|---|---|---|---|---|
| C-Style String | Stack / Static | 'D' | 0x44 | Character byte 0 | |
'S' | 0x53 | Character byte 1 | |||
'A' | 0x41 | Character byte 2 | |||
'\0' | 0x00 | Sentinel Null Terminator | |||
| Length-Prefixed | Stack Frame | Offset | ptr | 0x7ffee0 | Pointer to heap storage |
| Offset | len | 3 | Explicit length counter ( access) | ||
| Offset | cap | 4 | Allocated buffer capacity | ||
| Heap Buffer | ptr + 0..2 | "DSA" | 0x44 0x53 0x41 | Contiguous character code units |
2. Character Encodings & Mutability Semantics
#Encoding Standards
- ASCII (7-bit): Maps integers to Latin characters, digits, and punctuation. Exactly 1 byte per character (highest bit unused).
- UTF-8 (Variable-Width, 1 to 4 bytes):
- ASCII characters () occupy exactly 1 byte (fully backward-compatible).
- Accented Latin, Greek, Arabic occupy 2 bytes.
- East Asian scripts (CJK), Indic scripts occupy 3 bytes.
- Emojis and historical symbols occupy 4 bytes.
- Implication: In UTF-8, string byte length does not equal character count! Random indexing is without an index translation table.
- UTF-16: Uses 2 bytes (or 4 bytes via surrogate pairs). Standard in Java and JavaScript runtimes.
Mutability vs. Immutability
- Mutable Strings (C++, C): In-place modification () is .
- Immutable Strings (Python, Java, JavaScript): Strings cannot be modified after allocation. Any transformation creates a new string object in heap memory ( time and space).
⚠️ The Repeated Concatenation Trap:
In immutable languages, concatenating characters in a loop (s = s + ch) copies the entire prefix at each step:**Remediation**: Always use a mutable `StringBuilder` or list of characters, then join once in time.
3. Core String Operations & Invariants
#| Operation | Description | Time Complexity | Auxiliary Space |
|---|---|---|---|
| Length() | Length-prefixed header read | ||
| Length() | C-style null-sentinel scan | ||
| CharAccess() | Fixed-width code unit lookup | ||
| Concatenate() | Allocate new buffer of size | ||
| Substring() | Extract range | ||
| In-Place Reverse() | Two-pointer symmetric swap |
Two-Pointer Reversal & Palindrome Verification
FUNCTION ReverseString(S: Array of Char, n: Integer) -> Void:
left <- 0
right <- n - 1
while left < right:
swap(S[left], S[right])
left <- left + 1
right <- right - 1
FUNCTION IsPalindrome(S: Array of Char, n: Integer) -> Boolean:
left <- 0
right <- n - 1
while left < right:
if S[left] != S[right]:
return False
left <- left + 1
right <- right - 1
return TrueStep-by-Step Two-Pointer Trace: Reversing "RADAR" ()
| Iteration | left | right | S[left] | S[right] | Action | Array State |
|---|---|---|---|---|---|---|
| 0 | 0 | 4 | 'R' | 'R' | Swap indices 0 and 4 | ['R', 'A', 'D', 'A', 'R'] |
| 1 | 1 | 3 | 'A' | 'A' | Swap indices 1 and 3 | ['R', 'A', 'D', 'A', 'R'] |
| 2 | 2 | 2 | 'D' | 'D' | left >= right Terminate | ['R', 'A', 'D', 'A', 'R'] |
Topic 23: Matrices, 2D Arrays & Memory Orderings
#1. Conceptual Foundations & Physical Address Linearization
A Matrix is a two-dimensional grid of rows and columns containing elements. Because physical computer RAM is strictly linear (one-dimensional byte addresses), a multi-dimensional array must be flattened into a 1D sequence using a deterministic mapping scheme.
Logical 2D View:
Col 0 Col 1 Col 2
Row 0: [ 10 , 20 , 30 ]
Row 1: [ 40 , 50 , 60 ]Mapping Equations: Row-Major vs. Column-Major
| Mapping Scheme | Language Ecosystem | Physical Storage Sequence | Addressing Formula for Element |
|---|---|---|---|
| Row-Major Order | C, C++, Java, Python, C# | Row 0 followed by Row 1: [10, 20, 30, 40, 50, 60] | |
| Column-Major Order | Fortran, MATLAB, Julia, R | Col 0, Col 1, Col 2: [10, 40, 20, 50, 30, 60] |
2. CPU Cache Locality & Stride Penalty Analysis
The physical storage ordering dictates how the memory hierarchy behaves during matrix traversals:
Row-Major Storage: [ Row 0 (10, 20, 30) | Row 1 (40, 50, 60) ]
Traversal Pattern A: Row-by-Row (Outer loop i, Inner loop j)
Address Sequence: α+0, α+4, α+8, α+12, α+16, α+20
Hardware Behavior: Contiguous streaming access. L1 cache prefetcher saturates line buffer.
Result: 93%+ Cache Hit Rate (~1 ns per read).
Traversal Pattern B: Column-by-Column (Outer loop j, Inner loop i)
Address Sequence: α+0, α+12, α+4, α+16, α+8, α+20
Hardware Behavior: Strided jumps of C * sizeof(Element). Cache lines evicted before reuse.
Result: Repeated Cache Misses (~50-100 ns latency per read). 10x-50x slower!💡 Architectural Principle:
In Row-Major languages, always structure nested loops with row indices outer and column indices inner:for (int i = 0; i < R; i++) for (int j = 0; j < C; j++).
3. Comprehensive Taxonomy of Special Matrices
#| Matrix Type | Structural / Mathematical Invariant | Memory Optimization Strategy |
|---|---|---|
| Square Matrix | Number of rows equals columns: | Standard 2D grid or symmetric 1D compression |
| Diagonal Matrix | All non-diagonal elements are zero: | Store only the diagonal elements as a 1D array of size |
| Identity Matrix () | Diagonal elements are 1, all others 0: | Compute on the fly (); zero memory overhead |
| Upper Triangular | Store elements in a compressed 1D array | |
| Lower Triangular | Store elements in a compressed 1D array | |
| Symmetric Matrix | Equal to its transpose: | Store only lower or upper triangle, saving nearly RAM |
| Toeplitz Matrix | Every descending diagonal has identical values: | Store top row and left column ( elements total) |
| Sparse Matrix | Vast majority () of cells are zero | Compressed Sparse Row (CSR) or Coordinate List (COO) |
4. Algorithmic Transformations
#A. In-Place Transpose (Square Matrix )
To transpose a matrix in-place without auxiliary memory, swap elements across the main diagonal () strictly for :
FUNCTION TransposeInPlace(M: 2D Array of Type, N: Integer) -> Void:
for i from 0 to N - 1:
for j from i + 1 to N - 1:
swap(M[i][j], M[j][i])B. Rotate Matrix Clockwise In-Place
A clockwise rotation can be decomposed into two distinct, symmetric elementary transformations:
- Transpose the matrix ().
- Reverse each row horizontally ().
Rotation State Progression ( Matrix)
Complexity: time, strictly auxiliary space.
5. Search Paradigms in 2D Grids
#Paradigm 1: Monotonically Sorted Matrix (Virtual 1D Binary Search)
- Structure: Each row is sorted left-to-right; the first integer of each row is strictly greater than the last integer of the previous row.
- Algorithm: Treat the matrix as a flattened 1D array of size .
- Index Translation:
- Complexity: time, auxiliary space.
Paradigm 2: Row-Wise & Column-Wise Sorted Matrix (Young Tableau / Staircase Search)
- Structure: Integers in each row are sorted left-to-right; integers in each column are sorted top-to-bottom.
- Algorithm: Start at the Top-Right corner :
- If Found!
- If All elements below are larger; eliminate column: .
- If All elements to the left are smaller; eliminate row: .
Staircase Search Step-by-Step Trace (Target )
Given matrix:
| Step | Current Position | Value | Comparison vs Target () | Decision & Movement | Remaining Search Window |
|---|---|---|---|---|---|
| 0 | 30 | Value too high | Columns , Rows | ||
| 1 | 25 | Value too high | Columns , Rows | ||
| 2 | 15 | Value too low | Columns , Rows | ||
| 3 | 18 | Value too low | Columns , Rows | ||
| 4 | 20 | Value too low No rows left, but check col 2: backtrack or re-evaluate | Found in col 2 at ! |
FUNCTION StaircaseSearch(M: 2D Array, R: Integer, C: Integer, target: Integer) -> Boolean:
row <- 0
col <- C - 1
while row < R and col >= 0:
if M[row][col] == target:
return True
else if M[row][col] > target:
col <- col - 1
else:
row <- row + 1
return FalseComplexity: At each step, either row increases or col decreases. Maximum steps time, auxiliary space.
6. Key Takeaways
#- String Architectures: C-style strings trade memory overhead ( byte) for length scans; modern length-prefixed strings provide length operations at the cost of a 24-byte stack descriptor.
- Avoid Repeated Concatenations: In immutable languages,
s = s + chcreates an quadratic allocation cascade; accumulate in a mutable buffer. - Hardware Stride Alignment: Row-major languages require row-outer column-inner loop orderings to exploit CPU cache-line prefetching.
- Symmetric Decomposition: Rotating a square matrix clockwise equals
Transpose + ReverseRows, executable in-place in time and space. - Staircase Elimination: Searching a 2D grid sorted along both axes from the top-right corner achieves time by pruning a complete row or column per comparison.
Academic Attribution & References
#- Cormen, T. H., Leiserson, C. E., Rivest, R. L., & Stein, C. (2022). Introduction to Algorithms (4th ed.), Chapter 10: Elementary Data Structures, Chapter 32: String Matching. MIT Press.
- Hennessy, J. L., & Patterson, D. A. (2019). Computer Architecture: A Quantitative Approach (6th ed.), Chapter 2: Memory Hierarchy Design. Morgan Kaufmann.
- Sedgewick, R., & Wayne, K. (2011). Algorithms (4th ed.), Chapter 5: Strings. Addison-Wesley.
- Knuth, D. E. (1997). The Art of Computer Programming, Volume 1: Fundamental Algorithms (3rd ed.), Section 2.2: Linear Lists. Addison-Wesley.