From 6024b81fd46e6c6eb0b11f7dd6eac9dfdab55cde Mon Sep 17 00:00:00 2001 From: Yang Libin Date: Sun, 8 Oct 2023 01:10:33 +0000 Subject: [PATCH 1/2] feat: add solutions to lc problem: No.2034 No.2034.Stock Price Fluctuation --- .../2034.Stock Price Fluctuation/README.md | 153 ++++++++++-------- .../2034.Stock Price Fluctuation/README_EN.md | 153 ++++++++++-------- .../2034.Stock Price Fluctuation/Solution.cpp | 36 ++--- .../2034.Stock Price Fluctuation/Solution.go | 51 +++--- .../Solution.java | 28 ++-- .../2034.Stock Price Fluctuation/Solution.py | 29 ++-- 6 files changed, 242 insertions(+), 208 deletions(-) diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/README.md b/solution/2000-2099/2034.Stock Price Fluctuation/README.md index 075f7099e0864..20c47a93315fb 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/README.md +++ b/solution/2000-2099/2034.Stock Price Fluctuation/README.md @@ -66,6 +66,23 @@ stockPrice.minimum(); // 返回 2 ,最低价格时间戳为 4 ,价格为 +**方法一:哈希表 + 有序集合** + +我们定义以下几个数据结构或变量,其中: + +- `d`:表示一个哈希表,用于存储时间戳和对应的价格; +- `ls`:表示一个有序集合,用于存储所有的价格; +- `last`:表示最后一次更新的时间戳。 + +那么,我们可以得到以下几个操作: + +- `update(timestamp, price)`:更新时间戳 `timestamp` 对应的价格为 `price`。如果 `timestamp` 已经存在,那么我们需要先将其对应的价格从有序集合中删除,再将其更新为 `price`。否则,我们直接将其更新为 `price`。然后,我们需要更新 `last` 为 `max(last, timestamp)`。时间复杂度为 $O(\log n)$。 +- `current()`:返回 `last` 对应的价格。时间复杂度为 $O(1)$。 +- `maximum()`:返回有序集合中的最大值。时间复杂度为 $O(\log n)$。 +- `minimum()`:返回有序集合中的最小值。时间复杂度为 $O(\log n)$。 + +空间复杂度为 $O(n)$。其中,$n$ 为 `update` 操作的次数。 + ### **Python3** @@ -73,35 +90,30 @@ stockPrice.minimum(); // 返回 2 ,最低价格时间戳为 4 ,价格为 ```python -from sortedcontainers import SortedDict +from sortedcontainers import SortedList class StockPrice: def __init__(self): - self.last_ts = 0 - self.mp = {} - self.counter = SortedDict() + self.d = {} + self.ls = SortedList() + self.last = 0 def update(self, timestamp: int, price: int) -> None: - if timestamp in self.mp: - old_price = self.mp[timestamp] - self.counter[old_price] -= 1 - if self.counter[old_price] == 0: - del self.counter[old_price] - if price not in self.counter: - self.counter[price] = 0 - self.counter[price] += 1 - self.mp[timestamp] = price - self.last_ts = max(self.last_ts, timestamp) + if timestamp in self.d: + self.ls.remove(self.d[timestamp]) + self.d[timestamp] = price + self.ls.add(price) + self.last = max(self.last, timestamp) def current(self) -> int: - return self.mp[self.last_ts] + return self.d[self.last] def maximum(self) -> int: - return self.counter.keys()[-1] + return self.ls[-1] def minimum(self) -> int: - return self.counter.keys()[0] + return self.ls[0] # Your StockPrice object will be instantiated and called as such: @@ -118,36 +130,36 @@ class StockPrice: ```java class StockPrice { - private int lastTs; - private Map mp = new HashMap<>(); - private TreeMap counter = new TreeMap<>(); + private Map d = new HashMap<>(); + private TreeMap ls = new TreeMap<>(); + private int last; public StockPrice() { + } public void update(int timestamp, int price) { - if (mp.containsKey(timestamp)) { - int oldPrice = mp.get(timestamp); - counter.put(oldPrice, counter.get(oldPrice) - 1); - if (counter.get(oldPrice) == 0) { - counter.remove(oldPrice); + if (d.containsKey(timestamp)) { + int old = d.get(timestamp); + if (ls.merge(old, -1, Integer::sum) == 0) { + ls.remove(old); } } - mp.put(timestamp, price); - counter.put(price, counter.getOrDefault(price, 0) + 1); - lastTs = Math.max(lastTs, timestamp); + d.put(timestamp, price); + ls.merge(price, 1, Integer::sum); + last = Math.max(last, timestamp); } public int current() { - return mp.get(lastTs); + return d.get(last); } public int maximum() { - return counter.lastKey(); + return ls.lastKey(); } public int minimum() { - return counter.firstKey(); + return ls.firstKey(); } } @@ -166,35 +178,35 @@ class StockPrice { ```cpp class StockPrice { public: - int lastTs; - unordered_map mp; - map counter; - StockPrice() { + } void update(int timestamp, int price) { - if (mp.count(timestamp)) { - int oldPrice = mp[timestamp]; - --counter[oldPrice]; - if (counter[oldPrice] == 0) counter.erase(oldPrice); + if (d.count(timestamp)) { + ls.erase(ls.find(d[timestamp])); } - mp[timestamp] = price; - ++counter[price]; - lastTs = max(lastTs, timestamp); + d[timestamp] = price; + ls.insert(price); + last = max(last, timestamp); } int current() { - return mp[lastTs]; + return d[last]; } int maximum() { - return counter.rbegin()->first; + return *ls.rbegin(); } int minimum() { - return counter.begin()->first; + return *ls.begin(); } + +private: + unordered_map d; + multiset ls; + int last = 0; }; /** @@ -211,52 +223,57 @@ public: ```go type StockPrice struct { - lastTs int - mp map[int]int - counter *redblacktree.Tree + d map[int]int + ls *redblacktree.Tree + last int } func Constructor() StockPrice { return StockPrice{ - mp: make(map[int]int), - counter: redblacktree.NewWithIntComparator(), + d: make(map[int]int), + ls: redblacktree.NewWithIntComparator(), + last: 0, } } func (this *StockPrice) Update(timestamp int, price int) { - if timestamp > this.lastTs { - this.lastTs = timestamp - } - if old, ok := this.mp[timestamp]; ok { - cnt := getInt(this.counter, old) - if cnt == 1 { - this.counter.Remove(old) + merge := func(rbt *redblacktree.Tree, key, value int) { + if v, ok := rbt.Get(key); ok { + nxt := v.(int) + value + if nxt == 0 { + rbt.Remove(key) + } else { + rbt.Put(key, nxt) + } } else { - this.counter.Put(old, cnt-1) + rbt.Put(key, value) } } - this.mp[timestamp] = price - this.counter.Put(price, getInt(this.counter, price)+1) + if v, ok := this.d[timestamp]; ok { + merge(this.ls, v, -1) + } + this.d[timestamp] = price + merge(this.ls, price, 1) + this.last = max(this.last, timestamp) } func (this *StockPrice) Current() int { - return this.mp[this.lastTs] + return this.d[this.last] } func (this *StockPrice) Maximum() int { - return this.counter.Right().Key.(int) + return this.ls.Right().Key.(int) } func (this *StockPrice) Minimum() int { - return this.counter.Left().Key.(int) + return this.ls.Left().Key.(int) } -func getInt(rbt *redblacktree.Tree, key int) int { - val, found := rbt.Get(key) - if !found { - return 0 +func max(a, b int) int { + if a > b { + return a } - return val.(int) + return b } /** diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md b/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md index fbee1abc80858..9d3f56c98b996 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md +++ b/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md @@ -61,40 +61,52 @@ stockPrice.minimum(); // return 2, the minimum price is 2 at timestamp 4. ## Solutions +**Solution 1: Hash Table + Ordered Set** + +We define the following data structures or variables: + +- `d`: a hash table that stores the timestamp and the corresponding price; +- `ls`: an ordered set that stores all prices; +- `last`: the timestamp of the last update. + +Then, we can perform the following operations: + +- `update(timestamp, price)`: update the price corresponding to the timestamp `timestamp` to `price`. If `timestamp` already exists, we need to first remove its corresponding price from the ordered set, and then update it to `price`. Otherwise, we directly update it to `price`. Then, we need to update `last` to `max(last, timestamp)`. The time complexity is O(log n). +- `current()`: return the price corresponding to `last`. The time complexity is $O(1)$. +- `maximum()`: return the maximum value in the ordered set. The time complexity is $O(\log n)$. +- `minimum()`: return the minimum value in the ordered set. The time complexity is $O(\log n)$. + +The space complexity is $O(n)$, where $n$ is the number of `update` operations. + ### **Python3** ```python -from sortedcontainers import SortedDict +from sortedcontainers import SortedList class StockPrice: def __init__(self): - self.last_ts = 0 - self.mp = {} - self.counter = SortedDict() + self.d = {} + self.ls = SortedList() + self.last = 0 def update(self, timestamp: int, price: int) -> None: - if timestamp in self.mp: - old_price = self.mp[timestamp] - self.counter[old_price] -= 1 - if self.counter[old_price] == 0: - del self.counter[old_price] - if price not in self.counter: - self.counter[price] = 0 - self.counter[price] += 1 - self.mp[timestamp] = price - self.last_ts = max(self.last_ts, timestamp) + if timestamp in self.d: + self.ls.remove(self.d[timestamp]) + self.d[timestamp] = price + self.ls.add(price) + self.last = max(self.last, timestamp) def current(self) -> int: - return self.mp[self.last_ts] + return self.d[self.last] def maximum(self) -> int: - return self.counter.keys()[-1] + return self.ls[-1] def minimum(self) -> int: - return self.counter.keys()[0] + return self.ls[0] # Your StockPrice object will be instantiated and called as such: @@ -109,36 +121,36 @@ class StockPrice: ```java class StockPrice { - private int lastTs; - private Map mp = new HashMap<>(); - private TreeMap counter = new TreeMap<>(); + private Map d = new HashMap<>(); + private TreeMap ls = new TreeMap<>(); + private int last; public StockPrice() { + } public void update(int timestamp, int price) { - if (mp.containsKey(timestamp)) { - int oldPrice = mp.get(timestamp); - counter.put(oldPrice, counter.get(oldPrice) - 1); - if (counter.get(oldPrice) == 0) { - counter.remove(oldPrice); + if (d.containsKey(timestamp)) { + int old = d.get(timestamp); + if (ls.merge(old, -1, Integer::sum) == 0) { + ls.remove(old); } } - mp.put(timestamp, price); - counter.put(price, counter.getOrDefault(price, 0) + 1); - lastTs = Math.max(lastTs, timestamp); + d.put(timestamp, price); + ls.merge(price, 1, Integer::sum); + last = Math.max(last, timestamp); } public int current() { - return mp.get(lastTs); + return d.get(last); } public int maximum() { - return counter.lastKey(); + return ls.lastKey(); } public int minimum() { - return counter.firstKey(); + return ls.firstKey(); } } @@ -157,35 +169,35 @@ class StockPrice { ```cpp class StockPrice { public: - int lastTs; - unordered_map mp; - map counter; - StockPrice() { + } void update(int timestamp, int price) { - if (mp.count(timestamp)) { - int oldPrice = mp[timestamp]; - --counter[oldPrice]; - if (counter[oldPrice] == 0) counter.erase(oldPrice); + if (d.count(timestamp)) { + ls.erase(ls.find(d[timestamp])); } - mp[timestamp] = price; - ++counter[price]; - lastTs = max(lastTs, timestamp); + d[timestamp] = price; + ls.insert(price); + last = max(last, timestamp); } int current() { - return mp[lastTs]; + return d[last]; } int maximum() { - return counter.rbegin()->first; + return *ls.rbegin(); } int minimum() { - return counter.begin()->first; + return *ls.begin(); } + +private: + unordered_map d; + multiset ls; + int last = 0; }; /** @@ -202,52 +214,57 @@ public: ```go type StockPrice struct { - lastTs int - mp map[int]int - counter *redblacktree.Tree + d map[int]int + ls *redblacktree.Tree + last int } func Constructor() StockPrice { return StockPrice{ - mp: make(map[int]int), - counter: redblacktree.NewWithIntComparator(), + d: make(map[int]int), + ls: redblacktree.NewWithIntComparator(), + last: 0, } } func (this *StockPrice) Update(timestamp int, price int) { - if timestamp > this.lastTs { - this.lastTs = timestamp - } - if old, ok := this.mp[timestamp]; ok { - cnt := getInt(this.counter, old) - if cnt == 1 { - this.counter.Remove(old) + merge := func(rbt *redblacktree.Tree, key, value int) { + if v, ok := rbt.Get(key); ok { + nxt := v.(int) + value + if nxt == 0 { + rbt.Remove(key) + } else { + rbt.Put(key, nxt) + } } else { - this.counter.Put(old, cnt-1) + rbt.Put(key, value) } } - this.mp[timestamp] = price - this.counter.Put(price, getInt(this.counter, price)+1) + if v, ok := this.d[timestamp]; ok { + merge(this.ls, v, -1) + } + this.d[timestamp] = price + merge(this.ls, price, 1) + this.last = max(this.last, timestamp) } func (this *StockPrice) Current() int { - return this.mp[this.lastTs] + return this.d[this.last] } func (this *StockPrice) Maximum() int { - return this.counter.Right().Key.(int) + return this.ls.Right().Key.(int) } func (this *StockPrice) Minimum() int { - return this.counter.Left().Key.(int) + return this.ls.Left().Key.(int) } -func getInt(rbt *redblacktree.Tree, key int) int { - val, found := rbt.Get(key) - if !found { - return 0 +func max(a, b int) int { + if a > b { + return a } - return val.(int) + return b } /** diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp index be492bc4ddb33..429b7565d2ad5 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp @@ -1,34 +1,34 @@ class StockPrice { public: - int lastTs; - unordered_map mp; - map counter; - StockPrice() { - } + } + void update(int timestamp, int price) { - if (mp.count(timestamp)) { - int oldPrice = mp[timestamp]; - --counter[oldPrice]; - if (counter[oldPrice] == 0) counter.erase(oldPrice); + if (d.count(timestamp)) { + ls.erase(ls.find(d[timestamp])); } - mp[timestamp] = price; - ++counter[price]; - lastTs = max(lastTs, timestamp); + d[timestamp] = price; + ls.insert(price); + last = max(last, timestamp); } - + int current() { - return mp[lastTs]; + return d[last]; } - + int maximum() { - return counter.rbegin()->first; + return *ls.rbegin(); } - + int minimum() { - return counter.begin()->first; + return *ls.begin(); } + +private: + unordered_map d; + multiset ls; + int last = 0; }; /** diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.go b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.go index 1aa353a11e1ce..92bfecf7baaca 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.go +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.go @@ -1,50 +1,55 @@ type StockPrice struct { - lastTs int - mp map[int]int - counter *redblacktree.Tree + d map[int]int + ls *redblacktree.Tree + last int } func Constructor() StockPrice { return StockPrice{ - mp: make(map[int]int), - counter: redblacktree.NewWithIntComparator(), + d: make(map[int]int), + ls: redblacktree.NewWithIntComparator(), + last: 0, } } func (this *StockPrice) Update(timestamp int, price int) { - if timestamp > this.lastTs { - this.lastTs = timestamp - } - if old, ok := this.mp[timestamp]; ok { - cnt := getInt(this.counter, old) - if cnt == 1 { - this.counter.Remove(old) + merge := func(rbt *redblacktree.Tree, key, value int) { + if v, ok := rbt.Get(key); ok { + nxt := v.(int) + value + if nxt == 0 { + rbt.Remove(key) + } else { + rbt.Put(key, nxt) + } } else { - this.counter.Put(old, cnt-1) + rbt.Put(key, value) } } - this.mp[timestamp] = price - this.counter.Put(price, getInt(this.counter, price)+1) + if v, ok := this.d[timestamp]; ok { + merge(this.ls, v, -1) + } + this.d[timestamp] = price + merge(this.ls, price, 1) + this.last = max(this.last, timestamp) } func (this *StockPrice) Current() int { - return this.mp[this.lastTs] + return this.d[this.last] } func (this *StockPrice) Maximum() int { - return this.counter.Right().Key.(int) + return this.ls.Right().Key.(int) } func (this *StockPrice) Minimum() int { - return this.counter.Left().Key.(int) + return this.ls.Left().Key.(int) } -func getInt(rbt *redblacktree.Tree, key int) int { - val, found := rbt.Get(key) - if !found { - return 0 +func max(a, b int) int { + if a > b { + return a } - return val.(int) + return b } /** diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java index 95fc77a406368..1e98204161f5e 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java @@ -1,34 +1,34 @@ class StockPrice { - private int lastTs; - private Map mp = new HashMap<>(); - private TreeMap counter = new TreeMap<>(); + private Map d = new HashMap<>(); + private TreeMap ls = new TreeMap<>(); + private int last; public StockPrice() { + } public void update(int timestamp, int price) { - if (mp.containsKey(timestamp)) { - int oldPrice = mp.get(timestamp); - counter.put(oldPrice, counter.get(oldPrice) - 1); - if (counter.get(oldPrice) == 0) { - counter.remove(oldPrice); + if (d.containsKey(timestamp)) { + int old = d.get(timestamp); + if (ls.merge(old, -1, Integer::sum) == 0) { + ls.remove(old); } } - mp.put(timestamp, price); - counter.put(price, counter.getOrDefault(price, 0) + 1); - lastTs = Math.max(lastTs, timestamp); + d.put(timestamp, price); + ls.merge(price, 1, Integer::sum); + last = Math.max(last, timestamp); } public int current() { - return mp.get(lastTs); + return d.get(last); } public int maximum() { - return counter.lastKey(); + return ls.lastKey(); } public int minimum() { - return counter.firstKey(); + return ls.firstKey(); } } diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.py b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.py index eaad7fe93ac4b..f077be733a49e 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.py +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.py @@ -1,32 +1,27 @@ -from sortedcontainers import SortedDict +from sortedcontainers import SortedList class StockPrice: def __init__(self): - self.last_ts = 0 - self.mp = {} - self.counter = SortedDict() + self.d = {} + self.ls = SortedList() + self.last = 0 def update(self, timestamp: int, price: int) -> None: - if timestamp in self.mp: - old_price = self.mp[timestamp] - self.counter[old_price] -= 1 - if self.counter[old_price] == 0: - del self.counter[old_price] - if price not in self.counter: - self.counter[price] = 0 - self.counter[price] += 1 - self.mp[timestamp] = price - self.last_ts = max(self.last_ts, timestamp) + if timestamp in self.d: + self.ls.remove(self.d[timestamp]) + self.d[timestamp] = price + self.ls.add(price) + self.last = max(self.last, timestamp) def current(self) -> int: - return self.mp[self.last_ts] + return self.d[self.last] def maximum(self) -> int: - return self.counter.keys()[-1] + return self.ls[-1] def minimum(self) -> int: - return self.counter.keys()[0] + return self.ls[0] # Your StockPrice object will be instantiated and called as such: From 662f220f72e12ad7be08dae14325287d19e01b22 Mon Sep 17 00:00:00 2001 From: Yang Libin Date: Sun, 8 Oct 2023 01:15:13 +0000 Subject: [PATCH 2/2] fix: code style --- .../2000-2099/2034.Stock Price Fluctuation/README.md | 2 -- .../2000-2099/2034.Stock Price Fluctuation/README_EN.md | 2 -- .../2000-2099/2034.Stock Price Fluctuation/Solution.cpp | 9 ++++----- .../2000-2099/2034.Stock Price Fluctuation/Solution.java | 1 - 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/README.md b/solution/2000-2099/2034.Stock Price Fluctuation/README.md index 20c47a93315fb..355120f03858b 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/README.md +++ b/solution/2000-2099/2034.Stock Price Fluctuation/README.md @@ -135,7 +135,6 @@ class StockPrice { private int last; public StockPrice() { - } public void update(int timestamp, int price) { @@ -179,7 +178,6 @@ class StockPrice { class StockPrice { public: StockPrice() { - } void update(int timestamp, int price) { diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md b/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md index 9d3f56c98b996..65818ff14a7f5 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md +++ b/solution/2000-2099/2034.Stock Price Fluctuation/README_EN.md @@ -126,7 +126,6 @@ class StockPrice { private int last; public StockPrice() { - } public void update(int timestamp, int price) { @@ -170,7 +169,6 @@ class StockPrice { class StockPrice { public: StockPrice() { - } void update(int timestamp, int price) { diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp index 429b7565d2ad5..f6dfa6e54aa6a 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.cpp @@ -1,9 +1,8 @@ class StockPrice { public: StockPrice() { - } - + void update(int timestamp, int price) { if (d.count(timestamp)) { ls.erase(ls.find(d[timestamp])); @@ -12,15 +11,15 @@ class StockPrice { ls.insert(price); last = max(last, timestamp); } - + int current() { return d[last]; } - + int maximum() { return *ls.rbegin(); } - + int minimum() { return *ls.begin(); } diff --git a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java index 1e98204161f5e..a96df48068a57 100644 --- a/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java +++ b/solution/2000-2099/2034.Stock Price Fluctuation/Solution.java @@ -4,7 +4,6 @@ class StockPrice { private int last; public StockPrice() { - } public void update(int timestamp, int price) {