Introduction
Array basics:
- Contiguous block of memory
- Elements of same data type
- Fixed size set at creation
- Faster access than other data structures
- Stored in stack memory
- Address is read-only
- Can create variable length arrays in C++
Declaration & Allocation
dataType arrayName[arraySize];
Accessing Elements
Use index to access elements within bounds
arrayName[0]; // First element
arrayName[i]; // ith element
Initialization
int arr[3] = {1, 2, 3}; // Initializer list
int arr2[3] = {}; // Default initialization
Array Creation
#include<iostream>
using namespace std;
int main(){
// Create
int arr[100] = {1,2,3,4};
cout << arr[0] << endl;
cout << arr[1] << endl;
cout << arr[2] << endl;
int n = sizeof(arr)/sizeof(int);
cout << n << endl;
// Print all the elements of the array
for(int i=0, i<=n, i++){
cout << arr[i] << " , ";
}
cout << endl;
return 0;
}
The key things to note:
sizeof(arr)
Β returns the total size in bytes of the arrayΒarr
- Each integer element occupies 4 bytes
- There are X elements in the array
- So total size = X * size of each element
- By dividing the total size by the size of 1 element, we get the number of elements, which is X
Array Input
To take array input from the user:
#include<iostream>
using namespace std;
int main(){
// Take size of array
int n;
cin >> n;
// Declare array
int arr[n];
// Take input for each element
for(int i=0; i<n; i++){
cin >> arr[i];
}
// Print all the elements of the array
for(int i=0; i<n; i++){
cout << arr[i] << " , "
}
cout << endl;
}
- First take size of array
n
- Declare array of size
n
dynamically - Run loop till n and use
cin
to input element by element
We can also calculate total array size using:
int size = sizeof(arr)/sizeof(int);
Passing Arrays to Functions
Arrays can be passed to functions in two ways:
Passing entire array
#include<iostream>
using namespace std;
// Array name is an address which is read only
// Pointer variable can hold the address of any bucket/array in the memory
void print(int * myArray){
cout << sizeof(myArray) << endl;
}
int main(){
// Take Input N
int n;
cin >> n;
// Create
int arr[n];
for(int i; i=0; i++){
cin >> arr[i];
}
// Array capacity
cout << sizeof(arr)/sizeof(int) << endl;
print(arr);
return 0;
}
// When done without pointer
// 1 warning
/*Output
5
12345
20 <- Main loop <- Size of the array
8 <- Size of Pointer variable
*/
Passing pointer to first element
#include<iostream>
using namespace std;
// Array name is an address which is read only
// Pointer variable can hold the address of any bucket/array in the memory
void print(int * myArray,int n){
for(int i=0; i < n; i++){
cout << myArray[i] << endl;
}
}
int main(){
// Take Input N
int n;
cin >> n;
// Create
int arr[n];
for(int i; i=0; i++){
cin >> arr[i];
}
// Array capacity
cout << sizeof(arr)/sizeof(int) << endl;
print(arr, n);
return 0;
}
In both cases, modifications to array in function are reflected in original array.
Time Complexity: Passing array only passes address. So it takes constant time O(1).
Linear Search
Linear search is used to find the position of a given element in an array.
It works by traversing the array sequentially and comparing each element to the search value.
#include<iostream>
using namespace std;
void print(int * myArray,int n){
for(int i=0; i < n; i++){
cout << myArray[i] << " , ";
}
cout << endl;
}
// Return index if present, otherwise -1
int linearSearch(int arr[], int n, int key) {
for(int i = 0; i < n; i++) {
// If key is found
if(arr[i] == key) {
return i;
}
}
// If key not found
return -1;
}
int main(){
// Take Input N
int n;
cin >> n;
// Input an array
int arr[n];
for(int i; i=0; i++){
cin >> arr[i];
}
// Output an array
print(arr, n);
// Which element to search
int key;
cin >> key;
int res = linearSearch(arr, n, key);
if(res==-1){
cout << "Element not found" << endl;
}
else{
cout << "Present at index : " << endl;
}
return 0;
}
Implementation:
- Start from the first element
- Compare each element with search key
- If match found, return index
- If no match, return -1
Time Complexity:
- O(n) - Linear scan of entire array
Applications:
- Find if an element is present or not
- Find index/position of an element
Reversing an Array
Reversing an array means changing the order of elements to opposite direction.
For example, if original array is:
1, 2, 3, 4, 5
Reversed array would be:
5, 4, 3, 2, 1
There are two common methods:
Using Loop
void reverseArray(int arr[], int n) {
for(int i = n-1; i >= 0; i--) {
cout << arr[i] << " ";
}
}
- Start from end of array
- Print each element in reverse
By Index Manipulation
void reverseArray(int arr[], int n){
for(int i = 0; i < n; i++){
cout << arr[n - i - 1] << " ";
}
}
- Print element from end
- E.g. element from end is n-1
Time Complexity: O(n) to traverse array once
Reversing In-Place
In-place reversal modifies array within same memory.
void reverseInPlace(int arr[], int n) {
int start = 0;
int end = n-1;
while(start < end) {
swap(arr[start], arr[end]);
start++;
end--;
}
}
- Initialize start and end index
- In loop, swap elements from ends
- Increment start, decrement end
- Repeat till start < end
Benefits:
- Does not require extra memory
- Modifies array in-place
Subarrays
A subarray is a contiguous part of an array.
For an array arr[]
of size n, we can form different subarrays as follows:
#include<iostream>
using namespace std;
int main(){
// Subarray
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr)/sizeof(int);
// Generate all subarrays
for(int i = 0; i < n; i++) {
for(int j = i; j < n; j++) {
// Print current subarray
for(int k = i; k <= j; k++) {
cout << arr[k] << " ";
}
cout << endl;
}
}
return 0;
}
This generates subarrays starting from arr[i]
to arr[j]
.
For example for above array, it will print:
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
2
2 3
2 3 4
2 3 4 5
3
3 4
3 4 5
4
4 5
5
So in this way, we can generate all subsequences of an array.
Applications:
- Finding maximum sum contiguous subarray (Kadaneβs algorithm)
- Finding longest contiguous subarray with a given property
- Many other array/string problems
Time Complexity:
- O(N^3) to print all subarrays
- Can be optimized to O(N^2) using better loops
Multidimensional Arrays
- Arrays containing other arrays
- Create 2D, 3D etc arrays
- Access elements using multiple indices
2D Arrays
A 2D array is an array of arrays. It is a table of elements arranged in rows and columns.
For example:
1 2 3
4 5 6
7 8 9
Declaration
We declare a 2D array by specifying the number of rows and columns:
int arr[100][100]; // Declares a 2D array of size 100 x 100
Initialization
A 2D array can be initialized by nested for loops:
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cin >> arr[i][j];
}
}
This allows us to initialize each element one by one.
Accessing Elements
To access a particular element, we use two index values - row index and column index:
arr[i][j] // ith row and jth column
For example:
arr[2][3] = 5; // Row 3, Column 4 element
Traversal
We can traverse a 2D array using nested for loops.
To print all elements:
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
Applications
- Tabular data
- Matrices
- Images
- Games like chess, sudoku etc.
2D arrays allow us to represent multi-dimensional data for various applications. The elements are stored contiguously in row-major order in memory for fast access.
NOTE
Defining columns is a must .
#include<iostream>
using namespace std;
int main(){
// 2D arrays
int arr[100][100];
// Rows, columns
int rows;
int cols;
cout << "Rows: ";
cin >> rows;
cout << "Columns: ";
cin >> cols;
// Read a 2D array
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cin >> arr[i][j];
}
cout << endl;
}
// Print a 2D array
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
return 0;
}
Memory (Row Major Order)
Even though a 2D array is conceptualized as a table of rows and columns, physically the elements are stored linearly in memory.
In row major order, the elements are stored row-wise i.e first all elements of row 1, then row 2 and so on.
For example:
1 2 3
4 5 6
7 8 9
This is stored in memory in the following linear sequence:
1 2 3 4 5 6 7 8 9
Row major order allows easy access along rows, since each rowβs elements are stored contiguously.
So if we access arr[1][2]
:
- Go to 2nd row (address is calculated based on number of cols)
- Go to 3rd position within that row
This access takes constant time O(1).
However, column-wise access may be slower as the column elements are not contiguous. To fetch a column we may need to traverse through multiple rows.
Overall, the linear row-major storage allows simplicity in computing locations, since 2D logical coordinates can be mapped to 1D physical location.
Wave Print
Wave print refers to printing the elements of a 2D array column-wise in a wave pattern.
For example, for the following array:
1 2 3
4 5 6
7 8 9
Wave print would be:
1 4 7 8 5 2 3 6 9
Here is an implementation in C++:
#include<iostream>
using namespace std;
void wavePrint(int arr[][100], int rows, int cols){
// Iterate on every column
for(int col = 0; col < cols; col++){
if(col%2==0){
for(int row=0; row <= rows-1; row++){
cout << arr[row][col] << " ";
}
}
else{
for(int row = rows-1; row >= 0; row--){
cout << arr[row][col] << " ";
}
}
}
cout << endl;
}
int main(){
// 2D arrays
int arr[100][100];
// Rows, columns
int rows;
int cols;
cout << "Rows: ";
cin >> rows;
cout << "Columns: ";
cin >> cols;
// Read a 2D array
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cin >> arr[i][j];
}
cout << endl;
}
// Print a 2D array
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
cout << arr[i][j] << " ";
}
cout << endl;
}
wavePrint(arr, rows, cols);
return 0;
}
The key steps are:
- Iterate through each column
- Check if column is even or odd usingΒ
col%2
- If even column:
- Print from top to bottom
- If odd column:
- Print from bottom to top
The time complexity is O(rows x cols) since we are iterating through every element once.
INFO
Wave print can be useful for problems where we need to traverse a 2D array in zig-zag fashion from top left to bottom right. It is an alternative way to traverse a 2D array.