From ed8325560470f59b7c1cea4c86d355a9d68245b4 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Fri, 29 Nov 2024 21:27:21 -0600 Subject: [PATCH] fix(post): fetch code lazily --- posts/algorithms/extrema-circular-buffer.html | 166 +----------------- .../extrema-circular-buffer/map.cpp | 56 ++++++ .../extrema-circular-buffer/monotonic.cpp | 65 +++++++ .../extrema-circular-buffer/naive.cpp | 38 ++++ public/prism/prism-theme.css | 4 +- scripts/index.js | 2 - scripts/post.js | 36 ++++ styles/common.css | 5 - styles/index.css | 4 + styles/post.css | 5 +- 10 files changed, 209 insertions(+), 172 deletions(-) create mode 100644 public/code/algorithms/extrema-circular-buffer/map.cpp create mode 100644 public/code/algorithms/extrema-circular-buffer/monotonic.cpp create mode 100644 public/code/algorithms/extrema-circular-buffer/naive.cpp diff --git a/posts/algorithms/extrema-circular-buffer.html b/posts/algorithms/extrema-circular-buffer.html index 7980a09..58ebb74 100644 --- a/posts/algorithms/extrema-circular-buffer.html +++ b/posts/algorithms/extrema-circular-buffer.html @@ -115,44 +115,7 @@ operations. The minimum/maximum element must be found via a linear scan in \(O(n)\) time, certainly far from optimal.

-
#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;
-};
+

optimizing the approach

@@ -174,9 +137,7 @@ private: std::map<double, size_t>std::map allows us to do all of this.

@@ -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.

-
#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;
-};
+

monotonic queues deques

@@ -298,71 +204,7 @@ private: once; across a sequence of \(n\) operations, \(n\) total \(O(1)\) operations will be executed).

-
#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;
-};
+

further improvements

  1. diff --git a/public/code/algorithms/extrema-circular-buffer/map.cpp b/public/code/algorithms/extrema-circular-buffer/map.cpp new file mode 100644 index 0000000..9adb021 --- /dev/null +++ b/public/code/algorithms/extrema-circular-buffer/map.cpp @@ -0,0 +1,56 @@ +#include +#include +#include + +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 prices; + std::map sorted_prices; + size_t capacity; +}; diff --git a/public/code/algorithms/extrema-circular-buffer/monotonic.cpp b/public/code/algorithms/extrema-circular-buffer/monotonic.cpp new file mode 100644 index 0000000..68e1f75 --- /dev/null +++ b/public/code/algorithms/extrema-circular-buffer/monotonic.cpp @@ -0,0 +1,65 @@ +#include +#include +#include + +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 prices; + std::deque> maxs; + size_t capacity; +}; diff --git a/public/code/algorithms/extrema-circular-buffer/naive.cpp b/public/code/algorithms/extrema-circular-buffer/naive.cpp new file mode 100644 index 0000000..c43d3c8 --- /dev/null +++ b/public/code/algorithms/extrema-circular-buffer/naive.cpp @@ -0,0 +1,38 @@ +#include +#include +#include + +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 prices; + size_t capacity; +}; diff --git a/public/prism/prism-theme.css b/public/prism/prism-theme.css index d1550c7..cadd28e 100644 --- a/public/prism/prism-theme.css +++ b/public/prism/prism-theme.css @@ -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; -} \ No newline at end of file +} diff --git a/scripts/index.js b/scripts/index.js index 23885b2..24fb812 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -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" }, ], ], ]); diff --git a/scripts/post.js b/scripts/post.js index 7d3d4d6..4a2db53 100644 --- a/scripts/post.js +++ b/scripts/post.js @@ -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); + } +} diff --git a/styles/common.css b/styles/common.css index ddda8f3..b68bf62 100644 --- a/styles/common.css +++ b/styles/common.css @@ -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; -} diff --git a/styles/index.css b/styles/index.css index a4f1480..5582d37 100644 --- a/styles/index.css +++ b/styles/index.css @@ -56,6 +56,10 @@ ul { flex: 1; } +a { + text-decoration: none; +} + .post { margin-bottom: 25px; } diff --git a/styles/post.css b/styles/post.css index 9615d6c..643efa2 100644 --- a/styles/post.css +++ b/styles/post.css @@ -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;