fix(post): fetch code lazily
This commit is contained in:
parent
ee3a3b61a2
commit
ed83255604
10 changed files with 209 additions and 172 deletions
|
|
@ -115,44 +115,7 @@
|
|||
operations. The minimum/maximum element must be found via a linear
|
||||
scan in \(O(n)\) time, certainly far from optimal.
|
||||
</p>
|
||||
<pre><code class="language-cpp">#include <algorithm>
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return *std::max_element(prices.begin(), prices.end());
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<double> prices;
|
||||
size_t capacity;
|
||||
};</code></pre>
|
||||
<div class="code" data-file="naive.cpp"</div>
|
||||
</div>
|
||||
<h3>optimizing the approach</h3>
|
||||
<div class="problem-content">
|
||||
|
|
@ -174,9 +137,7 @@ private:
|
|||
<a
|
||||
target="blank"
|
||||
href="https://en.cppreference.com/w/cpp/container/map"
|
||||
><span class="inline-code"
|
||||
><code>std::map<double, size_t></code></span
|
||||
></a
|
||||
>std::map</a
|
||||
>
|
||||
allows us to do all of this.
|
||||
</p>
|
||||
|
|
@ -184,62 +145,7 @@ private:
|
|||
Now, we can access extrema instantly. Insertion and deletion take
|
||||
\(O(log(n))\) time thanks to the map—but we can do better.
|
||||
</p>
|
||||
<pre><code class="language-cpp">#include <deque>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
double front = prices.front();
|
||||
|
||||
if (--sorted_prices[front] == 0)
|
||||
sorted_prices.erase(front);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
++sorted_prices[value];
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
double front = prices.front();
|
||||
|
||||
if (--sorted_prices[front] == 0)
|
||||
sorted_prices.erase(front);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get_max() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return sorted_prices.rbegin()->first;
|
||||
}
|
||||
|
||||
double get_min() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find min() of empty buffer");
|
||||
}
|
||||
|
||||
return sorted_prices.begin()->first;
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<double> prices;
|
||||
std::map<double, size_t> sorted_prices;
|
||||
size_t capacity;
|
||||
};</code></pre>
|
||||
<div class="code" data-file="map.cpp"></div>
|
||||
</div>
|
||||
<h3>monotonic <s>queues</s> deques</h3>
|
||||
<div class="problem-content">
|
||||
|
|
@ -298,71 +204,7 @@ private:
|
|||
once; across a sequence of \(n\) operations, \(n\) total \(O(1)\)
|
||||
operations will be executed).
|
||||
</p>
|
||||
<pre><code class="language-cpp">#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
explicit ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
double front_value = prices.front();
|
||||
pop_max(front_value);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
push_max(value);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
double front_value = prices.front();
|
||||
pop_max(front_value);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get_max() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return maxs.front().first;
|
||||
}
|
||||
|
||||
private:
|
||||
void push_max(double value) {
|
||||
size_t popped = 0;
|
||||
|
||||
while (!maxs.empty() && maxs.back().first < value) {
|
||||
popped += maxs.back().second + 1;
|
||||
maxs.pop_back();
|
||||
}
|
||||
|
||||
maxs.emplace_back(value, popped);
|
||||
}
|
||||
|
||||
void pop_max(double value) {
|
||||
size_t popped = maxs.front().second;
|
||||
|
||||
if (popped == 0) {
|
||||
maxs.pop_front();
|
||||
} else {
|
||||
--maxs.front().second;
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<double> prices;
|
||||
std::deque<std::pair<double, size_t>> maxs;
|
||||
size_t capacity;
|
||||
};</code></pre>
|
||||
<div class="code" data-file="monotonic.cpp"></div>
|
||||
<h3>further improvements</h3>
|
||||
<ol>
|
||||
<li>
|
||||
|
|
|
|||
56
public/code/algorithms/extrema-circular-buffer/map.cpp
Normal file
56
public/code/algorithms/extrema-circular-buffer/map.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include <deque>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
double front = prices.front();
|
||||
|
||||
if (--sorted_prices[front] == 0)
|
||||
sorted_prices.erase(front);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
++sorted_prices[value];
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
double front = prices.front();
|
||||
|
||||
if (--sorted_prices[front] == 0)
|
||||
sorted_prices.erase(front);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get_max() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return sorted_prices.rbegin()->first;
|
||||
}
|
||||
|
||||
double get_min() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find min() of empty buffer");
|
||||
}
|
||||
|
||||
return sorted_prices.begin()->first;
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<double> prices;
|
||||
std::map<double, size_t> sorted_prices;
|
||||
size_t capacity;
|
||||
};
|
||||
65
public/code/algorithms/extrema-circular-buffer/monotonic.cpp
Normal file
65
public/code/algorithms/extrema-circular-buffer/monotonic.cpp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <deque>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
explicit ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
double front_value = prices.front();
|
||||
pop_max(front_value);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
push_max(value);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
double front_value = prices.front();
|
||||
pop_max(front_value);
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get_max() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return maxs.front().first;
|
||||
}
|
||||
|
||||
private:
|
||||
void push_max(double value) {
|
||||
size_t popped = 0;
|
||||
|
||||
while (!maxs.empty() && maxs.back().first < value) {
|
||||
popped += maxs.back().second + 1;
|
||||
maxs.pop_back();
|
||||
}
|
||||
|
||||
maxs.emplace_back(value, popped);
|
||||
}
|
||||
|
||||
void pop_max(double value) {
|
||||
size_t popped = maxs.front().second;
|
||||
|
||||
if (popped == 0) {
|
||||
maxs.pop_front();
|
||||
} else {
|
||||
--maxs.front().second;
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<double> prices;
|
||||
std::deque<std::pair<double, size_t>> maxs;
|
||||
size_t capacity;
|
||||
};
|
||||
38
public/code/algorithms/extrema-circular-buffer/naive.cpp
Normal file
38
public/code/algorithms/extrema-circular-buffer/naive.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <stdexcept>
|
||||
|
||||
class ExtremaCircularBuffer {
|
||||
public:
|
||||
ExtremaCircularBuffer(size_t capacity) : capacity(capacity) {}
|
||||
|
||||
void push_back(double value) {
|
||||
if (prices.size() == capacity) {
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
prices.push_back(value);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot pop_front() from empty buffer");
|
||||
}
|
||||
|
||||
prices.pop_front();
|
||||
}
|
||||
|
||||
size_t size() const { return prices.size(); }
|
||||
|
||||
double get() const {
|
||||
if (prices.empty()) {
|
||||
throw std::out_of_range("Cannot find max() of empty buffer");
|
||||
}
|
||||
|
||||
return *std::max_element(prices.begin(), prices.end());
|
||||
}
|
||||
|
||||
private:
|
||||
std::deque<double> prices;
|
||||
size_t capacity;
|
||||
};
|
||||
|
|
@ -41,7 +41,7 @@ pre[class*="language-"] {
|
|||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
background: #f6f8fa;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em .3em;
|
||||
|
|
@ -129,4 +129,4 @@ pre[class*="language-"] > code[class*="language-"] {
|
|||
}
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,7 @@ const postMapping = new Map([
|
|||
[
|
||||
"Algorithms",
|
||||
[
|
||||
{ name: "leetcode daily", link: "leetcode-daily" },
|
||||
{ name: "extrema circular buffer", link: "extrema-circular-buffer" },
|
||||
{ name: "two pointers", link: "two-pointers" },
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -20,3 +20,39 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||
document.querySelectorAll(".post-article h2").forEach(setStyle);
|
||||
document.querySelectorAll(".post-article h3").forEach(setStyle);
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
document.querySelectorAll(".code").forEach(loadCode);
|
||||
});
|
||||
|
||||
async function loadCode(e) {
|
||||
const file = e.dataset.file;
|
||||
const language = file.substring(file.lastIndexOf(".") + 1);
|
||||
let [_, __, topic, post] = window.location.pathname.split("/");
|
||||
post = post.substring(0, post.lastIndexOf("."));
|
||||
|
||||
try {
|
||||
const path = `/public/code/${topic}/${post}/${file}`;
|
||||
const response = await fetch(path);
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(
|
||||
`Failed to fetch ${path}: ${response.status} ${response.statusText}\n${errorText}`,
|
||||
);
|
||||
}
|
||||
|
||||
const code = await response.text();
|
||||
|
||||
const pre = document.createElement("pre");
|
||||
const codeElement = document.createElement("code");
|
||||
codeElement.className = `language-${language}`;
|
||||
codeElement.textContent = code;
|
||||
|
||||
pre.appendChild(codeElement);
|
||||
e.appendChild(pre);
|
||||
|
||||
Prism.highlightElement(codeElement);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ footer {
|
|||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
li {
|
||||
|
|
@ -79,7 +78,3 @@ li {
|
|||
.terminal-container {
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
.inline-code {
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,10 @@ ul {
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ li {
|
|||
|
||||
:not(pre) > code {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
background-color: #f4f4f4;
|
||||
padding: 2px 4px;
|
||||
margin: 0 5px;
|
||||
border: 1px solid #e1e1e1;
|
||||
|
|
@ -60,6 +59,10 @@ li {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
code {
|
||||
background: #f4f4f4 !important;
|
||||
}
|
||||
|
||||
.post-title::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue