Skip to content

Commit

Permalink
Stacks and queues
Browse files Browse the repository at this point in the history
  • Loading branch information
herrera-ignacio committed Dec 3, 2024
1 parent df2b377 commit 8cedbe1
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 57 deletions.
3 changes: 2 additions & 1 deletion .obsidian/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"vimMode": true,
"showInlineTitle": false,
"alwaysUpdateLinks": true,
"attachmentFolderPath": "./_attachments"
"attachmentFolderPath": "./_attachments",
"showLineNumber": true
}
2 changes: 1 addition & 1 deletion .obsidian/plugins/obsidian-minimal-settings/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"underlineInternal": true,
"underlineExternal": true,
"folding": true,
"lineNumbers": false,
"lineNumbers": true,
"readableLineLength": true,
"devBlockWidth": false
}
65 changes: 11 additions & 54 deletions .obsidian/workspace.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,21 @@
"id": "1ef8a9d0e455d5f2",
"type": "tabs",
"children": [
{
"id": "5be967f0881160fb",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Data structures/Hashing/Universal hashing.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Universal hashing"
}
},
{
"id": "73686db3a8fa2556",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Data structures/Hashing/Hash map.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Hash map"
}
},
{
"id": "26cfaac60005359e",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Data structures/Arrays/Sliding window.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Sliding window"
}
},
{
"id": "c342bf80ea3addd5",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Data structures/Linked Lists/Reversing a linked list.md",
"file": "Data structures/Stacks and queues/String problems using stacks.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Reversing a linked list"
"title": "String problems using stacks"
}
}
],
"currentTab": 3
]
}
],
"direction": "vertical"
Expand Down Expand Up @@ -219,10 +176,16 @@
},
"active": "c342bf80ea3addd5",
"lastOpenFiles": [
"Data structures/Linked Lists/Fast and slow pointers.md",
"Data structures/README.md",
"Data structures/Data structure.md",
"README.md",
"Data structures/Stacks and queues/String problems using stacks.md",
"Data structures/Stacks and queues/Stacks.md",
"Data structures/Stacks and queues",
"Data structures/Abstract Data Type.md",
"Data structures/Linked Lists/Reversing a linked list.md",
"Data structures/Linked Lists/Fast and slow pointers.md",
"Data structures/Linked Lists/Linked Lists.md",
"Data structures/README.md",
"Data structures/Linked Lists",
"Data structures/Hashing/Hashing.md",
"Data structures/Hashing/Hash map.md",
Expand All @@ -234,10 +197,7 @@
"Data structures/Hashing/Hash function.md",
"Data structures/Hashing/_attachments/Pasted image 20241114124706.png",
"Data structures/Hashing/_attachments",
"Data structures/Data structure.md",
"READMEv2.md",
"README.md",
"Data structures/Abstract Data Type.md",
"Data structures/Arrays/Array.md",
"Data structures/Arrays/Prefix sum.md",
"Data structures/Hashing",
Expand All @@ -254,12 +214,9 @@
"legacy/glossary/spof/README.md",
"legacy/glossary/modularization/README.md",
"legacy/glossary/side-effect/README.md",
"legacy/glossary/pattern/README.md",
"legacy/glossary/crud/README.md",
"legacy/glossary/web-api",
"legacy/glossary/spof",
"legacy/glossary/side-effect",
"legacy/glossary/pattern",
"legacy/frontend/legacy-lifecycle/2022-12-28-22-40-07.png",
"legacy/frontend/legacy-lifecycle/2022-12-28-22-39-07.png",
"legacy/frontend/design-system/2022-11-09-09-19-31.png",
Expand Down
9 changes: 8 additions & 1 deletion Data structures/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@
- [[Hashing]]
- [[Universal hashing]]
- [[Hash map]]
- [[Sets]]
- [[Sets]]
## Linked Lists
- [[Linked Lists|What's a linked list?]]
- [[Fast and slow pointers]]
- [[Reversing a linked list]]
## Stacks and queues
- [[Stacks]]
- [[String problems using stacks]]
10 changes: 10 additions & 0 deletions Data structures/Stacks and queues/Stacks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Stacks
## Overview
A stack is an **ordered collection of elements** where elements are only added and removed from the same end (**LIFO - Last In First Out**).
In the physical world, an example of a stack would be a stack of plates in a kitchen - you add plates or remove plates from the top of the pile. In the software world, a good example of a stack is the history of your current browser's tab.
Typically, inserting into a stack is called **pushing** and removing from a stack is called **popping**. Stacks will usually also come with operations like **peek**, which means looking at the element at the top of the stack.
## Implementation
The time complexity of stack operations is dependent on the implementation. If you use a dynamic array, which is the most common and easiest way, then the time complexity of your operations is the same as that of a dynamic array. $O(1)$ push, pop, and random access, and $O(n)$ search. Sometimes, a stack may be implemented with a linked list with a tail pointer.
## When to use it?
For algorithm problems, a stack is a good option whenever you can recognize the LIFO pattern. Usually, there will be some component of the problem that involves elements in the input interacting with each other.
Interacting could mean matching elements together, querying some property such as "how far is the next largest element", evaluating a mathematical equation given as a string, just comparing elements against each other, or any other abstract interaction.
149 changes: 149 additions & 0 deletions Data structures/Stacks and queues/String problems using stacks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# String problems using stacks
## Overview
Normally, string questions that can utilize a stack will involve iterating over the string and putting characters into the stack, and comparing the top of the stack with the current character at each iteration.
Stacks are useful for **string matching** because it saves a "history" of the previous characters.
## Examples
### Example: Valid parentheses
Given a string `s` containing just the characters `'('``')'``'{'``'}'``'['` and `']'`, determine if the input string is valid. The string is valid if all open brackets are closed by the same type of closing bracket in the correct order, and each closing bracket closes exactly one open bracket.

For example, `s = "({})"` and `s = "(){}[]"` are valid, but `s = "(]"` and `s = "({)}"` are not valid.
#### Solution
The "correct" order is determined by whatever the previous opening bracket was. The order is **last in, first out (LIFO)** - the last opening bracket we saw is the first one we should close, which is perfect functionality for a stack to provide.
As we iterate over the string, if we see an opening bracket, we should put it on the stack. If we see a closing bracket, we can check the most recent unclosed opening bracket by popping it from the top of the stack. If it matches, then continue, if it doesn't, or there is no opening bracket on the stack at all (this would occur in a case like `"{}]"`), then we know the string is invalid. In the end, there should be no unmatched open brackets (like in the case of `"(){"`), so the stack should be empty for the string to be valid.
How can we associate the opening and closing brackets together? We can use a hash map to map each opening bracket to its closing bracket. Then, when we see a closing bracket, we can use the top of the stack as a key and check if the value is equal to the current character.
```python
class Solution:
def isValid(self, s: str) -> bool:
stack = []
matching = {"(": ")", "[": "]", "{": "}"}

for c in s:
if c in matching: # if c is an opening bracket
stack.append(c)
else:
if not stack:
return False

previous_opening = stack.pop()
if matching[previous_opening] != c:
return False

return not stack
```

```javascript
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
let stack = [];
const matching = {
"(": ")",
"[": "]",
"{": "}"
}

for (const c of s) {
if (c in matching) { // if c is an opening bracket
stack.push(c);
} else {
if (!stack.length) {
return false;
}

let previousOpening = stack.pop();
if (matching[previousOpening] != c) {
return false;
}
}
}

return !stack.length;
};
```
Because the stack's push and pop operations are $O(1)$, this gives us a time complexity of $O(n)$, where $n$ is the size of the input array. This is because each element can only be pushed or popped once. The space complexity is also $O(n)$ because the stack's size can grow linearly with the input size.
### Example: Remove all adjacent duplicates in string
You are given a string `s`. Continuously remove duplicates (two of the same character beside each other) until you can't anymore. Return the final string after this.

For example, given `s = "abbaca"`, you can first remove the `"bb"` to get `"aaca"`. Next, you can remove the `"aa"` to get `"ca"`. This is the final answer.
#### Solution
The tricky part of this problem is that not all removals are necessarily available at the start. As you can see in the example, deleting the `"aa"` is only possible **after** deleting the `"bb"`. We don't need to delete the first character until we have already iterated quite a bit past it
Iterate over the input and put characters in the stack. At each step, if the top of the stack is the same as the current character, we know that they are adjacent (at some point in time) and can be deleted.

```python
class Solution:
def removeDuplicates(self, s: str) -> str:
stack = []
for c in s:
if stack and stack[-1] == c:
stack.pop()
else:
stack.append(c)

return "".join(stack)
```

```javascript
/**
* @param {string} s
* @return {string}
*/
var removeDuplicates = function(s) {
let stack = [];
for (const c of s) {
if (stack.length && stack[stack.length - 1] == c) {
stack.pop();
} else {
stack.push(c);
}
}

return stack.join("");
};
```
### Example: Backspace string compare
Given two strings `s` and `t`, return true if they are equal when both are typed into empty text editors. `'#'` means a backspace character.

For example, given `s = "ab#c"` and `t = "ad#c"`, return true. Because of the backspace, the strings are both equal to `"ac"`.
#### Solution
When typing characters, push them onto a stack. Whatever character is at the top of the stack is the most recently typed character, so when we backspace, we can just pop. Make sure to be careful of the edge case where we backspace on an empty string.
```python
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
def build(s):
stack = []
for c in s:
if c != "#":
stack.append(c)
elif stack:
stack.pop()

return "".join(stack)

return build(s) == build(t)
```

```javascript
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var backspaceCompare = function(s, t) {
let build = s => {
let stack = [];
for (const c of s) {
if (c != "#") {
stack.push(c);
} else if (stack.length) {
stack.pop();
}
}

return stack.join("");
}

return build(s) == build(t);
};
```

0 comments on commit 8cedbe1

Please sign in to comment.