You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: posts/algos/lowest-common-ancestor-of-a-binary-search-tree.txt
+53-8Lines changed: 53 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -7,7 +7,7 @@ This post will take you on a journey from the basics of trees to a specific type
7
7
8
8
## What is a Tree?
9
9
10
-
In computer science, a **Tree** is a hierarchical data structure that consists of nodes connected by edges.
10
+
In computer science, a **Tree** is a hierarchical data structure that consists of nodes connected by edges.
11
11
12
12
Unlike linear data structures like arrays or linked lists, trees are non-linear and are used to represent hierarchical relationships.
13
13
@@ -27,7 +27,7 @@ Trees are used in various applications, such as file systems, organization chart
27
27
28
28
## Binary Search Trees (BSTs)
29
29
30
-
A **Binary Search Tree (BST)** is a special type of binary tree where the nodes are ordered in a specific way.
30
+
A **Binary Search Tree (BST)** is a special type of binary tree where the nodes are ordered in a specific way.
31
31
32
32
This ordering makes operations like searching, insertion, and deletion very efficient.
33
33
@@ -60,7 +60,7 @@ Let's explore how to implement some of these fundamental tree algorithms in Go.
60
60
61
61
### Finding the Height/Depth of a Binary Tree
62
62
63
-
The **height** of a binary tree is the number of edges on the longest path from the root node to a leaf node. A tree with only a root node has a height of 0.
63
+
The **height** of a binary tree is the number of edges on the longest path from the root node to a leaf node. A tree with only a root node has a height of 0.
64
64
65
65
The concept is closely related to the **depth** of a node, which is its distance from the root. The height of the tree is, therefore, the maximum depth of any node in the tree.
## LeetCode 235: Lowest Common Ancestor of a Binary Search Tree
140
140
141
-
Now, let's apply our knowledge to a classic problem.
141
+
Now, let's apply our knowledge to a classic problem.
142
142
143
143
The **Lowest Common Ancestor (LCA)** of two nodes, `p` and `q`, in a tree is the lowest (i.e., deepest) node that has both `p` and `q` as descendants.
144
144
@@ -165,15 +165,15 @@ For example, consider the following BST:
165
165
166
166
### The Solution
167
167
168
-
The properties of a BST make finding the LCA particularly efficient.
168
+
The properties of a BST make finding the LCA particularly efficient.
169
169
170
170
We can start at the root of the tree and use the values of `p` and `q` to decide where to go next.
171
171
172
172
Let's consider the current node we are at, let's call it `current`.
173
173
174
174
1. If both `p` and `q` are **greater** than `current.val`, it means that the LCA must be in the **right** subtree. So, we move to the right child.
175
175
2. If both `p` and `q` are **less** than `current.val`, it means that the LCA must be in the **left** subtree. So, we move to the left child.
176
-
3. If one of `p` or `q` is greater than `current.val` and the other is less than `current.val` (or if one of them is equal to `current.val`), then `current` is the LCA.
176
+
3. If one of `p` or `q` is greater than `current.val` and the other is less than `current.val` (or if one of them is equal to `current.val`), then `current` is the LCA.
177
177
178
178
This is because `p` and `q` are on opposite sides of the current node, meaning it's the split point and thus the lowest common ancestor.
Both of these solutions are very efficient, with a time complexity of O(H), where H is the height of the tree. In a balanced BST, this is O(log N), where N is the number of nodes.
236
+
Both of these solutions are very efficient, with a time complexity of O(H), where H is the height of the tree. In a balanced BST, this is O(log N), where N is the number of nodes.
237
237
238
238
The space complexity for the iterative solution is O(1), while the recursive solution has a space complexity of O(H) due to the call stack.
239
239
240
+
### Alternative: Stack-Based Solution
241
+
242
+
Another way to solve this problem is by finding the path from the root to each of the two nodes, `p` and `q`. Once we have both paths, we can compare them to find the last common node, which is the LCA.
243
+
244
+
This method is more generic and would also work for a regular binary tree, but it's less efficient for a BST than the previous solutions because it requires traversing parts of the tree multiple times and uses extra space to store the paths.
245
+
246
+
Here is the implementation in Go:
247
+
248
+
```go
249
+
// Helper function to find the path from the root to a target node
for i := 0; i < len(pathP) && i < len(pathQ); i++ {
274
+
if pathP[i] == pathQ[i] {
275
+
lca = pathP[i]
276
+
} else {
277
+
break
278
+
}
279
+
}
280
+
281
+
return lca
282
+
}
283
+
```
284
+
240
285
## Conclusion
241
286
242
-
Trees and Binary Search Trees are powerful data structures that are essential for any programmer's toolkit. By understanding their properties and the algorithms that operate on them, you can solve a wide range of problems efficiently.
287
+
Trees and Binary Search Trees are powerful data structures that are essential for any programmer's toolkit. By understanding their properties and the algorithms that operate on them, you can solve a wide range of problems efficiently.
243
288
244
289
The Lowest Common Ancestor problem is a perfect example of how the structure of a BST can be leveraged to find an elegant and optimal solution.
# Demystifying Tailwind CSS in `fezcodex`: A Utility-First Approach
3
+
4
+
In the `fezcodex` project, you'll notice that our components are styled **cool**(!). Instead of writing custom CSS for every element, **Tailwind CSS** is used. This post will explain what Tailwind CSS is, how it's configured in my project,
5
+
and why it's a powerful tool for building user interfaces.
6
+
7
+
## Part 1: The Core Concept - Utility-First CSS
8
+
9
+
Traditionally, when styling a webpage, you might write CSS like this:
10
+
11
+
```css
12
+
.my-button {
13
+
background-color: blue;
14
+
color: white;
15
+
padding: 1rem;
16
+
border-radius: 0.5rem;
17
+
}
18
+
```
19
+
20
+
And then apply it in your HTML:
21
+
22
+
```html
23
+
<button class="my-button">Click Me</button>
24
+
```
25
+
26
+
Tailwind CSS takes a **utility-first** approach. Instead of writing custom CSS classes for every component, you apply small, single-purpose utility classes directly in your HTML (or JSX, in our case). Each class does one thing, and one thing only.
27
+
28
+
For example, the same button in Tailwind would look like this:
* **Rapid UI Development:** You can build complex UIs much faster because you're not constantly switching between HTML/JSX and CSS files. All the styling happens directly in your markup.
37
+
* **Consistent Design:** By using a predefined set of utility classes (which are based on a design system), it's much easier to maintain a consistent look and feel across your application.
38
+
* **No More Unused CSS:** Tailwind, especially with its JIT (Just-In-Time) mode, only generates the CSS that you actually use in your project. This results in incredibly small and optimized CSS bundles, improving performance.
39
+
* **Avoid Naming Headaches:** You no longer have to come up with semantic class names for every single element, which can be a surprisingly difficult task in larger projects.
40
+
41
+
## Part 2: Tailwind in `fezcodex` - Configuration and Customization
42
+
43
+
Our project customizes Tailwind to fit its specific design needs. This is primarily managed through two files:
44
+
45
+
### `tailwind.config.js`
46
+
47
+
This is the central configuration file for Tailwind CSS. It tells Tailwind how to behave and what custom styles to include.
48
+
49
+
* **`content`**: This array (`./src/**/*.{js,jsx,ts,tsx}`) tells Tailwind which files to scan for utility classes. This is crucial for the build process to identify and include only the necessary CSS.
50
+
* **`theme.extend.colors`**: This is where we integrate our custom color palette. You'll see it imports `colors` from `./src/config/colors.js`. This means that any color defined in `colors.js` (like `article: '#FA8072'`) becomes available as a Tailwind utility class. For example:
51
+
* `text-article` will apply the `article` color to text.
52
+
* `bg-article` will apply the `article` color to the background.
53
+
* `border-article` will apply the `article` color to the border.
54
+
This is why we use `text-article` and not just `article` – the `text-` prefix tells Tailwind *what* CSS property to apply the color to.
55
+
* **`theme.extend.fontFamily`**: Similar to colors, this section allows us to define and use custom fonts (imported from `./src/config/fonts.js`) throughout the project using Tailwind's `font-{name}` classes.
56
+
* **`plugins`**: We use `@tailwindcss/typography` here. This plugin provides a set of `prose` classes that can be used to style raw HTML (like the content generated from Markdown files) with beautiful, readable typography, without having to manually style every heading, paragraph, and list item.
57
+
58
+
### `src/config/colors.js` and `src/config/fonts.js`
59
+
60
+
These files act as our project's design token repositories. They centralize all our custom colors and font definitions, making it easy to manage and update our design system from a single source.
61
+
62
+
## Part 3: How It All Comes Together - Building UI
63
+
64
+
When you look at a component like `AppCard.js` or `WordCounterPage.js`, you'll see a lot of classes directly in the JSX. For example, a card might have classes like:
* `bg-transparent`: Sets the background to transparent.
75
+
* `border`: Adds a default border.
76
+
* `rounded-lg`: Applies a large border-radius.
77
+
* `shadow-lg`: Adds a large box shadow.
78
+
* `p-6`: Adds padding of `1.5rem` on all sides.
79
+
* `flex flex-col justify-between`: Configures the element as a flex container, arranging its children in a column and distributing space between them.
80
+
* `hover:scale-105 hover:shadow-2xl`: These are **variant** classes. They apply `scale-105` (makes the element 5% larger) and `shadow-2xl` (a larger shadow) *only when the element is hovered over*.
81
+
* `transition-all duration-300 ease-in-out`: Ensures that changes to properties like `transform` (for `scale`) and `box-shadow` (for `shadow`) happen smoothly over 300 milliseconds.
82
+
83
+
Tailwind also makes responsive design easy with prefixes like `sm:`, `md:`, `lg:`, and `xl:`. For example, `md:flex` would make an element a flex container only on medium screens and larger.
84
+
85
+
## Part 4: The Build Process
86
+
87
+
During development and when building for production, a tool like **Craco** (which sits on top of Create React App's Webpack configuration) processes your code. It uses **PostCSS** and the Tailwind plugin to scan all your files for Tailwind utility classes. It then generates a minimal CSS file containing *only* the styles corresponding to the classes you've actually used. This ensures that your final application bundle is as small and performant as possible.
88
+
89
+
## Conclusion
90
+
91
+
Tailwind CSS provides a powerful and efficient way to build and maintain the UI of the `fezcodex` project. By embracing its utility-first philosophy and leveraging its extensive configuration options, we can rapidly develop consistent, responsive, and performant user interfaces. It streamlines the styling process, allowing developers to focus more on functionality and less on managing complex CSS stylesheets.
0 commit comments