Thursday, 25 November 2021

Draft : Interview Material

 When to use semaphore/mutex:-

----------------------------


Here is how I remember when to use what -


Semaphore: Use a semaphore when you (thread) want to sleep till some other thread tells you to wake up. Semaphore 'down' happens in one thread (producer) and semaphore 'up' (for same semaphore) happens in another thread (consumer) e.g.: In producer-consumer problem, producer wants to sleep till at least one buffer slot is empty - only the consumer thread can tell when a buffer slot is empty.


Mutex: Use a mutex when you (thread) want to execute code that should not be executed by any other thread at the same time. Mutex 'down' happens in one thread and mutex 'up' must happen in the same thread later on. e.g.: If you are deleting a node from a global linked list, you do not want another thread to muck around with pointers while you are deleting the node. When you acquire a mutex and are busy deleting a node, if another thread tries to acquire the same mutex, it will be put to sleep till you release the mutex.


-------------------------------------------------------------------------------------------------------------------------------------


What are Conditional Variables in OS?


A conditional variable in operating system programming is a special kind of variable that is used to determine if a certain condition has been met or not. It is used to communicate between threads when certain conditions become true.


A conditional variable is like a queue. A thread stops its execution and enters the queue if the specified condition is not met. Once another thread makes that condition true, it sends a signal to the leading thread in the queue to continue its execution.


If there is no thread in the queue, the signal is wasted.


Supported actions

There are two types of actions that can be performed with condition variables:


wait

signal

We use the wait instruction in a thread if we want to halt the execution of that thread till a certain condition is met.


We use the signal instruction if we want to continue executing the leading thread in the waiting queue.


---------------------------------------------------------------------------------------------------------------------------------------------------------------------



Difference between a conditional variable and a semaphore:--


Often semaphore is confused with the conditional variable. Some of the differences between a semaphore and a conditional variable are listed in the table below:


Conditional Variable:--

The thread waits till a certain condition is met.

The signal() is wasted if there is no thread waiting.

We can run all waiting threads using broadcast unlocking.

The wait() function almost always block its caller.

It is mostly used with mutex. It signals the change in state from one thread to another.


Semaphore:---

Only those threads wait which make the value of the semaphore less than or equal to 0.

The sem_post() is not wasted even if no thread is waiting. The value of the semaphore is increased.

We can not run all waiting threads using broadcast unlocking.

The wait() function does not always block its caller.

It is mostly used to solve critical section problems.


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------




producer consumer problem:-

--------------------------



typedef struct 

{

char buf[SIZE];

int occupied;

int nextin;

int nextout;

pthread_mutex_t mutex;

pthread_cond_t more;

pthread_cond_t less;

}buffer_t;


void producer ( buffer_t *b, int item)

{

pthread_mutex_lock(&b->mutex);

while ( b->occupied >= SIZE)

pthread_cond_wait ( &b->less, &b->mutex);

b->buf[b->nextin++] = item;

b->nextIn = b->nextIn % SIZE;

b->occupied++;

pthread_cond_signal( &b->more); // this will unblock eht consumer to read 

pthread_mutex_unlock(&b->mutex);

}



char consumer( buffer_t *b)

{

char item;

pthread_mutex_lock(&b->mutex);

while ( b->occupied <= 0)

pthread_cond_wait(&b->more, &b->mutex);

item = buf[b->nextout++];

b->nextOut = b->nextOut%SIZE;

b->occupied--;

pthread_cond_signal(&b->less);

pthread_mutex_unlock(&b->mutes);

}



-------------------------------------------------------------------------------------------------


//arr is given find max consucitive sum of elements.


int maxConsecutiveSum ( int arr[])

{

int currentMax = arr[0], globalMax = arr[0];

for ( int i=0; i<n; i++)

{

if ( currentMax < 0 )

currentMax = arr[i];

else

currentMax = currentMax + arr[i];

if ( globalMax < currentMax)

globalMax = currentMax;

}

return globalMax;

}





-------------------------------------------------------------------------------------------------


Trees questions:-

-----------------



tree creations:-


struct node

{

int data;

struct node *left;

stcurt node *right;

};


node *createNode(int item)

{

node* newNode = new node();

newNode->data = item;

newNode->left = NULL;

newNode->right = NULL;

}




//sum of left leaves ....


int sumOfLeftLeaves ( node *root)

{

if ( root )

{

if ( root->left && !root->left->left && !root->left->right )

return root->left->data + sumOfLeftLeaves ( root->right);

return sumOfLeftLeaves ( root->left) + sumOfLeftLeaves ( root->right);

}

return 0;

}




//insert node in bst 


struct node *insert(struct node *root, int item)

{

if (root == NULL)

return (createNode(item));

if ( item > node->data )

return insert(root->left, item);

else

return insert(root->right, item);

return root;

}




//connect nodes at same level and last nodes of each level with first node of next level...

//basically it will create a linkedlist like structure were first node will be root and last node will last node of last level...


void linkllNOdesLevelWise( node *root)

{

node *lastNode, *currentNode;

lastNode = currentNode = root;

while ( currentNode != NULL )

{

if ( currentNode->left != NULL )

{

lastNode->next = currentNode->left;

last = currentNode->left;

}

if ( currentNode->right != NULL)

{

last->next = currentNode->right;

last = currentNode->right;

}

lastNode->next = NULL;

currentNode = currentNode->next;

}

}



//


// Least common ancestor of two nodes in tree.


struct node *lca ( struct node *root, int n1, int n2 )

{

struct node *temp = root;

while ( temp != NULL )

{

if ( temp->data > n1 && temp->data > n2 ) // means lca will lie in left subtree of this node.

{

temp = temp -> left; 

}

if else ( temp->data < n1 && temp->data < n2 )

{

temp = temp -> right;

}

else

break;

}

return temp;

}



time complexity - o(h)



//simple but incorrect


bool isBST(struct node *root)

{

if ( root == NULL )

retur true;

if ( (root->left && root->data < root->left->data) || (root->right && root->data > root->right->data ) )

{

return false;

}

if (  !isBST(root->left) || !isBST(root->right))

return false;

return true;

}


here there is a possibility that for each node it correct but if you see for whole tree then for some upper nodes its not correct.

so we need to check root->data with max(root->left) and root->data with min(root->right);


bool isBST(struct node *root)

{

if ( root == NULL )

retur true;

if ( (root->left && root->data <= maxValue(root->left) || (root->right && root->data >= minValue(root->right) ) )

{

return false;

}

if (  !isBST(root->left) || !isBST(root->right))

return false;

return true;

}


// Max depth or height of the tree


int maxHeight(strct node *root)

{

if ( root == NULL)

reutrn -1;

else

{

int ldepth = maxHeight( root->left);

int rdepth = maxHeight( root->right);

if ( ldepth > rdepth )

return ldepth + 1;

else

return rdepth + 1;

}

}


//Get max depth or heith using level wise traversal using queue.


1,NULL


int height ( strcut node *root)

{

int depth = 0;

queue <node*> q;

q.push(root);

q.push(NULL);

while ( !q.empty() )

{

node *temp = q.front();

q.pop();

if ( temp == NULL)

depth++;

if ( temp != NULL)

{

if ( temp -> left )

{

q.push(temp->left);

}

if ( temp->right)

{

q.push(temp->right);

}

}

else if ( !q.empty())

{

q.push(NULL);

}

}

return depth;


}


/////////////////////////////////////////


int findMin( struct node *root)

{

if ( root == NULL)

return INT_MIN;

int res = root->data;

int lres = findMin(root->left);

int rres = findMin(root->right);

if (lres > res)

        res = lres;

    if (rres > res)

        res = rres;

    return res;

}


///////////////////////////////////////////


Convert binary tree to BST

--------------------------

steps :- 

inorder BT and store in arr

sort the arr

inorder traversal of tree take  arr element and copy elements to tree 

modified tree will be BST....



void BT_to_BST( struct node *root)

{

if ( root == NULL)

return;

int n = count( root); // count the number of nodes .

int *arr = new int[n];

int i =0;

storeInOrder ( root, arr, &i);

qsort ( arr, n );

//copy arr elements to BT

i = 0;

arrToBSt( arr, root, &1);

delete [] arr;

}



int count ( struct node * root)

{

if ( root == NULL)

return 0;

return count ( root->left ) + count ( root->right) + 1;

}


void storeInOrder ( strcut node * root, int inorder [], int *index )

{

if ( node == NULL)

return;

storeInorder ( root->left, inorder, index );

inorder [*index] = root->data;

(*index)++;

storeInOrder ( root->right, inorder, index);

}


void arrToBst( struct node *root, int *arr, int *index)

{

if ( root == NULL)

return;

arrToBST( root->left, arr, index);

root->data = arr[*index];

(*index)++;

arrToBST( root->right, arr, index);

}


int main()

{

node *root = NULL;

root = createNode(10);

root->left = createNode(8);

root->right = createNode(12);

root->left->left = createNode(6);

root->left->right = createNode (7);

return;

}


//////////////////////////////////////////////



Smart pointers in c++

----------------------


Smart pointer :-


class SmartPtr

{

private:

T* m_data;

public:

SmartPtr (T* pValue )

{

m_data = pValue;

}

~SmartPtr()

{

delete m_data;

}

T & operator*()

{

return *m_data;

}

T & operator->()

{

return m_data;

}

};


int main ()

{

SmartPtr<int> *ptr ( new int());

*ptr = 10;

ptr->display();

return 0;

}


--------------------------------------------------------------

Unique pointer:-

---------------

two pointers can not be point to same object at the same time.

Here one pointer can pass the control to other pointer.


ex:- P1 = object1

p2 = object1 // here p1 passed its control to p2.

 

class rectangle 

{

int len;

int width;

public:

rectangle( int l, int b )

{

len = l;

width = b;

}

int area ( int l, int b)

{

return l * b;

}

};

 

int main ()

{

uniquePtr < rectangle > *ret1 = new rectangle ( 10, 5);

cout << ret1->area(); // it will print 50

uniquePtr < rectangle > *ret2;

ret2 = move ( ret1); // ret1 passed the control to ret2.

cout << ret2->area() // print 50

cout << ret1->area() // it will give an error as its not pointing to any object  now.

return 0;

}


 

Shared Pointer:-

----------------

Here basically more than one pointer shares particular object.

It maintains the reference count.


class rectangle 

{

int len;

int width;

public:

rectangle( int l, int b )

{

len = l;

width = b;

}

int area ( int l, int b)

{

return l * b;

}

};

 

int main ()

{

shared_ptr<rectangle> *p1 = new Rectangle(10, 5);

cout << p1->area() // print 50

shared_ptr<rectangle> *p2;

p2 = p1;

cout << p2->area(); // print 50

cout << p1->area(); // print 50, it will not give an error

cout << p1.useCount() // print 2 as reference count is 2

// Relinquishes ownership of p1 on the object

// and pointer becomes NULL

p1.reset();

cout << p1.get() << endl; // it will print null as it not pointing to any address now

cout << p2.use_count() << endl; // print 1

cout << p2.get() << endl; // it will give an address of object as its still pointing the same.

}



Weak pointer:-

--------------

This is used in place of shared_ptr in one problem - Cyclic dependency problem.

Here suppose we have two classs A and B and have one - one shared pointer in each class which can access to object of each others classes.

So here if we use shared pointer then it will always maintain a reference of each other and will not allow to delete the either of the objects.


So here weak pointer work if we declare either of one pointer as weak.




class Singleton

{

private:

static Singleton *Instance;

Singleton();

public:

Singleton *getInstance();

};



Singleton* Singleton::Instance = 0;


Singleton* Singleton::getInstance()

{

if ( Instance == NULL )

{

instance = new Singleton();

}

return instance;

}



singletone:-

-------------


#include <iostream>


class Singleton

{

    private:

        /* Here will be the instance stored. */

        static Singleton* instance;


        /* Private constructor to prevent instancing. */

        Singleton();


    public:

        /* Static access method. */

        static Singleton* getInstance();

};


/* Null, because instance will be initialized on demand. */

Singleton* Singleton::instance = 0;


Singleton* Singleton::getInstance()

{

    if (instance == 0)

    {

        instance = new Singleton();

    }


    return instance;

}


Singleton::Singleton()

{}


int main()

{

    //new Singleton(); // Won't work

    Singleton* s = Singleton::getInstance(); // Ok

    Singleton* r = Singleton::getInstance();


    /* The addresses will be the same. */

    std::cout << s << std::endl;

    std::cout << r << std::endl;


--------------------------------------------------------------------------------------------------

Need to read about PKI --


-PKI - its a software, encryption technologies, security methods used by organization to secure their data or to secure their business communications.

-How PKI helpful:

PKI provides below things.

Confidentiality - by data encryption

Integrity - by digital signature - 

1.sender calculate hash of data using any hash algorithm ( sha256). - this the part1

2.Sender encrypts the hash with its own private key and create a signature -- this is called a digital signature.

3.sender send the encrypted data by senders public key + hash + algo used in hash in plain text + its own digital signature.

4.reciever will decrypt the data with its own private key, calculate hash on date using same hash algorithm and calculate a hash.

5.Now reciever will decrpty the digital signatures by senders public key and get the hash. Now he will compare both the hash to check integrity.



Authenticity :- Its provided by digital signatures, hash algorithm - if reciever can decrypt the data it means that data is encrypted by its corresponding public key. 

Non-repudiation:- Digital signatures or audit logs - sender cant deny that its not sent data. 

Availability - multiple CAs are available to sign or allocate certificate to user/computer/program


- all component of PKI  - Digital certificate, CA, digital signatures, hashing, message digest etc.


- How key length plays the role in security:-

Keys with more length will provide more security to data but it slows the encryption performance.

ex: RSA-2048 it will provide more security in encryption compared with 3DES ( 168 bits ) or AES-256 bits.


So what to do to achieve better security + better performance.

Do encryption of data with symmetric key algo ( like AES,3DES) + encrypt that symmetric key only with assymetric key ( RSA-2048).


- Use of ciphers which agreed in ssl handshake.

- Use of digital signature ..

Digital signature means - identity of user can be used in integrity/ authenticity


Hashing algorithms:-

MD5 -- 128 bbits to generate the hash 

SHA0 -- 168 bits 

SHA1 -- 168 bits --  0 and 1 both got deprecated in 2017.


SHA2 -- now a days we are using SHA2 ( SHA-224 or SHA256 )

SHA3


- server and client verification through server/client certificates.


- Why KS required?

- about security tools - blackduck, qualys.

- How to prevent many well known attacks like ddos, sql injuction. 

- How and which tools are helpful to use for prevention of such kind of attacks.

- difference between encoding and encryption 


---------------------------------------------------------------------------------------------------



How ssl works:-

Client side:-

CA - certificate authorities will provide the public keys to browser venders , these vendor keep on updating these public keys to correspondign browser, in this way all browsers have public keys of all the CAs.


Server side:- When any one wants to make secure communication for his site then he first create a CSR ( certificate signinig request ) and get it signed by CA.

in signing process basically CA encrypts checksome of server ( CSR ) with its private key and corresponding public key shares with browser vender. So at this poing all the browser must have corresponding public key via which CA signs any server's CSR.


Now comes to communication part:- ( high level of SSL handshake )

Here once client wants to communicate wtih server, it send connect request.

server response with its certificate.

client verfies its certificate by looking up corresponding CA's public key. its decrypts the certs and find server cert checksome also create hash and calculate checksome. Now it compares both the checksome.


Structure of signatures:-

- Version

- serial number

- signature algo

- Issuer

-- issued to

-- issued by

- Vality

-- start date

-- end date

-Subject public key

- X509 extension

- Signature 

-- Signatures algo 

-- Signatures


PKCS -- things need to covers....

x509 formats and other formats ....

some ssl apis() need to cover ...

RSA vs ECC --





=====================================================================================================================================================


Bitwise operator :

-------------------

The following are many other interesting problems using XOR operator:

---------------------------------------------------------------------


Return true in case two numbers have opposite sign.


n1 = -13

n2= 10 


Return true


for n1  = 8, n2 = 10 return false


--------------------


singbit of positive number is - 0

signbit of negative number is - 1


if we do xor ( eg: 1 xor 0 = 1, 1 xor 1 = 0, 0 xor 0 = 0 ) 

so do xor of these tow numbers - if result is < 0 ( negative ) means xor of signbits are 1 ( -ve num).



bool opposite( int num1, int num2)

{

return (( x ^ y) < 0);

}



int main ()

{

int num1 = -10;

int num2 = 5;

if ( oppositeSign( num1, num2) < 1 )

{

signs are opposite

}

else

signs are same

}


---------------------------------------------------------------------------------


Wednesday, 20 October 2021

Distibuted system design

 User thinks that he is contacting with one computer only. But there are bunch of other machines/hardware/services are there in background. These machines/servies which runs in background are called distributed system.

Fallacies of Distributed system:-

Network is reliable

Latency is zero

Bandwidth is infinite

Topology doesnt change

Network is secure

Only one administration

Transport cost is zero


Distributed system Characterstics:-

- No shared clock 

- No shared Memory - each component having their own storage

- Shared resources - Anything should be shareable among the nodes eg; hardware/software/data.

- Concurrency and Consistency 

Distributed system communication:-

- Different parts of distributed systems needs to be able to talk.

- Requires agreed upon format and protocol.

- Lots of things can go wrong. Need to handle them.

--Client cant find server.

--Server crashes in between

--Server processed the request but somehow its response is not reached to client (network failed)

--Client crashes

Benefits of Distributed system:-

- More reliable, fault tolerant - high availability

- Scalability - multimachines or nodes we can add dependes of traffic

- Lower latency, increased performance - As lots of servers running in background

-Cost effective

System design performance matrics:-

Scalability:-

--Ability of a system to grow and manage increased traffic. Horizontal or vertical scalling needs to be applied.

--Increased volume of data or request

Reliability:-

--Probability a system will fail during a period of time

Failure can belongs to software or hardware. Need to monitor both by healthcheck related scripts on productions and take neccessory actions to reduce the chances of failure.

And other automated testing can be performed on production to find the bugs if any.

Any product is reliable when it keeps on working even after failure of hardware/software.

Availability:-

--Amount of time a system is operational during a period of time. High availability we need to use here. If one server failes, redirect requests to other server. In this way server is always available for the clients.

--Poorly designed system requires more downtime for the updates. I remember a decades ago where we need to update a small thing we used to down whole site and update it early morning when traffic flow was minimum.

Availability calculations:-

Availability % = ( available time / total time ) x 100

                        = 23/24 x 100 = 95.83% = 360 hrs in an year = 15 days of downtime required annually 

                        if availability is 95.83%.

Reliable Vs Available:-

- Reliable system is always an available system.

- Availablity can be achieved by high availabiliy , means by adding more servers, creating clusters and replications of those servers.

- Reliability says to detect early failure of software or hardware. So that traffic can be redirected working software/hardware timely.

- Reliable software are more profitable - because it doesnt required more servers/clusters/replications.

- Depends on your software you can think to give more priority to availability or reliablity. Eg: suppose software is social media, if you are posting a video and it got failed - no need to worry you can try it later...means its less reliable and you can live with it. But at same time if you take an example of plane and its in air. here reliability is more important that availability. you can't take risk to rely on availability.


                       






Tuesday, 5 April 2016

Some useful sites

C++ coding guidelines for beginners:-
-----------------------------------------------

https://www3.ntu.edu.sg/home/ehchua/programming/cpp/cp3_OOP.html

Linux and Networking:-
-----------------------------
Linux Kernel Development
Novel by Robert Love
https://doc.lagout.org/operating%20system%20/linux/Linux%20Kernel%20Development%2C%203rd%20Edition.pdf

HYPERLINK "http://rads.stackoverflow.com/amzn/click/0131411551"W. Richard Stevens, Unix Network Programming, Volume 1: The Sockets Networking API

http://www.kohala.com/start/

https://www.cs.stevens.edu/~jschauma/810/

https://scoecomp.files.wordpress.com/2014/02/2003-unix-network-programming-vol-1-3rd-ed.pdf



Friday, 22 January 2016

Standard Template Library

C++ Templates:-
-----------------------
Templates are a feature of the C++ programming language that allows functions and classes to operate with generic types. This allows a function or class to work on many different data types without being rewritten for each one.
What are generic functions or classes?
Many times while programming, there is a need for creating functions which perform the same operations but work with different data types. So C++ provides a feature to create a single generic function instead of many functions which can work with different data type by using the template parameter.

What is the template parameter?
The way we use normal parameters to pass as a value to function, in the same manner template parameters can be used to pass type as argument to function. Basically, it tells what type of data is being passed to the function.
The syntax for creating a generic function:
template <class  type> return-type function-name (parameter-list)
Here, ‘type’ is just a placeholder used to store the data type when this function is used you can use any other name instead class is used to specify the generic type of template, alternatively typename can be used instead of it.
Let’s try to understand it with an example:
Assume we have to swap two variables of int type and two of float type. Then, we will have to make two functions where one can swap int type variables and the other one can swap float type variables. But here if we use a generic function, then we can simply make one function and can swap both type of variables by passing their different type in the arguments. Let’s implement this:

#include <iostream>
using namespace std ;
// creating a generic function ‘swap (parameter-list)’ using template :
template <class X> 
void swap( X &a, X &b) {
    X tp;
    tp = a;
    a = b;
    b = tp;
    cout << " Swapped elements values of a and b are  " << a << " and  " << b << " respectively " << endl;
}

int main( ) {
    int a = 10, b = 20 ;
    float c = 10.5, d = 20.5 ;
            swap(a , b);          // function swapping ‘int’ elements 
    swap(c , d);                  // function swapping ‘float’ elements 
    return 0;
}
 
Output :
Swapped elements values of a and b are  20 and 10 respectively.
Swapped elements values of a and b are  20.5 and 10.5 respectively.
After creating the generic function, compiler will automatically generate correct code for the type of data used while executing the function.

C++ STL also has some containers (pre-build data structures) like vectors, iterators, pairs etc. These are all generic class which can be used to represent collection of any data type.

Iterator
An iterator is any object that, points to some element in a range of elements (such as an array or a container) and has the ability to iterate through those elements using a set of operators (with at least the increment (++) and dereference (*) operators).
A pointer is a form of an iterator. A pointer can point to elements in an array, and can iterate over them using the increment operator (++). There can be other types of iterators as well. For each container class, we can define iterator which can be used to iterate through all the elements of that container.
Example:
For Vector:
vector <int>::iterator  it;
For List:
list <int>::iterator it;
You will see the implementations using iterators in the topics explained below.

String
C++ provides a powerful alternative for the char*. It is not a built-in data type, but is a container class in the Standard Template Library. String class provides different string manipulation functions like concatenation, find, replace etc. Let us see how to construct a string type.
string s0;                                       // s0 = “”
string s1(“Hello”);                               // s1 = “Hello”
string s2 (s1);                                  // s2 = “Hello”
string s3 (s1, 1, 2);                            // s3 = “el”
string s4 ("Hello World", 5);                     // s4 = “Hello”
string s5 (5, ‘*’);                              // s5 = “*****”
string s6 (s1.begin(), s1.begin()+3);              // s6 = “Hel”
Here are some member functions:
append(): Inserts additional characters at the end of the string (can also be done using ‘+’ or ‘+=’ operator). Its time complexity is O(N) where N is the size of the new string.
assign(): Assigns new string by replacing the previous value (can also be done using ‘=’ operator).
at(): Returns the character at a particular position (can also be done using ‘[ ]’ operator). Its time complexity is O(1).
begin(): Returns an iterator pointing to the first character. Its time complexity is O(1).
clear(): Erases all the contents of the string and assign an empty string (“”) of length zero. Its time complexity is O(1).
compare(): Compares the value of the string with the string passed in the parameter and returns an integer accordingly. Its time complexity is O(N + M) where N is the size of the first string and M is the size of the second string.
copy(): Copies the substring of the string in the string passed as parameter and returns the number of characters copied. Its time complexity is O(N) where N is the size of the copied string.
c_str(): Convert the string into C-style string (null terminated string) and returns the pointer to the C-style string. Its time complexity is O(1).
empty(): Returns a boolean value, true if the string is empty and false if the string is not empty. Its time complexity is O(1).
end(): Returns an iterator pointing to a position which is next to the last character. Its time complexity is O(1).
erase(): Deletes a substring of the string. Its time complexity is O(N) where N is the size of the new string.
find(): Searches the string and returns the first occurrence of the parameter in the string. Its time complexity is O(N) where N is the size of the string.
insert(): Inserts additional characters into the string at a particular position. Its time complexity is O(N) where N is the size of the new string.
length(): Returns the length of the string. Its time complexity is O(1).
replace(): Replaces the particular portion of the string. Its time complexity is O(N) where N is size of the new string.
resize(): Resize the string to the new length which can be less than or greater than the current length. Its time complexity is O(N) where N is the size of the new string.
size(): Returns the length of the string. Its time complexity is O(1).
substr(): Returns a string which is the copy of the substring. Its time complexity is O(N) where N is the size of the substring.
Implementation:
#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    string s, s1;
    s = "HELLO";
    s1 = "HELLO";
    if(s.compare(s1) == 0)
        cout << s << " is equal to " << s1 << endl;
    else
        cout << s << " is not equal to " << s1 << endl;
    s.append(" WORLD!");
    cout << s << endl;
    printf("%s\n", s.c_str());
    if(s.compare(s1) == 0)
        cout << s << " is equal to " << s1 << endl;
    else
        cout << s << " is not equal to " << s1 << endl;
    return 0;
}
Output:
HELLO is equal to HELLO
HELLO WORLD!
HELLO WORLD!
HELLO WORLD! is not equal to HELLO
 
Vector:-
Vectors are sequence containers that have dynamic size. In other words, vectors are dynamic arrays. Just like arrays, vector elements are placed in contiguous storage location so they can be accessed and traversed using iterators. To traverse the vector we need the position of the first and last element in the vector which we can get through begin() and end() or we can use indexing from 0 to size(). Let us see how to construct a vector.
vector<int> a;                                       // empty vector of ints
vector<int> b (5, 10);                                // five ints with value 10
vector<int> c (b.begin(),b.end());                     // iterating through second
vector<int> d (c);                                   // copy of c
Some of the member functions of vectors are:
at(): Returns the reference to the element at a particular position (can also be done using ‘[ ]’ operator). Its time complexity is O(1).
back(): Returns the reference to the last element. Its time complexity is O(1).
begin(): Returns an iterator pointing to the first element of the vector. Its time complexity is O(1).
clear(): Deletes all the elements from the vector and assign an empty vector. Its time complexity is O(N) where N is the size of the vector.
empty(): Returns a boolean value, true if the vector is empty and false if the vector is not empty. Its time complexity is O(1).
end(): Returns an iterator pointing to a position which is next to the last element of the vector. Its time complexity is O(1).
erase(): Deletes a single element or a range of elements. Its time complexity is O(N + M) where N is the number of the elements erased and M is the number of the elements moved.
front(): Returns the reference to the first element. Its time complexity is O(1).
insert(): Inserts new elements into the vector at a particular position. ts time complexity is O(N + M) where N is the number of elements inserted and M is the number of the elements moved .
pop_back(): Removes the last element from the vector. Its time complexity is O(1).
push_back(): Inserts a new element at the end of the vector. Its time complexity is O(1).
resize(): Resizes the vector to the new length which can be less than or greater than the current length. Its time complexity is O(N) where N is the size of the resized vector.
size(): Returns the number of elements in the vector. Its time complexity is O(1).

Traverse:
void traverse(vector<int> v)
{
    vector <int>::iterator it;
    for(it = v.begin();it != v.end();++it)
        cout << *it <<   ‘;
    cout << endl;
    for(int i = 0;i < v.size();++i)
        cout << v[i] <<  ‘;
    cout << endl;
 }

Implementation:
#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector <int> v;
    vector <int>::iterator it;
    v.push_back(5);
    while(v.back() > 0)
        v.push_back(v.back() - 1);
    for(it = v.begin(); it != v.end();++it)
        cout << *it << ' ';
    cout << endl;
    for(int i = 0;i < v.size();++i)
        cout << v.at(i) << ' ';
    cout << endl;
    while(!v.empty())
    {
        cout << v.back() << ' ';
        v.pop_back();
    }
    cout << endl;
    return 0;
}
Output:
5 4 3 2 1 0
5 4 3 2 1 0
0 1 2 3 4 5
 
Question 1:- So the question is where memory gets created when we create a vector.
--Vector used defaults allocator, that uses 'new' to create a memory, so by default
memory gets created in heap.
Its all about whats inside allocator, if you want to create memory in stack use 
customize allocator, allocate memory without using 'new'.

Question 2:- How its maintain its dynamic array characterstics.
Suppose we have created a vector of size 10 and now i want to store 20 more elements,
in that case it , copies 10 elements temprorily and erase all 10 memory already created,
Now it will allocate memory for capacity 30 or more and store temprory saved values
and then next 20 elements. 



List:-
----------
List is a sequence container which takes constant time in inserting and removing elements. List in STL is implemented as Doubly Link List.
The elements from List cannot be directly accessed. For example to access element of a particular position ,you have to iterate from a known position to that particular position.

//declaration
list <int> LI;
Here LI can store elements of int type.
// here LI will have 5 int elements of value 100.
list<int> LI(5, 100)
Some of the member function of List:
begin( ): It returns an iterator pointing to the first element in list.Its time complexity is O(1).
end( ): It returns an iterator referring to the theoretical element(doesn’t point to an element) which follows the last element.Its time complexity is O(1).
empty( ): It returns whether the list is empty or not.It returns 1 if the list is empty otherwise returns 0.Its time complexity is O(1).
assign( ): It assigns new elements to the list by replacing its current elements and change its size accordingly.It time complexity is O(N).
back( ): It returns reference to the last element in the list.Its time complexity is O(1).
erase( ): It removes a single element or the range of element from the list.Its time complexity is O(N).
front( ): It returns reference to the first element in the list.Its time complexity is O(1).
push_back( ): It adds a new element at the end of the list, after its current last element. Its time complexity is O(1).
push_front( ): It adds a new element at the beginning of the list, before its current first element. Its time complexity is O(1).
remove( ): It removes all the elements from the list, which are equal to given element. Its time complexity is O(N).
pop_back( ): It removes the last element of the list, thus reducing its size by 1. Its time complexity is O(1).
pop_front( ): It removes the first element of the list, thus reducing its size by 1. Its time complexity is O(1).
insert( ): It insert new elements in the list before the element on the specified position. Its time complexity is O(N).
reverse ( ): It reverses the order of elements in the list. Its time complexity is O(N).
size( ): It returns the number of elements in the list. Its time complexity is O(1).

Implementation:
#include <iostream>
#include <list>
using namespace std;
int main()
{
    list <int> LI;
    list <int>::iterator it;
    //inserts elements at end of list
    LI.push_back(4);
    LI.push_back(5);

    //inserts elements at beginning of list
    LI.push_front(3);
    LI.push_front(5);

    //returns reference to first element of list
    it = LI.begin();

    //inserts 1 before first element of list
    LI.insert(it,1);

    cout<<"All elements of List LI are: " <<endl;
    for(it = LI.begin();it!=LI.end();it++)
    {
        cout<<*it<<" ";
    }
    cout<<endl;

    //reverse elements of list
    LI.reverse();

    cout<<"All elements of List LI are after reversing: " <<endl;
    for(it = LI.begin();it!=LI.end();it++)
    {
         cout<<*it<<" ";
    }
    cout<<endl;

    //removes all occurences of 5 from list
    LI.remove(5);

    cout<<"Elements after removing all occurence of 5 from List"<<endl;
    for(it = LI.begin();it!=LI.end();it++)
    {
         cout<<*it<<" ";
    }
    cout<<endl;

    //removes last element from list
    LI.pop_back();
    //removes first element from list
    LI.pop_front();
    return 0;
}
Output:
All elements of List LI are: 
1 5 3 4 5 
All elements of List LI are after reversing: 
5 4 3 5 1 
Elements after removing all occurrence of 5  from List
4 3 1
 
Pair:-
--------
Pair is a container that can be used to bind together a two values which may be of different types. Pair provides a way to store two heterogeneous objects as a single unit.
pair <int, char> p1;                    // default
pair <int, char> p2 (1, a’);            // value inititialization
pair <int, char> p3 (p2);               // copy of p2
We can also initialize a pair using make_pair() function. make_pair(x, y) will return a pair with first element set to x and second element set to y.
p1 = make_pair(2, b’);
To access the elements we use keywords, first and second to access the first and second element respectively.
cout << p2.first <<   << p2.second << endl;
 
Implementation:
#include <iostream>
#include <utility>

using namespace std;

int main()
{
    pair <int, char> p;
    pair <int, char> p1(2, 'b');
    p = make_pair(1, 'a');
    cout << p.first << ' ' <<  p.second << endl;
    cout << p1.first << ' ' << p1.second << endl;
    return 0;
}
Output:
1 a
2 b
 
 
Sets:-
-------
Data structure used: Ordered balanced BST.

Sets are containers which store only unique values and permit easy look ups. The values in the sets are stored in some specific order (like ascending or descending). Elements can only be inserted or deleted, but cannot be modified. We can access and traverse set elements using iterators just like vectors.
set<int> s1;                               // Empty Set
int a[]= {1, 2, 3, 4, 5, 5};
set<int> s2 (a, a + 6);                    // s2 = {1, 2, 3, 4, 5}
set<int> s3 (s2);                          // Copy of s2
set<int> s4 (s3.begin(), s3.end());         // Set created using iterators
Some of the member functions of set are:
begin(): Returns an iterator to the first element of the set. Its time complexity is O(1).
clear(): Deletes all the elements in the set and the set will be empty. Its time complexity is O(N) where N is the size of the set.
count(): Returns 1 or 0 if the element is in the set or not respectively. Its time complexity is O(logN) where N is the size of the set.
empty(): Returns true if the set is empty and false if the set has at least one element. Its time complexity is O(1).
end(): Returns an iterator pointing to a position which is next to the last element. Its time complexity is O(1).
erase(): Deletes a particular element or a range of elements from the set. Its time complexity is O(N) where N is the number of element deleted.
find(): Searches for a particular element and returns the iterator pointing to the element if the element is found otherwise it will return the iterator returned by end(). Its time complexity is O(logN) where N is the size of the set.
insert(): insert a new element. Its time complexity is O(logN) where N is the size of the set.
size(): Returns the size of the set or the number of elements in the set. Its time complexity is O(1).
Traverse:
void traverse(set<int> s)
{
    set <int>::iterator it;
    for(it = s.begin();it != s.end();++it)
        cout << *it <<   ‘;
    cout << endl;
 }
Implementation:
#include <iostream>
#include <set>

using namespace std;

int main()
{
    set <int> s;
    set <int>::iterator it;
    int A[] = {3, 5, 2, 1, 5, 4};
    for(int i = 0;i < 6;++i)
        s.insert(A[i]);
    for(it = s.begin();it != s.end();++it)
        cout << *it << ' ';
    cout << endl;
    return 0;
}
Output:
1 2 3 4 5
 
 
Maps:-
---------
Data Structure used:- pair stored in ordered balanced BST.

Maps are containers which store elements by mapping their value against a particular key. It stores the combination of key value and mapped value following a specific order. Here key value are used to uniquely identify the elements mapped to it. The data type of key value and mapped value can be different. Elements in map are always in sorted order by their corresponding key and can be accessed directly by their key using bracket operator ([ ]).
In map, key and mapped value have a pair type combination,i.e both key and mapped value can be accessed using pair type functionalities with the help of iterators.
//declaration. Here key values are of char type and mapped values(value of element) is of int type.
map <char ,int > mp;

mp[‘b’]  = 1;
It will map value 1 with key ‘b’. We can directly access 1 by using mp[ ‘b’ ].
mp[‘a’] = 2;
It will map value 2 with key ‘a’.
In map mp , the values be will be in sorted order according to the key.
Note: N is the number of elements in map.
Some Member Functions of map:
at( ): Returns a reference to the mapped value of the element identified with key.Its time complexity is O(logN).
count( ): searches the map for the elements mapped by the given key and returns the number of matches.As map stores each element with unique key, then it will return 1 if match if found otherwise return 0.Its time complexity is O(logN).
clear( ): clears the map, by removing all the elements from the map and leaving it with its size 0.Its time complexity is O(N).
begin( ): returns an iterator(explained above) referring to the first element of map.Its time complexity is O(1).
end( ): returns an iterator referring to the theoretical element(doesn’t point to an element) which follows the last element.Its time complexity is O(1).
empty( ): checks whether the map is empty or not. It doesn’t modify the map.It returns 1 if the map is empty otherwise returns 0.Its time complexity is O(1).
erase( ): removes a single element or the range of element from the map.
find( ): Searches the map for the element with the given key, and returns an iterator to it, if it is present in the map otherwise it returns an iterator to the theoretical element which follows the last element of map.Its time complexity is O(logN).
insert( ): insert a single element or the range of element in the map.Its time complexity is O(logN), when only element is inserted and O(1) when position is also given.
Implementation:
#include <iostream>
#include <map>
using namespace std;
int main(){
    map <char,int> mp;
    map <char,int> mymap,mymap1;

    //insert elements individually in map with the combination of key value and value of element
    mp.insert(pair<char,int>('a',2));   //key is 'c' and 2 is value.
    mp.insert(pair<char,int>('b',1));
    mp.insert(pair<char,int>('c',43));

    //inserts elements in range using insert() function in map 'mymap'.
    mymap.insert(mp.begin(),mp.end());

    //declaring iterator for map
    map <char,int>::iterator it;

    //using find() function to return reference of element mapped by key 'b'.
    it = mp.find('b');

    //prints key and element's value.
    cout<<"Key and element's value of map are: ";
    cout<<it->first<<" and "<<it->second<<endl;

    //alternative way to insert elements by mapping with their keys.
    mymap1['x'] = 23;
    mymap1['y'] = 21;

    cout<<"Printing element mapped by key 'b' using at() function : "<<mp.at('b')<<endl;

    //swap contents of 2 maps namely mymap and mymap1.
    mymap.swap(mymap1);

    /* prints swapped elements of mymap and mymap1 by iterating all the elements through    
        using   iterator. */
    cout<<"Swapped elements and their keys of mymap are: "<<endl;
    for(it=mymap.begin();it!=mymap.end();it++)
    {
    cout<<it->first<<" "<<it->second<<endl;
    }
    cout<<"Swapped elements and their keys of mymap1 are: "<<endl;
    for(it=mymap1.begin();it!=mymap1.end();it++)
    {
    cout<<it->first<<" "<<it->second<<endl;
    }
    //erases element mapped at 'c'.
    mymap1.erase('c');

    //prints all elements of mymap after erasing element at 'c'.

    cout<<"Elements of mymap1 after erasing element at key 'c' : "<<endl;
    for(it=mymap1.begin();it!=mymap1.end();it++)
    {
    cout<<it->first<<" "<<it->second<<endl;
    }

    //erases elements in range from mymap1
    mymap1.erase(mymap1.begin(),mymap1.end());

    cout<<"As mymap1 is empty so empty() function will return 1 : " << mymap1.empty()<<endl;

    //number of elements with key = 'a' in map mp.
    cout<<"Number of elements with key = 'a' in map mp are : "<<mp.count('a')<<endl;

    //if mp is empty then itmp.empty will return 1 else 0.
    if(mp.empty())
    {
        cout<<"Map is empty"<<endl;
    }
    else
    {
        cout<<"Map is not empty"<<endl;
    }

    return 0;
}
Output:
Key and element's value of map are: b and 1
Printing element mapped by key 'b' using at() function : 1
Swapped elements and their keys of mymap are: 
x 23
y 21
Swapped elements and their keys of mymap1 are: 
a 2
b 1
c 43
Elements of mymap1 after erasing element at key 'c' : 
a 2
b 1
As mymap1 is empty so empty() function will return 1 : 1
Number of elements with key = 'a' in map mp are : 1
Map is not empty
 
Stacks:
Stack is a container which follows the LIFO (Last In First Out) order and the elements are inserted and deleted from one end of the container. The element which is inserted last will be extracted first.
Declaration:
stack <int> s;
Some of the member functions of Stack are:
push( ): Insert element at the top of stack. Its time complexity is O(1).
pop( ): removes element from top of stack. Its time complexity is O(1).
top( ): access the top element of stack. Its time complexity is O(1).
empty( ): checks if the stack is empty or not. Its time complexity is O(1).
size( ): returns the size of stack. Its time complexity is O(1).
Implementation:
#include <iostream>
#include <stack>

using namespace std;
int main( )
{
    stack <int> s;  // declaration of stack

    //inserting 5 elements in stack from 0 to 4.
    for(int i = 0;i < 5; i++)
    {
        s.push( i ) ;
    }

    //Now the stack is {0, 1, 2, 3, 4}

    //size of stack s
    cout<<”Size of stack is:  <<s.size( )<<endl;

    //accessing top element from stack, it will be the last inserted element.
    cout<<”Top element of stack is:  <<s.top( ) <<endl ;

    //Now deleting all elements from stack 
    for(int i = 0;i < 5;i++)
    {
        s.pop( );
    }

    //Now stack is empty,so empty( ) function will return true.

    if(s.empty())
    {
        cout <<”Stack is empty.”<<endl;
    }
    else
    {
        cout <<”Stack is Not empty.”<<endl;
    }

    return 0;

}
Output:
Size of stack is: 5
Top element of stack is: 4
Stack is empty.
Queues:
Queue is a container which follows FIFO order (First In First Out) . Here elements are inserted at one end (rear ) and extracted from another end(front) .
Declaration:
queue <int> q;
Some member function of Queues are:
push( ): inserts an element in queue at one end(rear ). Its time complexity is O(1).
pop( ): deletes an element from another end if queue(front). Its time complexity is O(1).
front( ): access the element on the front end of queue. Its time complexity is O(1).
empty( ): checks if the queue is empty or not. Its time complexity is O(1).
size( ): returns the size of queue. Its time complexity is O(1).
Implementation:
#include <iostream>
#include <cstdio>
#include <queue>

using namespace std;

int main() {
    char qu[4] = {'a', 'b', 'c', 'd'};        
    queue <char> q;
    int N = 3;                            // Number of steps
    char ch;
    for(int i = 0;i < 4;++i)
        q.push(qu[i]);
    for(int i = 0;i < N;++i) {
            ch = q.front();
    q.push(ch);
            q.pop();
    }
    while(!q.empty()) {
        printf("%c", q.front());
        q.pop();
    }
    printf("\n");
    return 0;
}
Output:
dabc
Priority Queue:
A priority queue is a container that provides constant time extraction of the largest element, at the expense of logarithmic insertion. It is similar to the heap in which we can add element at any time but only the maximum element can be retrieved. In a priority queue, an element with high priority is served before an element with low priority.
Declaration:
priority_queue<int> pq;
Some member functions of priority queues are:
empty(): Returns true if the priority queue is empty and false if the priority queue has at least one element. Its time complexity is O(1).
pop(): Removes the largest element from the priority queue. Its time complexity is O(logN) where N is the size of the priority queue.
push(): Inserts a new element in the priority queue. Its time complexity is O(logN) where N is the size of the priority queue.
size(): Returns the number of element in the priority queue. Its time complexity is O(1).
top(): Returns a reference to the largest element in the priority queue. Its time complexity is O(1).
Implementation:
#include <iostream>
#include <queue>

using namespace std;

int main()
{
    priority_queue<int> pq;
    pq.push(10);
    pq.push(20);
    pq.push(5);
    while(!pq.empty())
    {
        cout << pq.top() << endl;
        pq.pop();
    }
    return 0;
}
Output:
20
10
5