Hash Table, Map, Priority Queue
0. Overview
Besides the basic data structures mentioned in STL, there are some other interesting data structure worth being mentioned.
1. Hash Table vs. Hash Map
Hash table in C++ is “unordered_map<T, T>”, what makes it different from the the Hash map, which is “map<T, T>”, is the sort of the key. In Hash map, the key is sorted, while hash table is not.
So how do hash table arrange its order? It involves the hash function, which is provided by C++ system. Of course, we could define the hash function, but what’s more important is that how to write the hasher and predicate.
//unordered_map and unordered_multimap are used to implement Hash Table
//template <class T> class hash { } is provided by C++ system, where T can be any primitive data type, such as int, double, string, etc.
#include <iostream>
#include <map>
#include <unordered_map>
#include <string>
#include <algorithm>//for_each
using namespace std;
template <class T>
class ThreeD {
public:
T ht;
T wid;
T dep;
ThreeD() :ht(0), wid(0), dep(0) {}
ThreeD(T i, T j, T k) : ht(i), wid(j), dep(k) {}
T getVol() const { return ht * wid * dep; }
T getSurface() const { return 2 * (ht * wid + wid * dep + dep * ht); }
};
template <class T>
class my_compare {
public:
bool operator()(const ThreeD<T>& t1, const ThreeD<T>& t2) const {
return t1.getSurface() < t2.getSurface(); }
};
// predicate
template <class T>
class my_equal_to {
public:
bool operator()(const ThreeD<T>& t1, const ThreeD<T>& t2) const {
return t1.getSurface() == t2.getSurface(); }
};
// hasheer
template <class T>
class my_hash {
public:
// it must be size_t, because it reflect to the size of the table
size_t operator() (const ThreeD<T>& t1) const {
hash<int> h;
return h(t1.getSurface() * t1.getVol());
}
};
//predicate
class my_equal_to1 {
public:
bool operator()(const ThreeD<int>& t1, const ThreeD<int>& t2) const {
return t1.getSurface() == t2.getSurface(); }
};
//hasher
class my_hash1 {
public:
size_t operator() (const ThreeD<int>& t1) const {
hash<int> h;
return h(t1.getSurface() * t1.getVol());
}
};
template <class T>
ostream& operator<<(ostream& str, const ThreeD<T>& t) {
str << "(" << t.ht << ", " << t.wid << ", " << t.dep << ")";
return str;
}
int main() {
map<int, int> M1{ {5, 20}, {4, 100}, {3, 40}, {6, 100}, {5, 1000} };
for (auto i : M1) { cout << i.first << " " << i.second << " "; }
cout << endl;
unordered_map<int, int> M2{ {5, 20}, {4, 100}, {3, 40}, {6, 100}, {5, 1000} };
M2.insert({ 5,111 });
M2[5] = 111;
for (auto i : M2) { cout << i.first << " " << i.second << " "; }
cout << endl;
//hash funciton used is hash<int>
//eqaulity function used is equal_to<int>
ThreeD<int> t1(6, 4, 3), t2(9, 8, 4), t3(1, 2, 3);
map<ThreeD<int>, int, my_compare<int>> M3{ {t1, 11},{t2, 12},{t3, 13},{t1, 111} };
for (auto i : M3) { cout << i.first << " " << i.second << " "; }
cout << endl;
unordered_map<ThreeD<int>, int, my_hash<int>, my_equal_to<int>> M4{ {t1, 11},{t2, 12},{t3, 13},{t1, 111} };
for (auto i : M4) { cout << i.first << " " << i.second << " "; }
cout << endl;
unordered_multimap<ThreeD<int>, int, my_hash<int>, my_equal_to<int>> M5{ {t1, 11},{t2, 12},{t3, 13},{t1, 111} };
for (auto i : M5) { cout << i.first << " " << i.second << " "; }
cout << endl;
auto ret = M5.equal_range(t1);
for_each(ret.first, ret.second, [](auto i) {cout << i.first<<" "<<i.second << " "; });
unordered_map<ThreeD<int>, int, my_hash1, my_equal_to1> M6{ {t1, 11},{t2, 12},{t3, 13},{t1, 111} };
for (auto i : M6) { cout << i.first << " " << i.second << " "; }
cout << endl;
return 0;
}
2. Priority Queue
Based on the queue, priority queue is more like a max/min heap. The default comparison is less< int>, of course we could define it greater< int> according to our needs.
//priority queue and hash table
#include <iostream>
#include <queue> //allow you to use prioiry_queue
#include <map>
#include <set>
#include <unordered_map> //Hahs Table
#include <algorithm> //for_each
#include <string>
#include <iomanip>
#include <tuple>
using namespace std;
int k;//initialized to 0 for global int
template <class T>
class my_compare {
public:
bool operator()(T a, T b) { return a % 3 < b % 3; }
};
int main() {
priority_queue<int> q1; //max heap; default comparator is less<int>
for (auto i : { 1,2,3,4,5 }) { q1.push(i); }
while (!q1.empty()) {
cout << q1.top() << " ";
q1.pop();
}
cout << endl;
// must have vector<int>, it likes a temporialy container
priority_queue<int, vector<int>, greater<int >> q2; //min heap
for (auto i : { 1,2,3,4,5 }) { q2.push(i); }
while (!q2.empty()) {
cout << q2.top() << " ";
q2.pop();
}
cout << endl;
priority_queue<int, vector<int>, my_compare<int>> q3; //min heap
for (auto i : { 11,12,13,14,15 }) { q3.push(i); }
while (!q3.empty()) {
cout << q3.top() << " ";
q3.pop();
}
cout << endl;
multiset<int> s{ 6,2,8,4,7,2,9,2,1, 3,5,2,7,2 };
for (auto i : s) { cout << i << " "; }
cout << endl;
auto it = s.find(2);
it--;
cout << *it << endl;
auto ret = s.equal_range(2);
//it return a pair of iterators [it1, it2)
//for all elements that match
cout << endl;
for_each(ret.first, ret.second, [](auto i) {cout << i << " "; });
return 0;
}