char a[5] = {'h', 'e', 'l', 'l', 'o'}; // Array: Fixed size, contiguous memory.
// random access
char x = a[1]; // x is 'e'
a[0] = 'b';
A size-n array can be created in this way: char a[n];
When writing the code, n must be known.
What if n is unknown until program is running?
// Dynamic Allocation of Arrays
char* a = NULL;
int n; // array size
cin >> n; // read in the size, e.g., get n = 5
a = new char[n];
// store something in the array
a[0] = 'h';
a[1] = 'e';
a[2] = 'l';
a[3] = 'l';
a[4] = 'o';
// When done, free memory.
// Otherwise, memory leak can happen.
delete[] a;
a = NULL;
Properties of Array
The size is fixed. (New elements cannot be appended.)
Random access using a[i] has O ( 1 ) O(1) O(1) time complexity.
Removing an element in the middle has O ( n ) O(n) O(n) time complexity. (Require moving the remaining items leftward.)
The main difference is that vector’s capacity can automatically grow.
New elements can be appended using push_back() in O ( 1 ) O(1) O(1) time (on average).
The last element can be removed using pop_back() in O ( 1 ) O(1) O(1) time.
vector<char> v = {'h', 'e', 'l', 'l', 'o'}; // Vector: dynamic size, contiguous memory.
// random access
char x = v[1]; // x is 'e'
// assignment
v[0] = 'b';
// Insert
// insert a new element to the end
v.push_back('s');
// Delete
// delete the element in the end
v.pop_back();
// delete an element in the middle
v.erase(v.begin() + 1); // O(n) time complexity! Slow!
Vector capacity can grow
vector<char> v(100);
cout << v.size(); // print "100"
cout << v.capacity(); // print "100"
v.push_back('x');
cout << v.size(); // print "101"
cout << v.capacity(); // print "200"
/*
* capacity of vector = 100
* size of vector = 100
* v.push_back('x');
* capacity of vector = 200
* size of vector = 101
* What happens when size is going to exceed capacity?
* Create a new array of capacity 200.
* Copy the 100 elements from the old array to the new.
* Put the new element in the 101-st position.
* Free the old array from memory.
*/
Inputs: (i) an array whose elements are in the ascending order and (ii) a key.
Goal: Search for the key in the array. If found, return its index; if not found, return -1.
left = 0.
right = arr.size() - 1.
mid = |(left + right) / 2|.
If arr[mid] == key ==> return mid.
If arr[mid] > key ==> right <- mid - 1.
If arr[mid] < key ==> left = mid + 1.
Time Complexity
Each iteration reduces the size of array by half.
Let n be the size of the array.
The total number of iterations is at most l o g 2 n log_2n log2n.
Per-iteration time complexity: O ( 1 ) O(1) O(1).
Overall time complexity: O ( l o g n ) O(logn) O(logn).
int search(int arr[], int left, int right, int key) {
while (left <= right) {
int mid = (left + right) / 2;
if (key == arr[mid]) return mid; // key is found
if (key > arr[mid]) left = mid + 1; // search in the right half
else right = mid - 1; // search in the left half
}
return -1; // key is not found
}