STL - krishnaramb/cplusplus Wiki

Introduction

Container Overview

template<typename T, typename A = allocator<T>>
class vector {
  // ...
};

Container Representation

Size and Capacity

The size is the number of elements in the container; the capacity is the number of elements that a container can hold before allocating more memory

Iterators

Iterators description
p=c.end() p points to one-past-last element of c
cp=c.cbegin() p points to constant first element of c
p=c.cend() p points to constant one-past-last element of c
p=c.rbegin() p points to first element of reverse sequence of c
p=c.rend() p points to one-past-last element of reverse sequence of c
p=c.crbegin() p points to constant first element of reverse sequence of c
p=c.crend() p points to constant one-past-last element of reverse sequence of c
for (auto& x : v) // implicit use of v.begin() and v.end()
  cout << x << '\n';
for (auto p = v.begin(); p!=end(); ++p) {
  if (p!=v.begin() && ∗(p−1)==∗p)
    cout << "duplicate " << ∗p << '\n';
}
for (auto p = v.cbegin(); p!=cend(); ++p) { // use const iterators
  if (p!=v.cbegin() && ∗(p−1)==∗p)
    cout << "duplicate " << ∗p << '\n';
}

vector

The vector’s template argument and member types are defined like this:

template<typename T, typename Allocator = allocator<T>>
class vector {
public:
  using reference = value_type&;
  using const_reference = const value_type&;
  using iterator = /* implementation-defined */;
  using const_iterator = /* implementation-defined */;
  using size_type = /* implementation-defined */;
  using difference_type = /* implementation-defined */;
  using value_type = T;
  using allocator_type = Allocator;
  using pointer = typename allocator_traits<Allocator>::pointer;
  using const_pointer = typename allocator_traits<Allocator>::const_pointer;
  using reverse_iterator = std::reverse_iterator<iterator>;
  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// ...
};

vector and Growth

Consider the layout of a vector object pointer

Different ways to initialize a vector 👊

#include <bits/stdc++.h>
using namespace std;

int main()
{
    vector<int> vect{ 10, 20, 30 }; //initializer list

    for (int x : vect)
        cout << x << " ";

    return 0;
}
// CPP program to initialize a vector from
// another vector.
#include <bits/stdc++.h>
using namespace std;

int main()
{
    vector<int> vect1{ 10, 20, 30 };
    vector<int> vect2(vect1.begin(), vect1.end());// note range is [ )
    // whenever the range is mentioned, either in insert() or any others it
    //  is [ ). i.e. the second value of the range is not inclusive
    for (int x : vect2)
        cout << x << " ";

    return 0;
}

👊 You cannot do

vector<string> name(5); //error in these 2 lines
vector<int> val(5,0);

in a class outside of a method. 👊 It will throw out compilation error expected identifier before numeric constant.

You can initialize the data members at the point of declaration, but not with () brackets: :rose:

class Foo {
    vector<string> name = vector<string>(5);
    vector<int> val{vector<int>(5,0)};
};

👊 Below code also throws out error due to the same reason

class Solution {
private:
    vector<vector<bool>> visited((300, vector<bool>(300, false));
    int n;
    int m;
public:

Instead, if we do the following,it works. 🌹

class Solution {
private:
    vector<vector<bool>> visited{(300, vector<bool>(300, false))};
    int n;
    int m;
public:

How to initialize a 2D vector 👊 👊

vector<vector<int>> v; // creates an empty two dimensional vector of ints.
vector<vector<int>> v(3); // creates a 3 vector of empty vector
vector<vector<int>> matrix(ROW, vector<int>(COL, default_value));
 //if the default value is not given, it is filled with 0s
vector<vector<int>>matrix(ROW, vector<int>(COL))
#include<bits/stdc++.h>
using namespace std;

int main(){
    int n = 3; //no of rows
    int m = 4;  //no of cols

    // Create a vector containing n
    //vectors of size m.  
    vector<vector<int> > vec( n , vector<int> (m, 0));  

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++){
            cout<< vec[i][j]<< " ";
        }
        cout<< "\n";
    }

return 0;
}

const vector and const iterators

int main()
  {
  vector<int> vect{1,3,4,4};
  vect[2] = 30; //vect[2] is the l-value
  vector<int>::iterator iter1 = vect.begin();
  *iter1 = 20; // you can change the value using iter1 variable

  const vector<int> cvect{1,3,4,4};
  // cvect[2] = 30; //Illegal: cvect[2] is r-value and not the modifiable l-value
  // vector<int>::iterator iter = cvect.begin();  //Illegal: you can't assign read only
                                              // memory pointer to read/write pointer

  vector<int>::const_iterator iter; // you can have it uninitialized because variable itself
                                    // is not const, only memory pointed by it is constant

  iter = cvect.begin();
  // *iter = 20; //Illegal: iter is constant and you can't change the value

  // you can have const_iterator for non-const vector as well
  vector<int>::const_iterator citer = vect.begin();
  // now you can't change the value of vect using citer variable
  // *citer = 20; //Illegal: citer points to read only memory

  // vector<int>::iterator iter2 = vect.cbegin(); // Illegal: cbegin() returns read-only iterator
  vector<int>::const_iterator iter2 = vect.cbegin(); // Illegal: cbegin() returns read-only iterator


  // since cvect is already const, there is no difference if you did cbegin() or begin().
  // In both case, it will give you read-only memory location pointer

  // vector<int> iter3 = cvect.cbegin(); // Illegal even if cvect is not const

}

Do you need to have cbegin() when you already have const vector??

How to insert element in vector at specific position

Vector provides different overloaded version of member function insert() , to insert one or more elements in between existing elements.

Inserting a single element at specific position in vector

iterator insert (const_iterator pos, const value_type& val);
std::vector<int> vecOfNums { 1, 4, 5, 22, 33, 2, 11, 89, 49 };
// Create Iterator pointing to 4th Position
auto itPos = vecOfNums.begin() + 4;
// Insert element with value 9 at 4th Position in vector
auto newIt = vecOfNums.insert(itPos, 9);
//vector's content: 1 , 4 , 5 , 22 , 9 , 33 , 2 , 11 , 89 , 49

As in vector all elements are stored at continuous memory locations, so inserting an element in between will cause all the elements in right to shift or complete reallocation of all elements.

swaping two Vectors

void swap (vector& x); Swap content
Exchanges the content of the container by the content of x, which is another vector object of the same type. Sizes may differ. After the call to this member function, the elements in this container are those which were in x before the call, and the elements of x are those which were in this. All iterators, references and pointers remain valid for the swapped objects.

std::vector<int> foo (3,100);   // three ints with a value of 100
std::vector<int> bar (5,200);   // five ints with a value of 200

foo.swap(bar);

Inserting multiple elements or a range at specific position in vector

Some times we encounter a situation where we want to insert multiple elements in vector at specific position. These multiple elements can from another vector , array or any other container.

iterator insert (const_iterator pos, InputIterator first, InputIterator last);

It inserts the elements in range from [first, end) before iterator position pos and returns the iterator pointing to position first newly added element.

std::vector<std::string> vec1 { "at" , "hello", "hi", "there", "where", "now", "is", "that" };
std::vector<std::string> vec2 { "one" , "two", "two" };
// Insert all the elements in vec2 at 3rd position in vec1
auto iter = vec1.insert(vec1.begin() + 3, vec2.begin(), vec2.end());
//content: at , hello , hi , one , two , two , there , where , now , is , that ,

Inserting multiple elements using Initializer list

iterator insert (const_iterator position, initializer_list<value_type> list);

It copies all the elements in given initializer list before given iterator position pos and also returns the iterator of first of the newly added elements. Suppose we have vector of int i.e.

std::vector<int> vecOfInts { 1, 4, 5, 22, 33, 2, 11, 89, 49 };
// Insert all elements from initialization_list to vector at 3rd position
auto iter2 = vecOfInts.insert(vecOfInts.begin() + 3, {34,55,66,77});
//contents: 1 , 4 , 5 , 34 , 55 , 66 , 77 , 22 , 33 , 2 , 11 , 89 , 49

lower_bound() method

The lower_bound() method in C++ is used to return an iterator pointing to the first element in the range [first, last) which has a value not less than val. This means that the function returns the index of the next smallest number just greater than or equal to that number. If there are multiple values that are equal to val, lower_bound() returns the index of the first such value. The elements in the range shall already be sorted or at least partitioned with respect to val.

  std::vector<int> v{ 10, 20, 30, 30, 30, 40, 50 };
// std :: lower_bound
 low1 = std::lower_bound(v.begin(), v.end(), 30);
 low2 = std::lower_bound(v.begin(), v.end(), 35);
 low3 = std::lower_bound(v.begin(), v.end(), 55);
 // Printing the lower bounds
std::cout
    << "\nlower_bound for element 30 at position : "
    << (low1 - v.begin());
std::cout
    << "\nlower_bound for element 35 at position : "
    << (low2 - v.begin());
std::cout
    << "\nlower_bound for element 55 at position : "
    << (low3 - v.begin());

    // lower_bound for element 30 at position : 2
    // lower_bound for element 35 at position : 5
    // lower_bound for element 55 at position : 7

Time Complexity: The number of comparisons performed is logarithmic. 🌹 🌺

std::vector::swap

Exchanges the content of the container by the content of x, which is another vector object of the same type. Sizes may differ.

After the call to this member function, the elements in this container are those which were in x before the call, and the elements of x are those which were in this. All iterators, references and pointers remain valid for the swapped objects.

std::vector<int> foo (3,100);   // three ints with a value of 100
std::vector<int> bar (5,200);   // five ints with a value of 200

foo.swap(bar);

std::set::swap

int myints[]={12,75,10,32,20,25};
std::set<int> first (myints,myints+3);     // 10,12,75
std::set<int> second (myints+3,myints+6);  // 20,25,32

first.swap(second);

how can you insert elements in the front of a vector

vector<int> vect{1,2,3};
vector<int>v;

for(auto x: vect)
  v.insert(v.begin(), x); // x can come from map or from anyother source

Pair

pair <data_type1, data_type2> Pair_name;
int main()
{
    pair<int, int> pr1{1,3}; //initialization

    pair<int, int> pr2(make_pair(3,4));

    cout<<pr1.first<<"\t"<<pr1.second;
    cout <<endl;
    cout<<pr2.first<<"\t"<<pr2.second;
    cout <<endl;

}
/*
1       3
3       4
*/

what is lambda function or lambda expression

A lambda expression, sometimes also referred to as a lambda function or (strictly speaking incorrectly, but colloquially) as a lambda, is a simplified notation for defining and using an anonymous function object

Parts of lambda expression

syntax

auto someVar [](int a, int b)->int{
  //body of the function
};

[myVriable, someotherVar]; But this will just copy the values, like a paramter passed by value. If you want to pass a variable by reference, you supply a & besides its name.

[&myVriable, &someotherVar];

#include <bits/stdc++.h>
using namespace std;

int main()
{
    auto helloworld = [](int a, int b){
        return a + b;
        };
    cout<<helloworld(2, 6)<<endl;
    return 0;

}
int main()
{
    int i = 10, j = 11;
    auto helloworld = [](int a, int b){
        return a + b + i; //an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
        };
    cout<<helloworld(2, 6)<<endl;
    return 0;

}
int main()
{
    int i = 10, j = 11;
    auto helloworld = [i, j](int a, int b){
        return a + b + i + j; //an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
        };
    cout<<helloworld(2, 6)<<endl;
    cout<<i<<" "<<j<<endl;
    return 0;

    /*
    29
    10 11
    */
}
int main()
{
    int i = 10, j = 11;
    auto helloworld = [i, j](int a, int b){ //capture list are read only variable
        ++i; //illegal
        return a + b + i + j; //an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
        };
    cout<<helloworld(2, 6)<<endl;
    cout<<i<<" "<<j<<endl;
    return 0;

}
int main()
{
    int i = 10, j = 11;
    auto helloworld = [&i, &j](int a, int b){
        ++i;
        return a + b + i + j; //an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
        };
    cout<<helloworld(2, 6)<<endl;
    cout<<i<<" "<<j<<endl;
    return 0;

    /*
    30
    11 11
    */
}
int main()
{
    int i = 10, j = 11;
    auto helloworld = [=](int a, int b){ //capture all as ready only
        ++i; //illegal
        return a + b + i + j;
    cout<<helloworld(2, 6)<<endl;
    cout<<i<<" "<<j<<endl;
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int i = 10, j = 11;
    auto helloworld = [&](int a, int b){ //capture all by reference
        ++i; //illegal
        return a + b + i + j;
    };
    cout<<helloworld(2, 6)<<endl;
    cout<<i<<" "<<j<<endl;
    return 0;
    /*
    30
    11 11
    */
}

Priority_Queue

Map

Writing code to look up a name in a list of (name,number) pairs is quite tedious. In addition, a linear search is inefficient for all but the shortest lists. The standard library offers a search tree (a redblack tree) called map: 👊 👊

pointer

In other contexts, a map is known as an associative array or a dictionary 🌺. It is implemented as a balanced binary tree 🌹

The standard-library map is a container of pairs of values optimized for lookup. We can use the same initializer as for vector and list.

No two mapped values can have same key values.

map<string,int> phone_book {
  {"David Hume",123456},
  {"Karl Popper",234567},
  {"Ber trand Ar thur William Russell",345678}
};

When indexed by a value of its first type (called the key), a map returns the corresponding value of the second type (called the value or the mapped type).

int get_number(const string& s)
{
  return phone_book[s];
}

The cost of a map lookup is O(log(n)) where n is the number of elements in the map.. It is because the internal implementation of map is done using a balanced binary tree (Red-Black Tree) :punch: :punch:

Basic functions of map

map vs unordered_map

use std::map when :punch: :punch:

Use std::unordered_map when

find() and count() function

unordered_map vs unordered_set :

In unordered_set, we have only key, no value, these are mainly used to see presence/absence in a set. For example, consider the problem of counting frequencies of individual words. We can’t use unordered_set (or set) as we can’t store counts.

how to retrieve all key-values from map

* Using for-each statement
  ```c++
  std::map<int,int> mapints;
  std::vector<int> vints;
  for(auto const& imap: mapints)
      vints.push_back(imap.first);

insert vs emplace vs operator[] in c++ map ref

std::map<Key,T,Compare,Allocator>::emplace

Inserts a new element into the container constructed in-place with the given args if there is no element with the key in the container

C++ equivalent to Java Map getOrDefault?

set vs multiset

Advantages of BST over Hash Table

  1. Search
  2. Insert
  3. Delete