Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Sr.No Data Type & Description You can combine two or more of these values by ORing them
together. For example if you want to open a file in write mode and
want to truncate it in case that already exists, following will be the
1 ofstream syntax −
ofstream outfile;
This data type represents the output file stream and is used to create outfile.open("file.dat", ios::out | ios::trunc );
files and to write information to files. Similar way, you can open a file for reading and writing purpose as
follows −
2 fstream afile;
ifstream
afile.open("file.dat", ios::out | ios::in );
This data type represents the input file stream and is used to read
information from files. Closing a File
When a C++ program terminates it automatically flushes all the
streams, release all the allocated memory and close all the opened
3 fstream files. But it is always a good practice that a programmer should
close all the opened files before program termination.
This data type represents the file stream generally, and has the Following is the standard syntax for close() function, which is a
capabilities of both ofstream and ifstream which means it can create member of fstream, ifstream, and ofstream objects.
files, write information to files, and read information from files. void close();
To perform file processing in C++, header files <iostream> and Writing to a File
<fstream> must be included in your C++ source file. While doing C++ programming, you write information to a file from
your program using the stream insertion operator (<<) just as you use
Opening a File that operator to output information to the screen. The only difference is
A file must be opened before you can read from it or write to it. Either that you use an ofstream or fstream object instead of the cout object.
ofstream or fstream object may be used to open a file for writing. And
ifstream object is used to open a file for reading purpose only. Reading from a File
Following is the standard syntax for open() function, which is a You read information from a file into your program using the stream
member of fstream, ifstream, and ofstream objects. extraction operator (>>) just as you use that operator to input
void open(const char *filename, ios::openmode mode); information from the keyboard. The only difference is that you use an
Here, the first argument specifies the name and location of the file to ifstream or fstream object instead of the cin object.
be opened and the second argument of the open() member function
defines the mode in which the file should be opened.
#include <fstream> // write the data at the screen.
#include <iostream> cout << data << endl;
using namespace std;
// again read the data from the file and display it.
int main () { infile >> data;
char data[100]; cout << data << endl;
for (int i = 0; i < 100; i++) 7. With Big-O, we're usually talking about the "worst case"
{ bool arrayContainsElement(int arr[], int size, int element)
printf("Hi\n"); {
} for (int i = 0; i < size; i++)
} {
This is O(1 + n/2 + 100), which we just call O(n). if (arr[i] == element) return true;
}
Why can we get away with this? Remember, for big O notation we're return false;
looking at what happens as n gets arbitrarily large. As n gets really big, }
adding 100 or dividing by 2 has a decreasingly significant effect. Here we might have 100 items in our array, but the first item might be
that element, in this case we would return in just 1 iteration of our
5. Drop the less significant terms loop.
void printAllNumbersThenAllPairSums(int arr[], int size)
{ In general we'd say this is O(n) runtime and the "worst case" part
for (int i = 0; i < size; i++) would be implied. But to be more specific we could say this is worst
{ case O(n) and best case O(1) runtime. For some algorithms we can
printf("%d\n", arr[i]); also make rigorous statements about the "average case" runtime.
}
Similarly:
The Big O notation defines an upper bound of an algorithm, it bounds Vectors are same as dynamic arrays with the ability to resize itself
a function only from above. For example, consider the case of automatically when an element is inserted or deleted, with their storage
Insertion Sort. It takes linear time in best case and quadratic time in being handled automatically by the container. Vector elements are
worst case. We can safely say that the time complexity of Insertion placed in contiguous storage so that they can be accessed and traversed
sort is O(n^2). Note that O(n^2) also covers linear time. using iterators. In vectors, data is inserted at the end. Inserting at the
end takes differential time, as sometimes there may be a need of
The Big-O Asymptotic Notation gives us the Upper Bound Idea, extending the array. Removing the last element takes only constant
mathematically described below: time because no resizing happens. Inserting and erasing at the
beginning or in the middle is linear in time.
f(n) = O(g(n)) if there exists a positive integer n0 and a positive
constant c, such that f(n)≤c.g(n) ∀ n≥n0 Certain functions associated with the vector are:
Iterators
The general step wise procedure for Big-O runtime analysis is as
follows: begin() – Returns an iterator pointing to the first element in the vector
end() – Returns an iterator pointing to the theoretical element that
Figure out what the input is and what n represents. follows the last element in the vector
Express the maximum number of operations, the algorithm rbegin() – Returns a reverse iterator pointing to the last element in the
performs in terms of n. vector (reverse beginning). It moves from last to first element
Eliminate all excluding the highest order terms. rend() – Returns a reverse iterator pointing to the theoretical element
Remove all the constant factors. preceding the first element in the vector (considered as reverse end)
cbegin() – Returns a constant iterator pointing to the first element in
the vector.
cend() – Returns a constant iterator pointing to the theoretical element
that follows the last element in the vector.
crbegin() – Returns a constant reverse iterator pointing to the last
element in the vector (reverse beginning). It moves from last to first
element
crend() – Returns a constant reverse iterator pointing to the theoretical
element preceding the first element in the vector (considered as reverse
end)
#include <iostream> Capacity
#include <vector>
size() – Returns the number of elements in the vector.
using namespace std; max_size() – Returns the maximum number of elements that the vector
can hold.
int main() capacity() – Returns the size of the storage space currently allocated to
{ the vector expressed as number of elements.
vector<int> g1; resize() – Resizes the container so that it contains ‘g’ elements.
empty() – Returns whether the container is empty.
for (int i = 1; i <= 5; i++) shrink_to_fit() – Reduces the capacity of the container to fit its size
g1.push_back(i); and destroys all elements beyond the capacity.
reserve() – Requests that the vector capacity be at least enough to
cout << "Output of begin and end: "; contain n elements.
for (auto i = g1.begin(); i != g1.end(); ++i)
cout << *i << " "; Element access:
cout << "\nOutput of cbegin and cend: "; reference operator [g] – Returns a reference to the element at position
for (auto i = g1.cbegin(); i != g1.cend(); ++i) ‘g’ in the vector
cout << *i << " "; at(g) – Returns a reference to the element at position ‘g’ in the vector
front() – Returns a reference to the first element in the vector
cout << "\nOutput of rbegin and rend: "; back() – Returns a reference to the last element in the vector
for (auto ir = g1.rbegin(); ir != g1.rend(); ++ir) data() – Returns a direct pointer to the memory array used internally
cout << *ir << " "; by the vector to store its owned elements.
A linked list is a linear data structure, in which the elements are not
List
stored at contiguous memory locations. The elements in a linked list
are linked using pointers as shown in the below image:
Lists are sequence containers that allow non-contiguous memory
allocation. As compared to vector, list has slow traversal, but once a
position has been found, insertion and deletion are quick. Normally,
when we say a List, we talk about doubly linked list. For implementing
a singly linked list, we use forward list.
void LinkedList::pop_back(){
assert(head != nullptr);
void LinkedList::pop_front(){
assert(head != nullptr);
Second Pass:
( 1 4 2 5 8 ) –> ( 1 4 2 5 8 )
( 1 4 2 5 8 ) –> ( 1 2 4 5 8 ), Swap since 4 > 2 Merge Sort
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) Merge Sort is a Divide and Conquer algorithm. It divides input array in
Now, the array is already sorted, but our algorithm does not know if it two halves, calls itself for the two halves and then merges the two
is completed. The algorithm needs one whole pass without any swap to sorted halves. The merge() function is used for merging two halves.
know it is sorted. The merge(arr, l, m, r) is key process that assumes that arr[l..m] and
arr[m+1..r] are sorted and merges the two sorted sub-arrays into one.
Third Pass: See following C implementation for details.
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 )
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) MergeSort(arr[], l, r)
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) If r > l
( 1 2 4 5 8 ) –> ( 1 2 4 5 8 ) 1. Find the middle point to divide the array into two halves:
middle m = (l+r)/2
void swap(int *xp, int *yp)
{ 2. Call mergeSort for first half:
int temp = *xp; Call mergeSort(arr, l, m)
*xp = *yp; 3. Call mergeSort for second half:
*yp = temp; Call mergeSort(arr, m+1, r)
}
4. Merge the two halves sorted in step 2 and 3:
// A function to implement bubble sort Call merge(arr, l, m, r)
void bubbleSort(int arr[], int n)
{
The following diagram from wikipedia shows the complete merge sort
int i, j;
for (i = 0; i < n-1; i++) process for an example array {38, 27, 43, 3, 9, 82, 10}. If we take a
closer look at the diagram, we can see that the array is recursively
// Last i elements are already in place divided in two halves till the size becomes 1. Once the size becomes 1,
for (j = 0; j < n-i-1; j++)
if (arr[j] > arr[j+1])
the merge processes comes into action and starts merging arrays back
swap(&arr[j], &arr[j+1]); till the complete array is merged.
}
{
arr[k] = L[i];
i++;
}
else
{
arr[k] = R[j];
j++;
}
k++;
}
Hashing is an improvement over Direct Access Table. The idea is to Index Mapping (or Trivial Hashing) with negatives allowed
use hash function that converts a given phone number or any other key
to a smaller number and uses the small number as index in a table Given a limited range array contains both positive and non-positive
called hash table. numbers, i.e., elements are in the range from -MAX to +MAX. Our
task is to search if some number is present in the array or not in O(1)
Hash Function: A function that converts a given big phone number to a time.
small practical integer value. The mapped integer value is used as an
index in hash table. In simple terms, a hash function maps a big Since range is limited, we can use index mapping (or trivial hashing).
number or string to a small integer that can be used as index in hash We use values as index in a big array. Therefore we can search and
table. insert elements in O(1) time.
A good hash function should have following properties
1) Efficiently computable.
2) Should uniformly distribute the keys (Each table position equally
likely for each key)
How to handle negative numbers? return false;
}
The idea is to use a 2D array of size hash[MAX+1][2]
void insert(int a[], int n)
Algorithm: {
for (int i = 0; i < n; i++) {
if (a[i] >= 0)
Assign all the values of the hash matrix as 0. has[a[i]][0] = 1;
Traverse the given array: else
has[abs(a[i])][1] = 1;
If the element ele is non negative assign }
hash[ele][0] as 1. }
Else take the absolute value of ele and
// Driver code
assign hash[ele][1] as 1. int main()
To search any element x in the array. {
int a[] = { -1, 9, -5, -8, -5, -2 };
int n = sizeof(a)/sizeof(a[0]);
If X is non-negative check if hash[X][0] is 1 or not. If hash[X][0] is insert(a, n);
one then the number is present else not present. int X = -5;
If X is negative take absolute vale of X and then check if hash[X][1] is if (search(X) == true)
cout << "Present";
1 or not. If hash[X][1] is one then the number is present else
Below is the implementation of the above idea. cout << "Not Present";
return 0;
// CPP program to implement direct index mapping }
// with negative values allowed.
#include <bits/stdc++.h> Hashing | Set 2 (Separate Chaining)
using namespace std;
#define MAX 1000
What is Collision?
// Since array is global, it is initialized as 0. Since a hash function gets us a small number for a key which is a big
bool has[MAX + 1][2]; integer or string, there is possibility that two keys result in same value.
// searching if X is Present in the given array The situation where a newly inserted key maps to an already occupied
// or not. slot in hash table is called collision and must be handled using some
bool search(int X) collision handling technique.
{
if (X >= 0) {
if (has[X][0] == 1) What are the chances of collisions with large table?
return true; Collisions are very likely even if we have big table to store keys. An
else
return false;
important observation is Birthday Paradox. With only 23 persons, the
} probability that two people have same birthday is 50%.
// if X is negative take the absolute
// value of X.
How to handle Collisions?
X = abs(X); There are mainly two methods to handle collision:
if (has[X][1] == 1) 1) Separate Chaining
return true;
2) Open Addressing
Separate Chaining: Time complexity of search insert and delete is
The idea is to make each cell of hash table point to a linked list of O(1) if α is O(1)
records that have same hash function value.
C++ program for hashing with chaining
Let us consider a simple hash function as “key mod 7” and sequence of
keys as 50, 700, 76, 85, 92, 73, 101. In hashing there is a hash function that maps keys to some values. But
hashChaining these hashing function may lead to collision that is two or more keys
are mapped to same value. Chain hashing avoids collision. The idea is
Advantages: to make each cell of hash table point to a linked list of records that
1) Simple to implement. have same hash function value.
2) Hash table never fills up, we can always add more elements to Let’s create a hash function, such that our hash table has ‘N’ number
chain. of buckets.
3) Less sensitive to the hash function or load factors.
4) It is mostly used when it is unknown how many and how frequently To insert a node into the hash table, we need to find the hash index for
keys may be inserted or deleted. the given key. And it could be calculated using the hash function.
Example: hashIndex = key % noOfBuckets
Disadvantages:
1) Cache performance of chaining is not good as keys are stored using Insert: Move to the bucket corresponds to the above calculated hash
linked list. Open addressing provides better cache performance as index and insert the new node at the end of the list.
everything is stored in same table. Delete: To delete a node from hash table, calculate the hash index for
2) Wastage of Space (Some Parts of hash table are never used) the key, move to the bucket corresponds to the calculated hash index,
3) If the chain becomes long, then search time can become O(n) in search the list in the current bucket to find and remove the node with
worst case. the given key (if found).
4) Uses extra space for links.
Performance of Chaining:
Performance of hashing can be evaluated under the assumption that
each key is equally likely to be hashed to any slot of table (simple
uniform hashing).
Open Addressing
Like separate chaining, open addressing is a method for handling
collisions. In Open Addressing, all elements are stored in the hash
table itself. So at any point, size of the table must be greater than or
equal to the total number of keys (Note that we can increase table size
by copying old data if needed).
Insert(k): Keep probing until an empty slot is found. Once an empty
slot is found, insert k.
Search(k): Keep probing until slot’s key doesn’t become equal to k or
an empty slot is reached.
Delete(k): Delete operation is interesting. If we simply delete a key,
then search may fail. So slots of deleted keys are marked specially as
“deleted”.
Insert can insert an item in a deleted slot, but the search doesn’t stop at
a deleted slot.
Open Addressing is done following ways: Clustering: The main problem with linear probing is clustering, many
consecutive elements form groups and it starts taking time to find a
a) Linear Probing: In linear probing, we linearly probe for next slot. free slot or to search an element.
For example, typical gap between two probes is 1 as taken in below b) Quadratic Probing We look for i2‘th slot in i’th iteration.
example also. let hash(x) be the slot index computed using hash function.
let hash(x) be the slot index computed using hash function and S be the
table size If slot hash(x) % S is full, then we try (hash(x) + 1*1) % S
If slot hash(x) % S is full, then we try (hash(x) + 1) % S If (hash(x) + 1*1) % S is also full, then we try (hash(x) + 2*2) % S
If (hash(x) + 1) % S is also full, then we try (hash(x) + 2) % S If (hash(x) + 2*2) % S is also full, then we try (hash(x) + 3*3) % S
If (hash(x) + 2) % S is also full, then we try (hash(x) + 3) % S ..................................................
.................................................. ..................................................
.................................................. c) Double Hashing We use another hash function hash2(x) and look
Let us consider a simple hash function as “key mod 7” and sequence of for i*hash2(x) slot in i’th rotation.
keys as 50, 700, 76, 85, 92, 73, 101. let hash(x) be the slot index computed using hash function.
If slot hash(x) % S is full, then we try (hash(x) + 1*hash2(x)) % S
If (hash(x) + 1*hash2(x)) % S is also full, then we try (hash(x) + S.No. Seperate Chaining Open Addressing
2*hash2(x)) % S
If (hash(x) + 2*hash2(x)) % S is also full, then we try (hash(x) +
3*hash2(x)) % S always add more elements
.................................................. to chain.
..................................................
See this for step by step diagrams. Open addressing
Comparison of above three:
Linear probing has the best cache performance but suffers from Chaining is Less sensitive requires extra care for to
clustering. One more advantage of Linear probing is easy to compute.
Quadratic probing lies between the two in terms of cache performance to the hash function or avoid clustering and load
and clustering.
3. load factors. factor.
Why rehashing?
Rehashing is done because whenever key value pairs are inserted into Priority Queue in C++ Standard Template Library
the map, the load factor increases, which implies that the time (STL)
complexity also increases as explained above. This might not give the
required time complexity of O(1). Priority queues are a type of container adapters, specifically designed
such that the first element of the queue is the greatest of all elements in
Hence, rehash must be done, increasing the size of the bucketArray so the queue and elements are in non decreasing order(hence we can see
as to reduce the load factor and the time complexity. that each element of the queue has a priority{fixed order}).
How Rehashing is done? Methods of priority queue are:
Rehashing can be done as follows:
priority_queue::empty() in C++ STL– empty() function returns
For each addition of a new entry to the map, check the load factor. whether the queue is empty.
If it’s greater than its pre-defined value (or default value of 0.75 if not priority_queue::size() in C++ STL– size() function returns the size of
given), then Rehash. the queue.
For Rehash, make a new array of double the previous size and make it priority_queue::top() in C++ STL– Returns a reference to the top most
the new bucketarray. element of the queue
Then traverse to each element in the old bucketArray and call the priority_queue::push() in C++ STL– push(g) function adds the element
insert() for each so as to insert it into the new larger bucket array. ‘g’ at the end of the queue.
priority_queue::pop() in C++ STL– pop() function deletes the first
element of the queue.
priority_queue::swap() in C++ STL– This function is used to swap the
contents of one priority queue with another priority queue of same type
and size.
priority_queue::emplace() in C++ STL – This function is used to insert
a new element into the priority queue container, the new element is
added to the top of the priority queue.
priority_queue value_type in C++ STL– Represents the type of object
stored as an element in a priority_queue. It acts as a synonym for the
template parameter.
#include <iostream>
#include <queue>
int main()
{
// empty set container
set <int, greater <int> > gquiz1;
// print all elements of the set gquiz2 The set gquiz2 after assign from gquiz1 is : 10 20 30 40 50
cout << "\nThe set gquiz2 after assign from gquiz1 is : ";
for (itr = gquiz2.begin(); itr != gquiz2.end(); ++itr)
60
{
cout << '\t' << *itr; gquiz2 after removal of elements less than 30 : 30 40 50 60
} gquiz2.erase(50) : 1 removed 30 40 60
cout << endl;
gquiz1.lower_bound(40) : 40
// remove all elements up to 30 in gquiz2 gquiz1.upper_bound(40) : 30
cout << "\ngquiz2 after removal of elements less than 30 : "; gquiz2.lower_bound(40) : 40
gquiz2.erase(gquiz2.begin(), gquiz2.find(30)); gquiz2.upper_bound(40) : 60
for (itr = gquiz2.begin(); itr != gquiz2.end(); ++itr)
{
cout << '\t' << *itr;
}
return 0;
}
Tree Traversals (Inorder, Preorder and Postorder)
Preorder Traversal (Practice):
Unlike linear data structures (Array, Linked List, Queues, Stacks, etc) Algorithm Preorder(tree)
which have only one logical way to traverse them, trees can be
1. Visit the root.
traversed in different ways. Following are the generally used ways for
traversing trees. 2. Traverse the left subtree, i.e., call Preorder(left-subtree)
3. Traverse the right subtree, i.e., call Preorder(right-subtree)
Uses of Preorder
Preorder traversal is used to create a copy of the tree. Preorder
traversal is also used to get prefix expression on of an expression tree.
Example: Preorder traversal for the above given figure is 1 2 4 5 3.