Arrays
Ajit Rajwade
Arrays (continued)
2
Bubble Sort
Bubble Sort
4
5
void bubble_sort(float *A, int n)
{
int i, j;
for (i = 0; i < n - 1; i++)
{
// Last i elements will be in place after this for loop
for (j = 0; j < n - i - 1; j++)
{
if (A[j] > A[j + 1])
swap(&A[j], &A[j + 1]);
}
}
}
Call from main as: bubble_sort(A,n);
How long does bubble sort take?
6
Notion of Time Complexity
The notion of time complexity of an algorithm
8
Big O notation
9
Insertion Sort (also called Insert Sort)
Insertion Sort
11
Insertion sort: routine
void insertion_sort (int *A, int n) {
int i, j;
for (i=1;i < n;i++){ // for each element of the array
j = i;
while (j > 0 && A[j-1] > A[j]) { // find the location to insert it to
// maintain a sorted subarray of i elements
swap (&A[j],&A[j-1]);
j = j - 1;
}
}
}
12
Demo of insertion sort from wikipedia:
https://en.wikipedia.org/wiki/Insertion_sort#/media/File:Insertion-sort-example-300px.gif
There is a slight difference between this code and the dry-run shown in the above gif. I will leave it to you to figure it out and modify the code above to reflect that change.
Time complexity
13
Character Strings
Character Strings
15
String input and output operations
char institute_name[] = “IITB”; char *xyz = institute_name;
16
String input and output operations
17
String input and output operations
char x[100]; cin.getline(x,n); // n = number of characters to be entered by a user
18
String operations: string length
int string_length (char *x)
{
int L = 0;
while (x[L] != ‘\0’) L++;
return L;
}
19
Comment: Note that we did not need to pass the length of x to this function. There is a potential infinite loop if x did not end with ‘\0’.
String operations: string copy
void string_copy (char *dest, const char* source) {
int i;
for(i=0;source[i]!= ‘\0’;i++){
dest[i] = source[i];
}
dest[i] = source[i];
}
20
Comment: Note that we did not need to pass the length of source to this function. There is a potential infinite loop if source did not end with ‘\0’. You can also use 0 instead of ‘\0’.
String operations: concatenation
void string_concat (char *first, char *second) {
int i = j = 0;
while (first[i] != 0) i++; // at the end of this loop, i = length of first
while (second[j] != 0) {
first[i++] = second[j++]; // copying the characters of second into first
}
first[i] = 0; // put a null character
}
21
String operations: String comparison
22
int string_compare (char *first, char * second){
int i = 0;
while (true){
if (first[i] == 0 && second[i] == 0) return 0;
if (first[i] == 0) return -1;
if (second[i] == 0) return +1;
if (first[i] < second[i]) return -1;
if (first[i] > second[i]) return +1;
i++;
}
return 0;
}
23
This function does not require string lengths to be specified.
But this function will treat the strings “abc” and “Abc” to be unequal. What changes will you make to this function to ignore case differences, i.e. treat “abc”, “ABC”, “Abc”, “abC” as all equal?
What does the following function do? Exercise in pointers!
void something_string (char *s, char *t){
while (*s++ = *t++);
// note this contains = and not == deliberately
}
24
String operations: String reverse
void string_reverse(char* s, char* rev_s){
int n = string_length(s);
for(int i=0;i<n;i++) rev_s[n-1-i] = s[i];
rev_s[n] = 0; // what will happen if you did not include this statement?
}
25
This function requires the length of string s to be computed. It fills in the reversed string inside rev_s.The programmer needs to ensure that adequate memory is contained in rev_s. Can you modify this routine to reverse the contents of s in-place without requiring a separate array? That is called in-place string reversal.
String operations: String reverse (in place)
void string_reverse_in_place (char* s){
int n = string_length(s);
for(int i=0;i<n/2;i++) swap(&s[i],&s[n-1-i]);
s[n] = 0; // what will happen if you did not include this statement?
}
26
We call this function in the form: string_reverse_in_place(s);
In-built string functions
27
String tokenizer
28
29
#include <cstring>
#include <iostream>
using namespace std;
int main(){ // test-driver for the strtok() function:
char s[] = "We are eagerly awaiting Mood Indigo, and then New Year’s Party";
char* p;
cout << "The string is: [" << s << "]\nIts tokens are:\n";
p = strtok(s, " "); // the delimiter is the space char.
while (p){
cout << "\t[" << p << "]\n";
p = strtok(NULL, " ");
}
cout << "Now the string is: [" << s << "]\n";
}
OUTPUT:
The string is: [We are eagerly awaiting Mood Indigo, and then New Year’s Party]
Its tokens are:
[We]
[are]
[eagerly]
[awaiting]
[Mood]
[Indigo,]
30
[and]
[then]
[New]
[Year’s]
[Party]
Now the string is: [We]
String tokenizer: explanation
31
String tokenizer: applications
32
Notion of (Auxiliary) Space Complexity
(Auxiliary) Space Complexity of an Algorithm
34
35
Algorithm | Time complexity | Space complexity |
Selection sort | O(n2) | O(1) |
Bubble sort | O(n2) | O(1) |
Linear search | O(n) | O(1) |
Binary search | O(log n) | O(1) for non-recursive, O(log n) for recursive |
string_length | O(n) | O(1) |
string_compare | O(min(n1,n2)) | O(1) |
string_reverse | O(n) | O(1) for in-place version, otherwise O(n) |
Convince yourself that these are correct!
Merge Sort
Merge Sort
37
Merge sort
void mergesort (int *A, int n){
if (n > 1){
int B1[n/2], B2[n-n/2];
for (int i=0;i<n/2;i++) { B1[i] = A[i]; }
for (int i=n/2;i<n;i++) { B2[i-n/2] = A[i]; }
mergesort(B1,n/2); mergesort (B2,n-n/2);
merge(A,B1,B2,n/2,n-n/2);
}
}
38
Merge-sort demo example
https://en.wikipedia.org/wiki/Merge_sort#/media/File:Merge-sort-example-300px.gif
One more example: https://www.geeksforgeeks.org/merge-sort/
39
Merge routine
void merge(int *A, int *B1, int *B2, int n1, int n2){
int cB1, cB2, cA; cB1 = cB2 = cA = 0; // counters for B1,B2,A respectively
while (cB1 < n1 && cB2 < n2){
// copy an element from B1 to A as B1 has the smaller element
if (B1[cB1] < B2[cB2]) A[cA++] = B1[cB1++];
// copy an element from B2 to A as B2 has the smaller element
else if (B1[cB1] > B2[cB2]) A[cA++] = B2[cB2++];
// both have equal-valued elements, copy from both to A
else { A[cA++] = B1[cB1++]; A[cA++] = B2[cB2++];}
}
// at most one of the following two while loops will be entered (why?)
// copy the remaining elements from either B1 or B2 to A
while(cB1 < n1) A[cA++] = B1[cB1++];
while(cB2 < n2) A[cA++] = B2[cB2++];
}
40
Merge routine
41
Merge Sort: Time and space complexity
42
Quick Sort
Quick sort
44
void quicksort (int *A, int low, int high) {
if (high - low < 1) return; // base case: nothing to sort in an array of 1 element
int pivotindex;
pivotindex = partition(A,low,high); // partition the array across the pivot
quicksort(A,low,pivotindex-1); // quicksort on left part (low to pivot-1)
quicksort(A,pivotindex+1,high); // quicksort on right part (pivot+1 to high)
}
45
46
int partition (int *A, int low, int high)
{
int i,j;
int pivot = A[high]; // last element is the pivot
i = low-1;
for(j=low;j<=high-1;j++)
{
if (A[j] < pivot)
{
i++;
swap(&A[i],&A[j]);
}
}
swap(&A[high],&A[i+1]);
return i+1;
}
47
Consider: arr[] = {10, 80, 30, 90, 40, 50, 70}
Source of image: geeksforgeeks
48
49
50
51
52
53
How long does quick sort take?
54
How long does quick sort take?
= 2T(1) + T(n-2) + n-1+n
= 3T(1) + T(n-3) + n-2+n-1+n
=n + T(1) + 1 + 2 + 3 + … + n-1 + n
= O(n2)
55
How long does quick sort take?
56
Multi-dimensional Arrays
Multi-dimensional arrays
float arr[n1][n2];
which is an array of n1*n2 floats
58
Multi-dimensional array declaration
Examples:
double a[3][2] = {{1,2},{3,4},{5,6}};
double c[2][4] = {{1,2,3,4},{5,6,7,8}};
59
Passing two-D arrays to a function
void matrix_add (int A[][100], int B[][100], int C[][100], int n1, int n2){
for (int i=0;i<n1;i++){
for(int j=0;j<n2;j++){
C[i][j] = A[i][j] + B[i][j];
}
}
}
60
This function adds two matrices and creates a third matrix. Just like with 1D arrays, 2D (or higher-D) arrays are always passed by reference, not by value.
Time complexity: O(n1 x n2)
Space complexity: O(n1 x n2)
Passing 2D arrays to a function
= summation over k of A[i][k]*B[k][j]
61
void matrix_multiply (int A[][MAX], int B[][MAX], int C[][MAX], int nr1, int nc1, int nr2, int nc2){
if (nc1 != nr2) { cout << “dimension mismatch”; return;}
int i,j,k;
for(i=0;i<nr1;i++) {
for(j=0;j<nc2;j++){
C[i][j] = 0;
for(k=0;k<nc1;k++){
C[i][j] += A[i][k]*B[k][j]; // dot product of i-th row of A and j-th column of B
} // close innermost for loop (on k)
} // close middle for loop (on j)
} // close outer for loop (on i)
}
62
Time complexity: O(nr1 x nc2 x nc1)
Space complexity: O(nr1 x nc2)
You can also use int A[MAX][MAX] as the parameter instead of int A[][MAX]
Input and Output of 2D arrays
void get2Darray (int A[][MAX], int n1, int n2){
int i,j;
for(i=0;i<n1;i++){
for(j=0;j<n2;j++){
cout << “enter the value of A at row: “ << i << “, column: ” << j << endl;
cin >> A[i][j];
}
}
}
63
Input and Output of 2D arrays
void print2Darray (int A[][MAX], int n1, int n2){
int i,j;
for(i=0;i<n1;i++){
for(j=0;j<n2;j++){
cout << A[i][j] << “ “;
}
cout << endl;
}
}
64
Input and Output of 2D arrays
int main() {
int A[MAX][MAX], B[MAX][MAX], C[MAX][MAX];
int n1, n2;
cin >> n1 >> n2 >> n3;
get2Darray(A,n1,n2);
get2Darray(B,n2,n3);
matrix_multiply(A,B,C,n1,n2,n2,n3);
}
65
Use of 2D Arrays
66
int main(){
char name[5][20];
int count=0;
cout << "Enter at most 5 names with at most 19 characters each:\n";
while (cin.getline(name[count++], 20)); // terminated by ctrl-Z
cout << "The names are:\n";
for (int i=0; i<count; i++)
cout << "\t" << i << ". [" << name[i] << "]" << endl;
}
67
Output:
Enter at most 4 names with at most 19 characters each:
George Washington
John Adams
Thomas Jefferson
^Z (or ^D — control Z or control D, which signals the end of input)
The names are:
0. [George Washington]
1. [John Adams]
2. [Thomas Jefferson]
68
3D Arrays
69
Dynamic Memory Allocation
Dynamic Memory Allocation
int *p = new int; // allocates memory for a single integer pointed to by p
*p = 3; // this is fine, as p now has a proper memory location
71
Dynamic Memory Allocation
int *p = new int (3);
if (p == NULL) { cout << “Insufficient memory available”; exit(0);}
*p = 4;
72
Dynamic Memory Allocation of Arrays
73
Memory de-allocation
float *q = new float (3.14);
delete q;
*q = 2.0; // this is dangerous
74
Memory de-allocation
float *p = new float [20];
delete [] p;
float x = 3.0;
float *y = &x;
delete y; // dangerous as y was not allocated memory via new
75
76
The get() function here creates a dynamic array:
void myget(double*& a, int& n) {
cout << "Enter number of items: "; cin >> n;
a = new double[n]; // array of n doubles, allocated dynamically
cout << "Enter " << n << " items, one per line:\n";
for (int i = 0; i < n; i++) { cout << "\t" << i+1 << ": ";
cin >> a[i];
}
}
void print(double* a, int n) {
for (int i = 0; i < n; i++)
cout << a[i] << " ";
cout << endl;
}
int main() {
double *a; int n;
myget (a,n); // create an array of n doubles
print (a,n); // print the array
delete [] a; // deallocate memory
}
Note: in the myget function, the pointer a is passed by reference! This is needed because the address in a (that is the value of a) is changed inside myget due to the new operator. We want this change to reflect in the calling function, so we need to pass a by reference! Another way to accomplish this is to just rewrite myget as double* myget (int&n);
Dynamic Memory Allocation of 2D arrays
77
int **allocate_2d (int n1, int n2){
int **A = new int*[n1]; // array of n1 int* variables
for(int i=0;i<n1;i++){
A[i] = new int [n2]; // array of n2 int variables
}
return A;
}
void deallocate_2d (int **A, int n1, int n2){
for(int i=0;i<n1;i++) delete [] A[i];
delete [] A;
}
int main(){
int **A = allocate_2d(4,5); deallocate_2d(A,4,5);
}
78
Array of pointers
79
Memory leaks
int* myfunction (int n){
int *a = new int [n];
for(int i=0;i<n;i++) a[i] = i*n;
return a;
}
int main() {
int n = 1000,*a;
for(int i=0;i<n;i++) a = myfunction(n);
}
80
Memory leaks
int* myfunction (int n){
int *a = new int [n];
for(int i=0;i<n;i++) a[i] = i*n;
return a;
}
int main() {
int n = 1000,*a;
for(int i=0;i<n;i++) { a = myfunction(n); delete [] a;}
}
81
Difference between static and dynamic memory allocation
82
Difference between static and dynamic memory allocation
83
void myfunction (int n) {
int A[n]; // or int A[100];
// some code here
return;
}
void myfunction2 (int n) {
int *A = new int[n]; // or new int[100];
// some code here
delete [] A; // required expressly to avoid memory leaks
return
}
static
dynamic