460. LFU Cache#67
Conversation
| self.size = 0 | ||
| self.key_to_frequency = {} | ||
| self.key_to_value = {} | ||
| self.frequency_to_key_chunks = collections.OrderedDict() |
There was a problem hiding this comment.
chunk は塊を表す単語なのですが、あまり意味がないように感じました。 frequency_to_keys のほうが読み手にとって理解しやすいように感じました。
There was a problem hiding this comment.
keysだけでもいくつかのkeyという意図は伝わりそうなので、その方がよさそうです
| del key_chunks[key] | ||
|
|
||
| # frequency回使用されたキー集合の中身がなくなった | ||
| if len(key_chunks) == 0: |
There was a problem hiding this comment.
こちらのコメントをご参照ください。
mamo3gr/arai60#6 (comment)
| @@ -1 +1,112 @@ | |||
| # Step1 | |||
|
|
|||
There was a problem hiding this comment.
自分でも書いてみました。かなり面倒でした。
C++ には OrderedDict がないため、 Python に比べてひと手間掛かっています。
class LFUCache {
public:
LFUCache(int capacity) {
capacity_ = capacity;
size_ = 0;
}
int get(int key) {
if (!key_to_value_.contains(key)) {
return -1;
}
increment_frequency(key);
return key_to_value_[key];
}
void put(int key, int value) {
if (key_to_value_.contains(key)) {
increment_frequency(key);
key_to_value_[key] = value;
return;
}
++size_;
if (size_ > capacity_) {
--size_;
int frequency = frequencies_.front();
int old_key = frequency_to_keys_[frequency].front();
key_to_frequency_.erase(old_key);
key_to_value_.erase(old_key);
key_to_keys_iterator_.erase(old_key);
frequency_to_keys_[frequency].erase(frequency_to_keys_[frequency].begin());
if (frequency_to_keys_[frequency].empty()) {
frequency_to_frequencies_iterator_.erase(frequency);
frequencies_.erase(frequencies_.begin());
}
}
key_to_value_[key] = value;
if (frequencies_.empty() || frequencies_.front() != 1) {
auto it = frequencies_.insert(frequencies_.begin(), 1);
frequency_to_frequencies_iterator_[1] = it;
}
auto it = frequency_to_keys_[1].insert(frequency_to_keys_[1].end(), key);
key_to_keys_iterator_[key] = it;
key_to_frequency_[key] = 1;
}
void increment_frequency(int key) {
int old_frequency = key_to_frequency_[key];
int new_frequency = old_frequency + 1;
key_to_frequency_[key] = new_frequency;
auto it = frequency_to_frequencies_iterator_[old_frequency];
auto new_it = it;
++new_it;
if (new_it == frequencies_.end() ||
*new_it != new_frequency) {
new_it = frequencies_.insert(new_it, new_frequency);
}
frequency_to_frequencies_iterator_[new_frequency] = new_it;
frequency_to_keys_[old_frequency].erase(key_to_keys_iterator_[key]);
if (frequency_to_keys_[old_frequency].empty()) {
frequencies_.erase(frequency_to_frequencies_iterator_[old_frequency]);
frequency_to_frequencies_iterator_.erase(old_frequency);
frequency_to_keys_.erase(old_frequency);
}
frequency_to_keys_[new_frequency].push_back(key);
key_to_keys_iterator_[key] = --frequency_to_keys_[new_frequency].end();
}
int capacity_;
int size_;
list<int> frequencies_;
unordered_map<int, list<int>::iterator> frequency_to_frequencies_iterator_;
unordered_map<int, list<int>> frequency_to_keys_;
unordered_map<int, int> key_to_frequency_;
unordered_map<int, int> key_to_value_;
unordered_map<int, list<int>::iterator> key_to_keys_iterator_;
};There was a problem hiding this comment.
increment_frequencyという関数名はわかりやすくていいですね。
問題には
The functions get and put must each run in O(1) average time complexity.
とありますが、put関数内の以下の部分でO(N)かかることはないのでしょうか?
if (frequencies_.empty() || frequencies_.front() != 1) {
auto it = frequencies_.insert(frequencies_.begin(), 1);
frequency_to_frequencies_iterator_[1] = it;
}具体的には,
frequencies = [2, 3, 4, 5, 6, .... ]となっていて
2回使われたkeyは1個
3回使われたkeyは1個
4回使われたkeyは1個
...
などとなっている場合です
There was a problem hiding this comment.
frequencies_ の型が list<int> で、 list は C++ ではリンクトリストとして実装されることがほとんどです。そのため、 insert() は O(1) になります。また、 frequency_to_frequencies_iterator_ の型が unordered_map<int, list<int>::iterator> で、unordered_map は C++ ではハッシュテーブルとして実装されることがほとんどです。そのため、 operator [] は O(1) となります。合計で O(1) となります。
There was a problem hiding this comment.
C++ではlistはリンクトリストだったのですね。勉強不足でした。ありがとうございます。
There was a problem hiding this comment.
struct Node {
int key;
int value;
int freqency;
};
std::unordered_map<int, std::list<Node>> freq_to_nodes_;
std::unordered_map<int, std::list<Node>::iterator> key_to_iter_;でもいけますか。これだと、最小の頻度が追い出されてなくなった時に次の頻度を計算するのが難しそうなのですが、それは実は必ず1です。
There was a problem hiding this comment.
https://leetcode.com/problems/lfu-cache/description/