vectors2

Vectors of structs
 We can define vectors of structs
id
gpa
1250
0
name
lastname
struct student
{
unsigned int id;
string name, lastname;
double gpa;
};
id
1251
1
gpa
3.2
name
lastname
vector<student> class(11);
// a vector with 11 students
id
class[1].gpa = 3.2;
1260
name
for (i = 0; i <= 10; i++)
class[i].id = i + 1250;
10
lastname
gpa
shuffle.cpp program
Vectors as lists
 The “vector as counters” example constructs and
initializes a vector with a specific number of
elements
 Other uses of vector require the vector to “grow” to
accommodate new elements
 Consider reading words from a text file, storing them
in a vector
 How big should we define vector initially? What are
potential problems?
 When a vector is used as a list, we’ll use a different
method for adding elements to the vector so that
the vector can “grow”
Reading words into a vector
(problematic version)
vector<string> words(1000);
string w;
int i = 0;
string filename = PromptString("enter file name: ");
ifstream input(filename.c_str());
while (input >> w)
{
words[i]=w;
i++;
}
cout << "read " << i << " words" << endl;
 What is the problem?
 there might be more than 1000 words in the file
 in this case index runs out of range
Reading words into a vector (with index range
control but still problematic)
vector<string> words(1000);
string w;
int i = 0;
string filename = PromptString("enter file name: ");
ifstream input(filename.c_str());
while ((input >> w) && (i < 1000))
{
words[i]=w;
i++;
}
cout << "read " << i << " words" << endl;
 What is the problem?
 works fine if there are no more than 1000 words
 but if there are more than 1000 words, the rest is not read
Reading words into a vector
(no problems)
 One method would be to pass over the file two times
 one to find out number of words
 second to read the words into array
 Another method is to benefit from vector class utilities as in
the following code
vector<string> words; //create empty vector
string w;
string filename = PromptString("enter file name: ");
ifstream input(filename.c_str());
while (input >> w)
{
words.push_back(w); //adds the next word to the vector
//also increases the size if necessary
}
cout << "read " << words.size() << " words" << endl;
Using vector::push_back
 The method push_back adds new objects to the “end” of
a vector,
 Internally, the vector keeps track of its capacity
 If there is capacity, then there is no problem; the new item is added
to the end of the vector
 When the capacity is reached and push_back attempts to add a
new element to the vector, then the vector automatically “grows” by
adding half of the current capacity to the capacity
 0, 1, 2, 3, 4, 6, 9, 13, 19, 28, ... n+(n/2) …
 If you want to use push_back mechanism, then the
vector should be defined initially without specifying a size
 empty vector (zero size)
Size versus Capacity
 Capacity is the allocated size of the vector
 Size is how many elements are in the vector so far
 They are not the same concepts, but related as
described in the previous slide and illustrated below
vector<string> names;
// size is 0, capacity is 0
names.push_back("Ali");
// size is 1, capacity is 1
names.push_back("Husnu"); // size is 2, capacity is 2
names.push_back("Ayse");
// size is 3, capacity is 3
names.push_back("Cem");
// size is 4, capacity is 4
names.push_back("Jale");
// size is 5, capacity is 6
names.push_back("Hale");
// size is 6, capacity is 6
names.push_back("Veli");
// size is 7, capacity is 9
names.push_back("Gonca");
// size is 8, capacity is 9
names.push_back("Fatma");
// size is 9, capacity is 9
names.push_back("Yesim");
//size is 10, capacity is 13
size()member function
 size() member function basically returns the number of
elements in the vector
 When a vector is defined with no initial capacity, and
push_back is used to add elements, size() member
function returns the number of elements exist in the vector
 This is the number of calls of push_back() if no elements are deleted
 If elements deleted using pop_back(), size updated too
(decremented)
 If a non-empty vector is created, then the capacity and the
size is set to the number of elements of the vector. This
capacity is considered full, so the first push_back
increases the capacity.
 What about size() in case the vector is created as a nonempty one
 returns the size specified during declaration if no push_back() is
used
 returns the size specified during declaration + the number
push_back()s, if push_back() is used
Vector Processing Examples – 1
(vectorproc.cpp – not in book)
 write a function that takes a vector of integers as parameter and
returns the maximum of numbers in it
 process all array elements – for loop from 0 to vector’s size - 1
int max (const vector<int> & v)
//pre: vector v is not empty
//post: return max of elements in v
{
int i, max_so_far = INT_MIN;
for (i=0; i < v.size(); i++)
{
if (v[i] > max_so_far)
{
max_so_far = v[i];
}
}
return max_so_far;
}
Vector Processing Examples – 2 (vectorproc.cpp –
not in book)
 Write a function that takes a vector of integers as parameter and returns true if the
vector is sorted in ascending manner, false otherwise
 may not process all vector elements
 In this type of rule-checking applications, a possible method is to assume that the
rule is satisfied before the loop and find a counterexample in the loop
bool issorted (const vector<int> & v)
//post: returns true if the array is acsending sorted
{
bool s = true; // initially assume that array is sorted
//in the function try to break this assumption
int i =1;
while (i < v.size() && s == true)
{ //check until the end of array or until a counterexample is found
if (v[i-1] > v[i])
// if not sorted
s = false;
// counterexample is found
i++;
}
return s;
}
Searching a vector
 We can search for one occurrence, return true/false or the index
of occurrence
 Search the vector starting from the beginning
 Stop searching when match is found
 We can search and count the number of occurrences and return
count
 Search entire vector
 Similar to one occurrence search, but do not stop after first
occurrence
 We can search for many occurrences, but return occurrences in
another vector rather than count
 In all these cases, we search the vector sequentially starting
from the beginning
 This type of search is called “sequential search”
Counting search
int countmatches(const vector<string> & a, const string& s)
// post: returns # occurrences of s in a
{
int count = 0;
int k;
for(k=0; k < a.size(); k++)
{
if (a[k] == s)
{
count++;
}
}
return count;
}
 How can we change this code to return the index of the
first occurrence?
 see next slide
One occurrence search
int firstmatch(const vector<string> & a, const string& s)
// post: returns the index of occurrence of s in a, -1
// otherwise
{
int k;
for(k=0; k < a.size(); k++)
{
if (a[k] == s)
{
return k;
}
}
return -1;
}
 Does not search the entire array if one match is found
 good for efficiency purposes
 How could you modify this to return true/false?
Collecting search
 Collect the occurrences in another vector
void collect(const vector<string> & a,
vector<string> & matches)
// pre: matches is empty
// post: matches contains all elements of a with
//
first letter 'A'
{
int k;
for (k=0; k < a.size(); k++)
{
if (a[k].substr(0,1) == "A")
{
matches.push_back(a[k]);
}
}
}
Binary search
 Alternative to sequential search for sorted vectors
 If a vector is sorted we can use the sorted property to
eliminate half of the vector elements with one
comparison
 What number (between 1 and 100) do we guess first in
number guessing game?
 Idea of creating program to do binary search
 Check the middle element
 If it has the searched value, then you’re done!
 If not, eliminate half of the elements of the vector
 search the rest using the same idea
 continue until match is found or there is no match
 how could you understand that there is no match?
 let’s develop the algorithm on an example
 we need two index values, low and high, for the search space
Binary Search (search for 62)
0
1
2
3
4
5
6
7
8
9
10
11
12 13
14
10 24 34 52 55 62 67 75 80 81 90 92 100 101 111
low=0
low=0
mid=7
mid=3
low=4
high=6
high=6
mid=5
=>
FOUND
high=14
Binary Search (search for 60)
0
1
2
3
4
5
6
7
8
9
10
11
12 13
14
10 24 34 52 55 62 67 75 80 81 90 92 100 101 111
low = 0
low=0
mid=7
mid=3
high =14
high=6
low=4
high=6
mid=5
low=4 high=4
mid=4
low=5 high=4
=> NO MATCH FOUND – STOP
Binary search code
int bsearch(const vector<string>& list, const string& key)
// pre: list.size() == # elements in list
// post: returns index of key in list, -1 if key not found
{
int low = 0;
// leftmost possible entry
int high = list.size()-1;
// rightmost possible entry
int mid;
// middle of current range
while (low <= high)
{
mid = (low + high)/2;
if (list[mid] == key)
{
return mid;
}
else if (list[mid] < key)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return -1;
}
// found key, exit search
// key in upper half
// key in lower half
// not in list
Comparing Sequential and Binary Search
Table 8.1 Comparing sequential / linear search with binary search
Number of Items Examined (at worst)
List Size
Binary Search
Sequential Search
1
1
1
10
4
10
1,000
11
1,000
5,000
14
5,000
100,000
18
100,000
1,000,000
21
1,000,000
A Computer Science Tapestry page 375
• Given a list of N elements:
• Binary search makes on the order of log N operation
O(log N)
• Linear (sequential) search takes on the order of N operations
O(N)
Sorting
 One of the fundamental operations in Computer
Science
 Given a randomly ordered array, sort it
 ascending
 descending
 Many algorithms exists
 some in Chapter 11
 we will discuss two of them – Selection Sort (11.1.1)
and Insertion Sort (11.1.2)
 Analysis in 11.4
Selection Sort
 N is the number of elements in vector/array
 Find smallest element, move into 0th vector/array location
 examine all N elements
 0 .. N-1
 Find next smallest element, move into 1st location
 0th location is already the minimum
 examine N-1 elements
 1 .. N-1
 Find next smallest element, move into 2nd location
 0th and 1st locations are already the minimum two elements
 examine N-2 elements
 2 .. N-1
 Generalize
for kth element, 0 <= k <= N-2
- find the minimum between kth and last element (element
with index N-1) of array
- swap the kth element with the minimum one
Selection Sort: The Code
void SelectSort(vector<int> & a)
// pre: a contains a.size() elements
// post: elements of a are sorted in non-decreasing order
{
int j, k, temp, minIndex, numElts = a.size();
for(k=0; k < numElts - 1; k++)
{
minIndex = k;
// minimal element index
for(j=k+1; j < numElts; j++)
{
if (a[j] < a[minIndex])
{
minIndex = j;
// new min, store index
}
}
temp = a[k];
// swap min and k-th elements
a[k] = a[minIndex];
a[minIndex] = temp;
}
}
insertion and deletion
 It’s easy to insert at the end of a vector, use
push_back()
 However, if the vector is sorted and if we want to keep it sorted,
then we can’t just add to the end
 We have to find an appropriate position to insert the element and do
some shifts.
 If we need to delete an element from a sorted vector,
how can we “close-up” the hole created by the deletion?
 Shift elements left by one index, decrease size
 We decrease size using pop_back()
pop_back() changes size, not capacity
Inserting an element into a sorted array
2
7
11 18
21 22 26 89 99
Insert NewNum which is e.g. 23
• Is the array “capacity” sufficient for an extra
element?
• What would you do to insert newNum in the right
spot?
Insertion into sorted array
0
1
2
3
2
7
11 18
4
5
6
7
8
21 22 26 89 99
NewNum = 23
2
7
11 18
21 22 26 26 89 99
0
1
2
4
3
5
6
7
8
9
Insert into sorted vector
void insert(vector<int>& a, int newnum) // NOT const vector
// pre: a[0] <= … <= a[a.size()-1], a is sorted
// post: newnum inserted into a, a still sorted
{
int count = a.size(); //size before insertion
a.push_back(newnum);
//increase size – newnum is inserted at
// the end but the inserted value is not important
int loc = count;
// start searching insertion loc from end
while (loc > 0 && a[loc-1] > newnum)
{ a[loc] = a[loc-1];
loc--;
// shift right until the proper insertion cell
}
a[loc] = newnum; //actual insertion
}
 See vectorproc.cpp (not in book)
What about deletion?
 Remove the element at a given position (pos)
void remove(vector<string>& a, int pos)
// post: original a[pos] removed, size decreased
{
int lastIndex = a.size()-1;
a[pos] = a[lastIndex];
a.pop_back();
}
 What about if vector is sorted, what changes?
 What’s the purpose of the pop_back() call?
Deletion from sorted vector
Ex: Delete element at position 3
0
1
2
3
2
7
11 18
4
5
6
7
8
Size is 9
21 22 26 89 99
First shift all elements on the right
of 3rd element one cell to the left
0
1
2
3
2
7
11 21 22 26 89 99 99
0
1
2
2
7
11 21 22 26 89 99 99
3
4
4
5
5
6
6
7
7
Size is now 8
8
pop back the last element of vector
Deletion from sorted vector
void remove(vector<int> & a, int pos)
// pre: a is sorted
// post: original a[pos] removed, a is still sorted
{
int lastIndex = a.size()-1;
int k;
for(k=pos; k < lastIndex; k++)
{
a[k] = a[k+1];
} //shift all elements on the right of pos one cell left
a.pop_back(); //remove the last element of the array
}
 Does pop_back() actually remove an element?
 no, it just decreases the size so that the last element
becomes unreachable
 Capacity remains the same
 See vectorproc.cpp (not in book)
Insertion Sort
 Insert 1st element before or after 0th
 first 2 sorted
 Insert 2nd element (element with index 2) in
proper location
 first 3 sorted
 Generalize
 insert kth element (element with index k) within first
k elements
 first k+1 sorted
 run this for all k between 1 .. N-1
Insertion Sort – The Code
void InsertSort(vector<string> & a)
// precondition: a contains a.size() elements
// postcondition: elements of a are sorted in nondecreasing order
{
int k,loc, numElts = a.size();
for(k=1; k < numElts; k++)
{
string hold = a[k];
// insert this element
loc = k;
// location for insertion
// shift elements to make room for hold (i.e. a[k])
while (0 < loc && hold < a[loc-1])
{
a[loc] = a[loc-1];
loc--;
}
a[loc] = hold;
}
}
Which one faster?
 No exact answer! It depends on the vector to be sorted
 already ordered, totally disordered, random
 Let’s see how many iterations do we have in Selection Sort
 Outer loop  k: 0 .. N-2
 Inner loop  j: k+1 .. N-1
 (N-1) + (N-2) + (N-3) + .. + 1 = N(N-1)/2 = (N2 – N)/2
 Worst case, best case, average case???
 Complexity is O(N2)
 order of N2
 Big-oh notation used to describe algorithmic complexities. This is not
a precise amount of operations and comparisons. Minor terms and
coefficients are not taken into consideration
Which one faster?
 Let’s analyze Insertion Sort
 Outer loop  k: 1 .. N-1
 N-1 iterations for the outer loop
 What about inner loop?
 worst case, best case differ
 worst case: k times, so total is 1+2+3+…+(N-1) = N(N-1)/2, complexity is
O(N2)
 best case: inner loop does not iterate, complexity is O(N), but best case
complexity analysis is not done too often
 what are the best and worst cases?
 average case: inner loop iterates k/2 times, order is still O(N2)
 Complexities of both Selection and Insertion Sort are O(N2)
 Which one would you prefer to use?
 Let’s run timesorts.cpp (modified from book) – needs several Tapestry
.h and .cpp files in the project folder to run (comparer.h, ctimer.h,
ctimer.cpp, prompt.h, prompt.cpp, randgen.h, randgen.cpp, sortall.h,
sortall.cpp – red ones to be added to the project).
Built-in Arrays
 C++ native array type
 Two versions
 fixed size arrays
 array size is fixed and must be specified with a constant
expression at the declaration
 we will see this type now
 array pointers
 array size is dynamically allocated
 we will not see it in this course
 use of both types are the same except definition
 vector versus built-in arrays
 vector is a class based on built-in arrays
 vector has member functions and operators, built-in
arrays do NOT
 vector is more flexible, but slower
Built-in Array declaration
 As we said, we will discuss fixed size built-in arrays, not the pointer
version with dynamic allocation
 size must be able to be determined at compile time
 constant, literal or an expression that involves constants and literals only
const int CLASSSIZE = 100;
// constant
string
double
int
// array of 100 strings
// array of 500 doubles
// array of 200 integers
names[CLASSIZE];
grades[CLASSIZE*5];
list[200];
 The following array declaration is INVALID
int size;
cout "Enter how many students ? ";
cin >> size;
string names[size]; // array size cannot be a variable
Built-in array initialization at declaration
 You may specify a list of initial values at declaration. See
following example:
string dayNames [] = {"Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday","Friday", "Saturday"};
 dayNames is an array with 7 elements of type string
 0th element is “Sunday”, 1st is “Monday”, ...
 not necessary to specify size (7), since the number of elements make
the size clear
 but you can specify the size, if you wish
string dayNames [7] = {"Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday","Friday", "Saturday"};
Assignment rules in arrays
 vectors with the same element type can be assigned to
each other by =
 LHS vector becomes the same as the RHS vector
 size and capacity also become the same
 Built-in arrays cannot be assigned to each other by =
int coins[] ={1,5,10,25};
int temp[4];
temp = coins;
temp[1] = coins[2];
// illegal
// legal – array element assignment
 How can we assign coins to temp?
 element by element
for (i=0; i<4; i++)
temp[i] = coins[i];
Passing built-in arrays as parameters
 A built-in array can be passed only as reference
parameter or const-reference parameter
 cannot be passed as value parameter
 But, we do not use ampersand character, &, at
parameter declaration
 and we do not specify the array size in array parameter
 however array size is generally passed as another
integer parameter since we do not have a size()
member function for built-in arrays
reference parameter
void Change(int list[], int numElts);
void Print(const int list[], int numElts);
const-reference parameter
Built-in array demo
 See fixlist.cpp (slightly modified from the version in
book)
 Why did we use const in Print?
 to avoid accidental changes in array list
 Why did we pass numElts as parameter for the
number of elements in the array?
 because we don’t know the total number of elements in the
array while writing the functions
Example – Fibonacci numbers
 Used in many areas of Mathematics and Computer Science
F0 = 1
F1 = 1
Fn = Fn-1 + Fn-2
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
 You can see many examples of Fibonacci numbers in nature
 E.g. Increase of number of branches of trees in time
 See http://www.mcs.surrey.ac.uk/Personal/R.Knott/Fibonacci/fibnat.html
for more examples
const int MAX_SIZE = 100;
int list[MAX_SIZE];
int k;
list[0] = list[1] = 1;
for (k=2; k < MAX_SIZE, k++)
{
list[k] = list[k-1]+list[k-2];
}
Use of strings as arrays
 Characters in a string can be referred as an array using [ ]
string s="cs201";
...
s[0] = 'n'; // makes 0th character of s 'n'
...
for (k=0; k<s.length(); k++)
cout << s[k] << " ";
 In general,
s[k]
means s.at(k)
The Matrix
 To represent two dimensional arrays
 Given rows and columns:
 We define a matrix as a vector of vectors
vector<vector<int>> mat(rows, vector<int>(cols));
vector<vector<int>> mat(3, vector<int>(5));
0
1
2
3
4
0
1
2
100
mymatrix[2][3] = 100;
 First index is for row, second is for column
Possible Matrix definitions
 Possible matrix declarations
 4 different declarations
vector<vector<int>> matrix_name;
empty matrix (zero rows, zero columns)
vector<vector<int>> matrix_name(rows);
matrix with rows many empty vector<int>’s
vector<vector<int>> matrix_name(rows, vector<int>(cols));
matrix with rows*cols elements all initialized to 0
vector<vector<int>> matrix_name(rows,
vector<int>(cols, init_value));
matrix with rows*cols elements all initialized to init_value
To get the size of rows and columns
mymatrix.size()
 number of rows in matrix
mymatrix[0].size()
 number of columns in matrix
 Example: Let’s run matdemo.cpp