C++ Knowledge in Coding Interview
- Some useful C++ knowledge and examples that may be used/asked in a coding interview
- For more articles like this, please visit https://medium.com/@yangpeng_tech
Table of Contents
- Variables definitions, assignments, and initiations
- Type conversions
- Pointer
- Object-Oriented Programming
- STL
- Other common usages
- Explanation of keywords
Variables definitions, assignments and initializations
Integers
int a;
int a = 5;
int a(5);
int a{5};
Char
char c;
c = 'a';
c = 97; // same as c = 'a'; cout << c will print 'a' as well.
char c = 'a';
char c('a');
char c{'a'};
Float and double
float f;
f = 5.0f;
float f = 5.0f;
float f(5.0f);
float f{5.0f};
double d;
d = 5.0;
double d = 5.0;
double d(5.0);
double d{5.0};
char str[] = "string"; //\0 is added automatically
str = "rope"; // not ok!
char str[] = {'S', 'a', 'm', '\0'}; //\0 is required
char *str = "hi"; //error,
C++11 does not allow conversion from string literal to 'char *', instead, use below:
const char *str = "hi";
std::string
// init
string str;
str = "string";
string str = "string";
string str("string");
string str{"string"};
string s(4, 'Q'); //QQQQ
string s = 'a';
string s{'a'};
string s("my string");
string str(s);
string str(s, 3); //string
string str(s, 3, 4); //stri
const char *cs("my string");
string str(cs); //my string
string str(cs, 4); //my s (length)
Array
int a[5];
a[0] = 1;
int a[] = { 0, 1, 2, 3 };
int a[] { 0, 1, 2, 3 };
int a[5] = { 0, 1, 2, 3 }; //0,1,2,3,0
int a[5] { 0, 1, 2, 3 }; //0,1,2,3,0
Pointer
int i;
int *p;
p = &i;
p = q; //q is another pointer pointing to int;
int *p = &i;
const int *p = &i; //cannot change value of i via p
int const *p = &i; //same as above
int * const p = &i; // cannot change p
const int * const p = &i;
Dynamic array
- Using just raw pointers there’s no way to know if they point to an array or a single value. A pointer that is being used as an array and a pointer to a single value are identical — they’re both just a memory address.
char *str;
str = new char[5]{"abcd"};
int *p = new int{5};
int *q = new int[3]{1,2,3};
cout << p << endl; //0x7fa2bec02be0
cout << p+1 << endl; // 0x7fa2bec02be4
cout << q << endl; //0x7fd959c02bf0
cout << q+1 << endl; //0x7fd959c02bf4
delete p;
delete[] q;
Enum
enum Color
{
COLOR_BLACK,
COLOR_RED,
COLOR_BLUE,
COLOR_GREEN,
COLOR_WHITE, //the comma here is allowed in C++11
};
Color paint = COLOR_WHITE;
Color house(COLOR_BLUE);
Color apple { COLOR_RED };
Enum Class
enum class Color // "enum class" defines this as a scoped enumeration instead of a standard enumeration
{
RED, // RED is inside the scope of Color
BLUE
};
Color color = Color::RED; // note: RED is not directly accessible any more, we have to use Color::RED
Color color(Color::RED);
Color color { Color::RED };
Struct
struct Date
{
int year;
int month;
int day;
};
Date today;
today.year = 2020;
Date today = { 2020, 10, 14 }; //initialization list
Date today { 2020, 10, 14 }; //uniform initialization
struct S1 { int a, b; } s { 0, 1 };
Class
class DateClass
{
public:
int m_year;
int m_month;
int m_day;
DateClass();//no parameter
DateClass(int year, int month, int day);
};
DateClass today(2020,10,14); // constructor DateClass(int, int, int)
DateClass today = { 2020, 10, 14 }; //initialization list, DateClass(int, int, int)
DateClass today { 2020, 10, 14 }; //initialize using parameterized constructor (C++11), DateClass(int, int, int)
// following work only if there is constructor with no parameter
DateClass today; //DateClass()
today.year = 2020;
//DateClass today(); wrong, empty parentheses interpreted as a //function declaration
DateClass *p = new DateClass(); //DateClass()
DateClass *q = new DateClass; //DateClass()
Type conversions
String
- convert numbers, char to string
// number to string (basically to_string works for all kinds of numbers)
std::to_string(123); // "123" (int to string) std::to_string(3.1); // "3.100000" (double to string) std::to_string(.1f); // "0.100000" (float to string)
// another way to convert number and char to string is via stringstream (or simply ostringstream) class
#include <sstream>
std::stringstream ss;
ss << 3.14;
string str = ss.str(); // "3.14"
std::stringstream ss;
ss << 1 << '+' << 2.0 << "=" << 1 + 2;
string s = ss.str(); // "1+2=3"
// to convert char to string, we can use stringstream as above, or do as below
char ch = 'a';
string str{ch}; // "a"
string str(to_string(ch)); //"97" not "a"
string str = to_string(ch) + "test"; //"97test" not "atest"
// Other
string s;
s += 'a' + 1; //b, 'a' + 1 is of type int, then converted to string
- convert string to numbers, const char*, char []
// convert string to number using istringsream
std::istringstream iStream("123");
int i; //same works for other numerical types
iStream >> i;
// convert string to number use functions
string str = "3.14";
int i = std::stoi(str); // 3, need to pass str.c_str() in c
double d = std::stod(str); // 3.14, need to pass str.c_str() in c
// convert string to const char *
string s = "hello";
const char *ch = s.c_str();//null terminator appended
// convert string to char[]
string str = "cat";
char ca[4];
strcpy(ca, str.c_str()); //or use strncp
cout << ca << endl; //cat
Pointer
Basic usage
int var = 10;
int *ptr = &Var;
cout << var << endl; //10
cout << *ptr << endl; //10
cout << &var << endl; //0x7fffa0757dd4, address of Var
cout << ptr << endl; //0x7fffa0757dd4, value of pointer == address of Var it points to
cout << &ptr << endl; //0x7fff98b499e8, address of pointer
//function pointers
int (*fcnPtr)();
int (*const fcnPtr)(); //cannot change the function points to
int (*fcnPtr)(int); //point to a function with one argument
// pass reference of pointer
void myfunction (int *&p){p == <somevalue>};
3 ways to pass C++ arguments to a function:
- call-by-value
- call-by-reference with pointer argument (pass by address, address is copied)
- call-by-reference with reference argument
//call by value
//Address of n1(&n1) and n(&n) are different
//n1 won't change after function call
int square1(int n){...}
square1(n1);
//Pass-by-Reference with Pointer Arguments
//Address of n2(&n2) and n(n) are the same
//n2 can be changed via the function call
void square2(int *n){...}
square2(&n2);
//Pass-by-Reference with Reference Arguments
//Address of n3(&n3) and n(&n) are the same
//n3 can be changed via the function call
void square3(int &n){...}
square3(n3);
- C++ does not allow to pass an entire array as an argument to a function. We can pass a pointer to an array by specifying the array’s name without an index. The pointer to the array is copied to the function, thus the value of elements can be changed via the function, but not the the address of the array.
void func(int a[], int n){
cout << "value of a in func: " << a << endl;
}
void func1(int *a, int n){
cout << "value of a in func1: " << a<< endl;
}
int a[] = {1,2,3};
cout << "value of a: " << a << endl;
func(a, 3); //func(&a[0], 3) also works
func1(a, 3); //func1(&a[0], 3) also works
value of a: 0x7ffee117e78c
value of a in func: 0x7ffee117e78c
value of a in func1: 0x7ffee117e78c
Array name as pointers and pointer arithmetic
- An array name contains the address of the first element of the array which acts like a constant pointer. It means, the address stored in the array name can’t be changed.
int a[] = {1,2,3};
int *p = a; //a contains the address of a[0]
a++; //compile error: cannot increment value of type 'int [3]'
p++; //ok, points to a[1]
cout << a+1; //ok, address of a[1]
cout << p+1; //ok, address of a[1]
Pointer and two-dimensional numeric arrays
int nums[2][3] = { { 16, 18, 20 }, { 25, 26, 27 } };
- nums[ i ][ j ] is equivalent to *(*(nums+i)+j), e.g.
*(*nums) == 16
*(*(nums+1)+2) == 27
- Although
nums
,*nums
andnums[0]
have the same value, they have different types.nums
is a type ofint **
,*nums
and nums[0] is a type ofint *
.
value of nums: 0x7ffee1218770
value of *nums: 0x7ffee1218770
value of nums[0]: 0x7ffee1218770
value of nums + 1: 0x7ffee121877c //3 elements * 4 bytes = 12
value of *nums + 1: 0x7ffee1218774 //1 element * 4 bytes = 12
value of nums[0] + 1: 0x7ffee1218774 // 1 elements * 4 = 4
size of nums: 24 // 2 rows * 3 columns * 4 bytes
size of nums[0]: 12 // 3 elements in a row * 4 bytes
Pointers and String literals
- String literals are arrays containing null-terminated character sequences. String literals are arrays of type character plus terminating null-character, with each of the elements being of type const char (as characters of string can’t be modified).
const char * ptr = "geek";
cout << sizeof("geek") << endl; // 5
ptr[0] = 'a'; //compile error: read-only variable is not assignable
char *ptr1 = "geek";// compile error: conversion from string literal to 'char *' is deprecated
Void pointers
- Void pointers are pointers that point to a value that has no type (and thus also an undetermined length and undetermined dereferencing properties).
- Void pointers cannot be directly dereferenced. They have to be first transformed into some other pointer type that points to a concrete data type before being dereferenced.
int a = 0;
void *p = &a;
int *p1 = (int*)p;
cout << *p << endl; //compile error:
cout << *p1 << endl;//ok, 0
- In the example below, foo is a pointer to a function taking one argument, an integer, and that returns void.
void my_int_func(int x)
{
printf( "%d\n", x );
}
void (*foo)(int); // declare a function pointer
foo = &my_int_func; //Initializing Function Pointers, the ampersand is optional
//Use a function pointer
//call my_int_func (note that you do not need to write (*foo)(2))
foo( 2 ); // you can also use (*foo)( 2 );
- Example uses of function pointers:
- Callback Functions
- Functions as arguments to other functions (I was asked in one of my video interviews). See the example in another article (Section: Pragmatics of Sorting). The fourth argument of the quicksort function in the standard library (C: stdlib.h, C++: cstdlib) is a function pointer.
void qsort(void *base, size_t num, size_t size,
int (*compare)(const void*, const void*))
STL
STL example and explanation, STL has four components:
- Algorithms: sorting, searching, etc.
- Containers: vector, stack, set, map, unordered_map, etc
- Functions: functors
- Iterators: iterators
Algorithms
qsort (standard c library, stdlib.h)
- Standard C library provides qsort function that can be used for sorting an array.
- The prototype of qsort() function
// Sort an array of any type. The parameters are, base
// address of array, size of array and pointer to
// comparator function
void qsort(void *base, size_t num, size_t size,
int (*compare)(const void*, const void*))
- Comparator function: it requires two
const void*
parameters and it returnsint
.
int comparator(const void* p1, const void* p2);
Return value meaning
<0 The element pointed by p1 goes before the element pointed by p2
0 The element pointed by p1 is equivalent to the element pointed by p2
>0 The element pointed by p1 goes after the element pointed by p2
- Example of qsort
int compare_int(const void *a, const void *b) // ascending order
{
return *(int*)a - *(int*)b;
}
int compare_struct(const void *p, const void *q)// ascending order based on marks
{
int l = ((struct Student *)p)->marks;
int r = ((struct Student *)q)->marks;
return (l - r);
}
int
arr[] = {1, 6, 5, 2, 3, 9, 4, 7, 8};qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]),
compare_int);
Output: 1,2,3,4,5,6,7,8,9
- C++ STL provides a similar function sort that sorts a vector or array (items with random access).
- Prototypes of C++ sort() function
// To sort in default or ascending order.
template
void sort(T first, T last);
// To sort according to the order specified by comp.
template
void sort(T first, T last, Compare comp);
- The order of equal elements is not guaranteed to be preserved. C++ provides std::stable_sort (same usage as sort) that can be used to preserve order.
- Comparator cplusplus
- Binary function that accepts two elements in the range as arguments, and returns a value convertible to
bool
. - The value returned indicates whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines.
- The function shall not modify any of its arguments.
- This can either be a function pointer or a function object.
bool myfunction (int i, int j) { return (i<j); }
struct myclass {
bool operator() (int i,int j) { return (i<j);}
} myobject;
int compareSec(const pair<string, int>& a, const pair<string, int>& b) {
return a.second > b.second;
}
- Example (ascending order)
//Array int
arr[] = {1, 5, 8, 9, 6, 7, 3, 4, 2, 0}; int
n = sizeof(arr)/sizeof(arr[0]); sort(arr, arr+n);
//Vector
vector<int> data(arr, arr + n);
sort(data.begin(), data.end());
- Example (descending order)
//Array int
arr[] = {1, 5, 8, 9, 6, 7, 3, 4, 2, 0}; int
n = sizeof(arr)/sizeof(arr[0]); sort(arr, arr+n, greater<int>());
//Vector
vector<int> data(arr, arr + n);
sort(data.begin(), data.end(), greater<int>());
- Sort elements in
unordered_map
: It seems impossible to sort elements inunordered_map
as sort function only supports containers with random access available (i.e: ele[i]). Thus, to sort elements inunordered_map
, we need to make avector
coping data fromunordered_map
and sort elements of the vector. Example:
//descending order on a basis of second element
int compareSec(const pair<string, int>& a, const pair<string, int>& b) {
return a.second > b.second;
}
//c++11 unodered_map initialisation
unordered_map<string, int> mp({{"practice", 3}, {"makes", 2}, {"perfect", 1}, {"just", 4}});
vector<pair<string, int>> vec(mp.begin(), mp.end());
sort(vec.begin(), vec.end(), compareSec);
Output of vector:
just 4
practice 3
makes 2
perfect 1
- Sorting
vector
ofpairs
: G4G, G4G. By default, the sort function sorts the vector elements on the basis of the first element of pairs. - Sorting
vector
ofvector
. By default, it sorts by the first element of each vector in ascending order.
vector<vector<int>>v{{2,1}, {1,3},{4,2}};
sort(v.begin(), v.end());
for(auto i : v){
cout << i[0] << " " << i[1] << endl;
}
// {1,3}, {2,1}, {4,2}
// sort by second element of the vector in ascending order
int comp(const vector<int> &a, const vector<int> &b){
return a[1] < b[1];
}
Searching
- std::binary_search (return bool)
int a[] = { 1, 5, 8, 9, 6, 7, 3, 4, 2, 0 };
int asize = sizeof(a) / sizeof(a[0]);
sort(a, a + asize);
if (std::binary_search(a, a + 10, 2))
cout << "Element found in the array";
std::vector<int> haystack {1, 3, 4, 5, 9};
if (std::binary_search(haystack.begin(), haystack.end(), needle)) {
cout << "Found " << needle << '\n';
- std::bsearch (return void*)
// Binary predicate
int compare(const void* ap, const void* bp)
{
// Typecasting
const int* a = (int*)ap;
const int* b = (int*)bp;
if (*a < *b)
return -1;
else if (*a > *b)
return 1;
else
return 0;
}
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
int ARR_SIZE = sizeof(arr) / sizeof(arr[0]);
int key1 = 4;
int* p1 = (int*)std::bsearch(&key1, arr, ARR_SIZE, sizeof(arr[0]), compare);
if (p1)
cout << key1 << " found at position " << (p1 - arr);
- Non-Manipulating Algorithms
int arr[] = {10, 20, 5, 23 ,42 , 15};
int n = sizeof(arr)/sizeof(arr[0]);
vector<int> vect(arr, arr+n);
// Reversing the Vector
reverse(vect.begin(), vect.end());
cout << *max_element(vect.begin(), vect.end());//Maximum element
cout << *min_element(vect.begin(), vect.end());//Minimum element
cout << accumulate(vect.begin(), vect.end(), 0);//summation from 0
cout << count(vect.begin(), vect.end(), 20); //Counts the occurrences of 20
if(find(vect.begin(), vect.end(),5) != vect.end()){}
vector<int>::iterator it = lower_bound(vect.begin(), vect.end(), 20); //first occurrence of 20
vector<int>::iterator it = upper_bound(vect.begin(), vect.end(), 20); //last occurrence of 20
- Some Manipulating Algorithms (permutation)
vect.erase(vect.begin()+1); //Delete second element of vector
vect.erase(unique(vect.begin(),vect.end()),vect.end()); //Deletes the duplicate occurrences, vect must be sorted
next_permutation(vect.begin(), vect.end());//modifies vector to its next permutation order
prev_permutation(vect.begin(), vect.end());modifies vector to its previous permutation order
cout << distance(vect.begin(),max_element(vect.begin(),vect.end())); //Return distance of first to maximum element
swap(x,y); //x and y are int
swap(vect1,vect2);
- Useful Array algorithms
int ar[6] = {1, 2, 3, 4, 5, -6};
if(all_of(ar, ar+6, [](int x){ return x>0; }))//all elements are positive
if(any_of(ar, ar+6, [](int x){ return x<0; }))//if any ele are negative
if(none_of(ar, ar+6, [](int x){ return x<0; })) //no ele is negative
copy_n(ar, 6, ar1);//copy 6 elements from ar to ar1
iota(ar, ar+6, 20); //assign 6 values to ar starting from 20 (21,22..)
Containers
Sequence Containers: Implement data structures which can be accessed in a sequential manner.
- vector: dynamic array capable of growing as needed to contain its elements
// init
vector<int> v;
v.push_back(1);
vector<int> v(n, 10); //n elements with value 10
vector<int> v = { 10, 20, 30 };
vector<int> v{ 10, 20, 30 };
vector<int> v(arr, arr + n); //arr is int array
vector<int> v(v1.begin(), v2.end()); //init from another vector
// common usage
cout << v.empty();
cout << v.size();
cout << v.front();
cout << v.back();
v.push_back(1);
v.pop_back(); //delete last
v.assign(5, 10); // fill the array with 10 five times
v.insert(v.begin(), 5); //insert 5 at beginning
v.erase(v.begin()); //remove the first element
v1.swap(v2); //swap two vectors
// other useage
vector<vector<int>>vv;
vector<vector<int>>vv{{}}; //push an empty vector
vv.push_back(vector<int>()); //push an empty vector
vv.push_back(vector<int>{}); //push an empty vector
vv.push_back({}); //push an empty vector
vector<vector<int>>vv(4, {}); //error
vector<vector<int>>vv(4, vector<int>()); //ok
vector<vector<int>>vv(4, vector<int>{}); //ok
//return vector
vector<int> func(){
return {1,2};
return vector<int>{1,2};
return {};
}
// Important
if(0 < v.size() - 1)
cout << "entered" << endl;
if vec is empty, it will enter the body, as v.size()-1 be a very large number as size() returns unsigned int. To avoid unexpected results, we can use if(0 < (int)v.size() - 1) or 0+1 < v.size().
e.g. https://leetcode.com/problems/binary-search-tree-iterator/
- list: a doubly linked list
// init
list<int> lst;
lst.push_back(2);
lst.push_front(1); //now: 1,2
list<int> lst(10); // size 10
list<int> lst(10, 5); //10 elements with all 5
list<int> lst{4, 6, 5}; // value: 4,6,5
list<int> lst(arr, arr + n); //arr is an array
list<int> lst(lst1); //copy constructor, lst1 is another list
// common usage
cout << lst.front();
cout << lst.last();
cout << lst.size();
cout << lst.empty();
lst.push_back(1);
lst.push_front(2);
lst.pop_front(); //delete first
lst.pop_back(); //delete last
lst.erase(itr); //delete at a position
lst.clear(); //delete all elements, size 0, free memory
lst.swap(lst1); //swap elements of two lists
- deque: double-ended queue class
// declare
deque<int> deq;
// common usage
deq.push_back(1);
deq.push_front(2);
// init
array<int,2> a;
a[0] = 1;
a[1] = 2;
array<int,6> ar = {1, 2, 3, 4, 5, 6};
array<int,6> ar1{2, 3, 4, 5, 6, 7};
// common usage
cout << ar.front(); //1
cout << ar.back(); //6
cout << ar[1]; //2
cout << ar.size(); //6
cout << ar.empty(); //0
for ( auto it = ar.begin(); it != ar.end(); ++it )
std::cout << ' ' << *it; //1,2,3,4,5,6
ar.fill(0); // Filling array with 0, all elements are 0 now
ar.swap(ar1); // Swapping ar1 values with ar
- forward_list( Introduced in C++11): implements singly linked list.
// Declare
forward_list<int> fl;
forward_list<int> fl = {10, 20, 30, 40, 50};
forward_list<int> fl{10, 20, 30, 40, 50};
// common usage
fl.assign({1, 2, 3}); // 1,2,3
fl.assign(5, 10); //5 elements with value 10
fl.push_front(10); //insert at front
fl.pop_front(); //delete frist
fl.remove(40); //Removes all occurrences of 40
Container Adaptors: provide a different interface for sequential containers.
- queue: FIFO
// Declare
queue<int>q;
queue<int>{{1}};
//common usage
q.push(1);
q.pop();
int i = q.front();
int i = q.back();
cout << q.empty();
cout << q.size();
- priority_queue: a type of queue where the elements are kept sorted. it can be used to create min-heap or max-heap.
1. top() = O(1)
2. pop()= O(logn)
3. push()= O(logn)
#include <queue> //not #include <priority_queue>
// declare
priority_queue<int> pq; //descending order (max heap)
priority_queue<int, vector<int>, greater<int>> pq; //ascending order (min heap)
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>> > pq; //ascending order (min heap, sorted by first element in pair)
//common usage
pq.push(10);
pq.push(30);
pq.push(20);
pq.push(make_pair(1,2)); //pair
pq.push({1,2}); //pair
cout << pq.top();//30, as it is sorted
pq.pop();
- stack: LIFO
//Declare
stack<char> st;
stack<char> st{{'a'}};
// common usage
st.push('a');
st.pop(); //remove last element, return void. segmentation fault if empty
char c = st.top(); //return last element, segmentation fault if empty
if(st.empty()) //empty or not
cout << st.size(); //size
Associative Containers : implement sorted data structures using binary search tree that can be quickly searched (O(log n) complexity). C++’s map and set are actually ordered map and ordered set. They are not implemented using hash functions (instead red-black trees, a BST). Every query would take O(logn)
and not O(1)
, but the values will be always sorted.
- set: stores unique elements, with duplicate elements disallowed. The elements are sorted ( can be searched with log(n) ) according to their values. The value of the elements in a set cannot be modified once in the container (the elements are always const), but they can be inserted or removed from the container.
// init
set<int>s; //ascending order
set<int, greater<int>> s; //descending order
s.insert(1);
set<vector<int>> res; //use vector as elements, this is useful when we need a set of vector without duplicates, however, we cannot define unordered_set the same way.
// common usage
if (s.find(1) != s.end()){}
if (s.count(1)){}
cout << s.size();
cout << s.empty();
for (auto it = s.begin(); it != s.end(); it++){ //set<int>::iterator
cout << *it << endl;
}
- multiset: is a set where duplicate elements are allowed, it can be used to create min or max heap.
Same usage as std::set
- map: is a set where each element is a key/value pair. The key is used for sorting and indexing the data, and must be unique. The value is the actual data. The elements in map are sorted by key.
// init
map<int, int> mp;
mp.insert(pair<int, int>(1, 40));
mp.insert(make_pair(2,3));
mp.insert({ 3, 40 });
mp[4] = 30;
map<int, int> mp1(mp.begin(), mp.end());
// common usage
cout << mp.size();
cout << mp.empty();
mp.erase(mp.begin(), mp.find(3)); //remove all elements up to element with key=3
cout << mp.erase(4); //remove all elements with key=4, returns 1 if the key element is found in the map else returns 0.
mp.clear(); //remove all elements
for (auto itr = mp.begin(); itr != mp.end(); ++itr) {
cout << '\t' << itr->first << '\t' << itr->second << '\n';
}
- multimap: is a map that allows duplicate keys.
Same usage as std::map
Unordered Associative Containers : implement unordered data structures using hashing that can be quickly searched. While they are not sorted, most queries and operations are possible in O(1)
averagely.
- unordered_set (Introduced in C++11)
// init method 1
unordered_set<int> us;
us.insert(1);
// init method 2
unordered_set<int> us{1,2}; //cannot use us(1,2)
// init method 3
unordered_set<int> us{v.begin(), v.end()}; //or us(v.begin(), v.end())
// common usage
if ((us.find(1) != us.end()) // find element
if ((us.count(1)) // find element
if (us1 == us2) // check equality
for (auto it = us.begin(); it != us.end(); it++){
cout << *it << endl;
}
- unordered_multiset (Introduced in C++11)
- unordered_map (Introduced in C++11)
// init method 1
unordered_map<char, int> um;
um['a'] = 1;
um['b'] = 2;
um['c']++; //if there was no key 'c', the value will be 1.
// init method 2
unordered_map<char, int> um;
um.insert(std::make_pair('a',1));
// init method 3
unordered_map<char, int> um {{'a',1},{'b',1}};
// Common usage
cout << um['a'] << endl; // 1
cout << um.empty() << endl; // 0
if ((um.find('a') != um.end()) // find element
if ((um.count('a')) // find element
auto it = um.find('a'); //or unordered_map<char, int>::iterator
cout << it->first << " " << it->second << endl; // a, 1
for(auto it = um.begin(); it != um.end(); it++){
cout << it->first << " " << it->second << endl;
}
- unordered_multimap (Introduced in C++11)
Other Common Usage
The following are other commonly used tricks / tips in a coding interview.
Variable declarations, definitions, etc.
Char
// uppercase to lower case
char lower = upper + 'a' - 'A'; // or upper+32
// lowercase to upper
char upper = lower - ('a' - 'A'); // or lower-32
char c = 'A';
cout << (char)toLower(c); //a, if without char, 97 #include<cctype>
cout << isalnum(c); //1, #include<cctype>
// Toggle case
char a = 'a', b = 'B';
a ^= (1<<5);
b ^= (1<<5);
cout << a << " " << b; // A b
Integer
// negative integer
int n = -12;
cout << n / 10 << ", " << n % 10; // -1, -2
// reverse integer
int ret = 0;
while(n){
ret = ret*10 + n%10;
n /= 10;
}
String
// find index of char
size_t index = str.find('?');
if(index != string::npos){}
// link string with char
string s = "Abc";
string s1 = "test";
string s2 = s1 + s[0]; // s2 is testA, no explicit type cast required
string s3 = s[0]; // error
string s4 = static_cast<string>(s[0]); // error
string s4 = (string)s[0]; //error
string s5{s[0]}; // error
string s6(s[0]); // OK
std::ostringstream
// instead of using std::string to add strings for output, we can use ostringstream.
std::ostringstream oss;
oss << "test" << 123 << std::endl;
std::cout << oss.str();
Exception
try { if (n < 0) throw std::runtime_error("Bad things happened"); }
catch (const std::exception& e) { assert(std::string(e.what()) == std::string("Bad things happened")); }
priority_queue (3 ways of creating min-heap)
// 1. comparison using struct to create min-heap
struct compare
{
bool operator()(const pair<int,pair<int, int> >& a, const pair<int,pair<int, int> >& b)
{
return a.first>b.first;
}
};
priority_queue< pair<int,pair<int, int> >, vector<pair<int, pair<int, int> > >, compare > p;
// 2. comparison using function to create min-heap
auto comp = [](ListNode* a, ListNode* b) { return a->val > b->val; };
priority_queue<ListNode*, vector<ListNode*>, decltype(comp)> pq(comp);
// 3. using std::greater<T> to create min-heap
priority_queue<int, vector<int>, create<int>> pq;
Reverse linked list (three pointers required)
//iterative
ListNode *current, *prev, *next;
current = head; // or current = *pointerToHead
prev = nullptr;
while(current)
{
next = current->next; //we can define next as tmp here as well
current->next = prev;
prev = current;
current = next;
}
//current, next are nullptr now
head = prev; // or *pointerToHead = prev
//recursive
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head->next == nullptr) return head;
ListNode *node = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return node;
}
Delete a node in linked list (two pointers required)
void Delete(int n) //delete n-th node
{
Node* temp1 = head; // or ListNode* temp1 = *pointerToHead
if (n == 1){
head = temp1->next; // head now points to the second node, or *pointerToHead = temp1->next;
delete temp1;
return;
}
for (int i = 0; i < n - 2; i++){ // temp1 points to n-1 th node, delete temp1->next
temp1 = temp1->next;
}
ListNode* temp2 = temp1->next;
temp1->next = temp2->next;
delete temp2;
}
Explanation of keywords
static
- Static variables: variables in a function, variables in a class
- Static members of class: Class objects and functions in a class
- Make variable declared outside of a function to have internal linkage (can only be used in that file)
static int a = 0; //internal linkage
int myFun(){
static int b = 0; //static variable in a function, same life time of global variable
}
class Example{
static int c; //static memeber variable, not belong to any object
static void myFunc(); //static member method, can only call other static members
}
int Example::c = 1; //have to define outside
static Examlae example; //similar to static variable, have a scope till the lifetime of program
extern
- Used to make const variables declared outside of a function to have external linkage (can be used in other files). In other words, it is used to extend the visibility of variables/functions.
- It can also mean “this is a forward declaration for an external variable that is defined somewhere else”.
extern int var = 0; //define var, it can be used by other files, extern keyword can be omitted here.
extern int var; //forward declaration
using
- Import namespace:
using namespace std;
- Used for
type alias
is identical totypedef
. e.g.using T = int;
- Change an inherited member’s access specifier in the derived class.
class Base
{
protected:
void printValue() { std::cout << "base"; }
};
class Derived: public Base
{
public:
// Base::printValue was inherited as protected, so the public has no access
// But we're changing it to public via a using declaration
using Base::printValue; // note: no parenthesis here
};
override
- Use override specifier for each override function in the derived class to ensure you’ve actually overridden the function you think you have.
class A
{
public:
virtual const char* getName1(int x) { return "A"; }
virtual const char* getName2(int x) { return "A"; }
virtual const char* getName3(int x) { return "A"; }
};
class B : public A
{
public:
virtual const char* getName1(short int x) override { return "B"; } // compile error, function is not an override
virtual const char* getName2(int x) const override { return "B"; } // compile error, function is not an override
virtual const char* getName3(int x) override { return "B"; } // okay, function is an override of A::getName3(int)
};
final
- The final specifier can be used to make a virtual function no longer overridable. To prevent inheriting from a class, the final specifier is applied after the class name.
class B : public A
{
public:
// note use of final specifier on following line -- that makes this function no longer overridable
virtual const char* getName() override final { return "B"; } // okay, overrides A::getName()
};
class C : public B
{
public:
virtual const char* getName() override { return "C"; } // compile error: overrides B::getName(), which is final
};
// to prevent inheriting from class A
class A final
{
}
auto and decltype
- auto keyword specifies that the type of the variable that is being declared will be automatically deducted from its initializer.
- decltype lets you extract the type from the variable, so decltype is sort of an operator that evaluates the type of passed expression.
auto x = 4; //x is of type int
decltype(fun1()) x; // type of x is same as return type of fun1()
explicit: to prevent converting constructors.
MyString mine = 'x'; //implicitly convert 'x' to MyString if there is a proper constructor, say: MyString(int x).
explicit MyString(int x) {
m_string.resize(x);
}
MyString mine = 'x'; // compile error, since MyString(int) is now explicit and nothing will match this
delete
- Similar purpose as explicit to prevent converting constructors.
MyString(char) = delete; // any use of this constructor is an error
MyString mine('x'); // compile error, since MyString(char) is deleted
- Hide functionality that exists in the base class, so that it can not be accessed through the derived class.
class Derived : public Base
{
public:
int getValue() = delete; // mark this function as inaccessible, note that the Base version of getValue() is still accessible though
};
- Delete dynamically allocated memory (return to OS).
int* array = new int[10];
delete[] array;
int* ptr1 = new int;
delete ptr1;