adding lecture 21
This commit is contained in:
164
lectures/21_hash_tables_II/README.md
Normal file
164
lectures/21_hash_tables_II/README.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# Lecture 21 --- Hash Tables, part II
|
||||||
|
|
||||||
|
## Today’s Lecture
|
||||||
|
|
||||||
|
- Function Objects
|
||||||
|
- Continuing with Hash Tables
|
||||||
|
- STL Queue and STL Stack
|
||||||
|
|
||||||
|
## 21.1 Function Objects, a.k.a. Functors
|
||||||
|
|
||||||
|
- In addition to the basic mathematical operators + - * / < > , another operator we can overload for our C++
|
||||||
|
classes is the function call operator.
|
||||||
|
Why do we want to do this? This allows instances or objects of our class, to be used like functions. It’s weird
|
||||||
|
but powerful.
|
||||||
|
- Here’s the basic syntax. Any specific number of arguments can be used.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class my_class_name {
|
||||||
|
public:
|
||||||
|
// ... normal class stuff ...
|
||||||
|
my_return_type operator() ( /* my list of args */ );
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## 21.2 Why are Functors Useful?
|
||||||
|
|
||||||
|
- One example is the default 3rd argument for std::sort. We know that by default STL’s sort routines will use
|
||||||
|
the less than comparison function for the type stored inside the container. How exactly do they do that?
|
||||||
|
- First let’s define another tiny helper function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
bool float_less(float x, float y) {
|
||||||
|
return x < y;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Remember how we can sort the my_data vector defined above using our own homemade comparison function
|
||||||
|
for sorting:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::sort(my_data.begin(),my_data.end(),float_less);
|
||||||
|
```
|
||||||
|
|
||||||
|
If we don’t specify a 3rd argument:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::sort(my_data.begin(),my_data.end());
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what STL does by default:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::sort(my_data.begin(),my_data.end(),std::less<float>());
|
||||||
|
```
|
||||||
|
|
||||||
|
- What is std::less? It’s a templated class. Above we have called the default constructor to make an instance
|
||||||
|
of that class. Then, that instance/object can be used like it’s a function. Weird!
|
||||||
|
|
||||||
|
- How does it do that? std::less is a teeny tiny class that just contains the overloaded function call operator.
|
||||||
|
|
||||||
|
```
|
||||||
|
template <class T>
|
||||||
|
class less {
|
||||||
|
public:
|
||||||
|
bool operator() (const T& x, const T& y) const { return x < y; }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- You can use this instance/object/functor as a function that expects exactly two arguments of type T (in this
|
||||||
|
example float) that returns a bool. That’s exactly what we need for std::sort! This ultimately does the
|
||||||
|
same thing as our tiny helper homemade compare function!
|
||||||
|
|
||||||
|
## 21.3 Another more Complicated Functor Example
|
||||||
|
|
||||||
|
Constructors of function objects can be used to specify internal data for the functor that can then be used
|
||||||
|
during computation of the function call operator! For example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class between_values {
|
||||||
|
private:
|
||||||
|
float low, high;
|
||||||
|
public:
|
||||||
|
between_values(float l, float h) : low(l), high(h) {}
|
||||||
|
bool operator() (float val) { return low <= val && val <= high; }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- The range between low & high is specified when a functor/an instance of this class is created. We might
|
||||||
|
have multiple different instances of the between_values functor, each with their own range. Later, when the
|
||||||
|
functor is used, the query value will be passed in as an argument. The function call operator accepts that
|
||||||
|
single argument val and compares against the internal data low & high.
|
||||||
|
- This can be used in combination with STL’s find_if construct. For example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
between_values two_and_four(2,4);
|
||||||
|
if (std::find_if(my_data.begin(), my_data.end(), two_and_four) != my_data.end()) {
|
||||||
|
std::cout << "Found a value greater than 2 & less than 4!" << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Alternatively, we could create the functor without giving it a variable name. And in the use below we also
|
||||||
|
capture the return value to print out the first item in the vector inside this range. Note that it does not print
|
||||||
|
all values in the range.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::vector<float>::iterator itr;
|
||||||
|
itr = std::find_if(my_data.begin(), my_data.end(), between_values(2,4));
|
||||||
|
if (itr != my_data.end()) {
|
||||||
|
std::cout << "my_data contains " << *itr
|
||||||
|
<< ", a value greater than 2 & less than 4!" << std::endl;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 21.5 Using STL’s Associative Hash Table (Map)
|
||||||
|
|
||||||
|
- Using the default std::string hash function.
|
||||||
|
– With no specified initial table size.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo> m;
|
||||||
|
```
|
||||||
|
– Optionally specifying initial (minimum) table size.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo> m(1000);
|
||||||
|
```
|
||||||
|
- Using a home-made std::string hash function. Note: We are required to specify the initial table size.
|
||||||
|
– Manually specifying the hash function type.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo,std::function<unsigned int(std::string)> > m(1000, MyHashFunction);
|
||||||
|
```
|
||||||
|
– Using the decltype specifier to get the “declared type of an entity”.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo,decltype(&MyHashFunction)> m(1000, MyHashFunction);
|
||||||
|
```
|
||||||
|
- Using a home-made std::string hash functor or function object.
|
||||||
|
– With no specified initial table size.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo,MyHashFunctor> m;
|
||||||
|
```
|
||||||
|
– Optionally specifying initial (minimum) table size.
|
||||||
|
```cpp
|
||||||
|
std::unordered_map<std::string,Foo,MyHashFunctor> m(1000);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 21.6 Additional STL Container Classes: Stacks
|
||||||
|
|
||||||
|
<!--We’ve studied STL vectors, lists, maps, and sets. These data structures provide a wide range of flexibility in
|
||||||
|
terms of operations. One way to obtain computational efficiency is to consider a simplified set of operations or
|
||||||
|
functionality.-->
|
||||||
|
<!-- For example, with a hash table we give up the notion of a sorted table and gain in find, insert, & erase efficiency.-->
|
||||||
|
– Stacks allow access, insertion and deletion from only one end called the top
|
||||||
|
- There is no access to values in the middle of a stack.
|
||||||
|
- Stacks may be implemented efficiently in terms of vectors and lists, although vectors are preferable.
|
||||||
|
- All stack operations are O(1)
|
||||||
|
|
||||||
|
## 21.7 Additional STL Container Classes: Queues
|
||||||
|
|
||||||
|
- Queues allow insertion at one end, called the back and removal from the other end, called the front
|
||||||
|
- There is no access to values in the middle of a queue.
|
||||||
|
- Queues may be implemented efficiently in terms of a list. Using vectors for queues is also possible,
|
||||||
|
but requires more work to get right.
|
||||||
|
- All queue operations are O(1)
|
||||||
|
|
||||||
|
## 21.8 Leetcode Exercises
|
||||||
|
|
||||||
|
- [Leetcode problem 1451: Rearrange Words in a Sentence](https://leetcode.com/problems/rearrange-words-in-a-sentence/). Solution: [p1451_rearrange_words_in_a_sentence.cpp](../../leetcode/p1451_rearrange_words_in_a_sentence.cpp).
|
||||||
46
leetcode/p1451_rearrange_words_in_a_sentence.cpp
Normal file
46
leetcode/p1451_rearrange_words_in_a_sentence.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// solution to leetcode 1451
|
||||||
|
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
static bool cmp(std::pair<string,int> p1, std::pair<string,int> p2){
|
||||||
|
//bool cmp(string p1, string p2){
|
||||||
|
int len1 = p1.first.length();
|
||||||
|
int len2 = p2.first.length();
|
||||||
|
if(len1==len2){
|
||||||
|
// keep the original order
|
||||||
|
return (p1.second<p2.second);
|
||||||
|
}else{
|
||||||
|
return (len1<len2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string arrangeWords(string text) {
|
||||||
|
string ans;
|
||||||
|
// store all words and its index.
|
||||||
|
vector<std::pair<string, int>> v1;
|
||||||
|
string word;
|
||||||
|
int index;
|
||||||
|
char c;
|
||||||
|
c=tolower(text[0]);
|
||||||
|
text[0]=c;
|
||||||
|
// store the words and its index into v1.
|
||||||
|
stringstream ss(text);
|
||||||
|
while(ss >> word){ // if there are n words, O(n)
|
||||||
|
v1.push_back({word, index});
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
std:sort(v1.begin(), v1.end(), cmp);
|
||||||
|
int size;
|
||||||
|
size = v1.size();
|
||||||
|
for(int i=0;i<size-1;i++){ // if there are n words, O(n)
|
||||||
|
ans += v1[i].first; // step 1. call operator+()
|
||||||
|
// step 2. call operator=(),
|
||||||
|
// step 3. call operator+=();
|
||||||
|
ans += ' ';
|
||||||
|
}
|
||||||
|
ans = ans + v1[size-1].first;
|
||||||
|
c=toupper(ans[0]);
|
||||||
|
ans[0]=c;
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user