This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Data Structures & Algorithms
Software Engineer | 50+ LeetCode Problems Solved | Go Enthusiast
Check out my profile @tigonguyen
This section is organized into the following categories:
-
Data Structures
- Stack
- Queue / Deque
- Heap
- Binary Tree
- Linked List
- Binary Search Tree (set / multiset) / (map / multimap)
- Bitset
-
Techniques
- Two Pointers
- Binary Search
- Binary Search on the Answer
- Binary Search on Float Number + Ternary Search
- Greedy
- Recursion + Backtracking
- Divide and Conquer
Each solution is documented with:
- Problem Description with LeetCode Link
- Clear Intuition & Implementation Approach
- Complexity Analysis
1 - Data Structures
Solutions organized by core data structures including stacks, queues, heaps, trees, and more
Collection of LeetCode solutions categorized by fundamental data structures:
- Stack
- Queue / Deque
- Heap
- Binary Tree
- Linked List
- Binary Search Tree (set / multiset) / (map / multimap)
- Bitset
1.1 - Stack
Solutions that utilize stack data structure
A stack is a Last-In-First-Out (LIFO) data structure that supports two main operations: push (insertion) and pop (deletion). In C++, you can use the built-in stack
container from the Standard Template Library (STL).
Basic Stack Operations in C++
#include <stack>
// Declaration
stack<long long> mystack; // Create an empty stack of long long integers
// Basic Operations
// 1. push(value) - Add element to top of stack - O(1)
mystack.push(6); // mystack = [6]
mystack.push(3); // mystack = [6, 3]
mystack.push(9); // mystack = [6, 3, 9]
// 2. pop() - Remove element from top of stack - O(1)
mystack.pop(); // Removes 9, mystack = [6, 3]
// 3. top() - Get value of top element - O(1)
int topValue = mystack.top(); // Returns 3
// 4. size() - Get number of elements in stack - O(1)
int stackSize = mystack.size(); // Returns 2
// 5. empty() - Check if stack is empty - O(1)
bool isEmpty = mystack.empty(); // Returns false
Important Notes:
- Always check if stack is not empty before calling
pop()
or top()
- Stack has constant time O(1) complexity for all basic operations
- Stack is particularly useful for:
- Function call management
- Expression evaluation
- Backtracking algorithms
- Undo operations
- Parentheses matching
Common Mistakes to Avoid:
- Calling
pop()
or top()
on empty stack
- Not including
<stack>
header
- Forgetting that
pop()
doesn’t return the removed value
Example Usage:
#include <stack>
#include <iostream>
using namespace std;
int main() {
stack<int> st;
// Push elements
st.push(1);
st.push(2);
st.push(3);
// Safe way to process stack
while (!st.empty()) {
cout << st.top() << " "; // Print top element
st.pop(); // Remove top element
}
// Output: 3 2 1
return 0;
}
Problems that demonstrate the effective use of stack data structure to solve algorithmic challenges.
1.2 - Queue / Deque
Solutions that utilize queue and double-ended queue data structures
Problems that demonstrate the effective use of queue and double-ended queue data structures to solve algorithmic challenges.
1.3 - Heap
Solutions that utilize heap data structure
Problems that demonstrate the effective use of heap data structure (priority queue) to solve algorithmic challenges.
1.4 - Binary Tree
Solutions that utilize binary tree data structure
Problems that demonstrate the effective use of binary tree data structure to solve algorithmic challenges.
1.4.1 - 199. Binary Tree Right Side View
LeetCode #199 - View a binary tree from the right side
Problem Description
LeetCode Problem Link
Given the root
of a binary tree, imagine yourself standing on the right side of it. Return the values of the nodes you can see ordered from top to bottom.
Constraints:
- The number of nodes in the tree is in the range
[0, 100]
-100 ≤ Node.val ≤ 100
Example:
Input: root = [1,2,3,null,5,null,4]
Output: [1,3,4]
Explanation: The nodes visible from the right side are 1, 3, and 4.
Intuition and Approach
Intuition
When viewing a binary tree from the right side, we want to see the rightmost node at each level. Using a queue for level-order traversal (BFS), we can capture the last node at each level to build our right-side view.
Approach
- Use a queue to perform level-order traversal
- For each level:
- Get the current level size
- Process all nodes at this level
- Add the last node’s value to our result
- Return the collected right-side values
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func rightSideView(root *TreeNode) []int {
if root == nil {
return []int{}
}
result := []int{}
queue := []*TreeNode{root}
for len(queue) > 0 {
levelSize := len(queue)
for i := 0; i < levelSize; i++ {
node := queue[0]
queue = queue[1:]
// If it's the last node in current level, add to result
if i == levelSize-1 {
result = append(result, node.Val)
}
// Add children to queue
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
}
return result
}
Time and Space Complexity
Time Complexity: O(n)
- Visit each node exactly once
- Queue operations are O(1)
Space Complexity: O(w)
- w is the maximum width of the tree
- In worst case (perfect binary tree), the last level has n/2 nodes
- Queue stores at most one level at a time
1.5 - Linked List
Solutions that utilize linked list data structure
Problems that demonstrate the effective use of linked list data structure to solve algorithmic challenges.
1.6 - Binary Search Tree
Solutions that utilize binary search tree data structures (set/multiset, map/multimap)
Problems that demonstrate the effective use of binary search tree data structures (set/multiset, map/multimap) to solve algorithmic challenges.
1.7 - Bitset
Solutions that utilize bitset data structure
Problems that demonstrate the effective use of bitset data structure to solve algorithmic challenges.
2 - Techniques
Solutions organized by algorithmic techniques and problem-solving strategies
Collection of LeetCode solutions categorized by fundamental algorithmic techniques:
- Two Pointers
- Binary Search
- Binary Search on the Answer
- Binary Search on Float Number + Ternary Search
- Greedy
- Recursion + Backtracking
- Divide and Conquer
2.1 - Two Pointers
Solutions that utilize the two pointers technique
Problems that demonstrate the effective use of two pointers technique to solve algorithmic challenges. This technique is particularly useful for array and string problems where we need to find pairs or process elements from both ends.
2.2 - Binary Search
Solutions that utilize the binary search algorithm
Problems that demonstrate the effective use of binary search algorithm to solve algorithmic challenges. This technique is used to efficiently search for elements in sorted arrays or to find optimal values in monotonic search spaces.
2.3 - Binary Search on the Answer
Solutions that utilize binary search to find the optimal answer
Problems that demonstrate the effective use of binary search technique to find the optimal answer in the solution space.
2.4 - Binary Search on Float & Ternary Search
Solutions that utilize binary search on float numbers and ternary search
Problems that demonstrate the effective use of binary search on floating-point numbers and ternary search techniques to solve algorithmic challenges.
2.5 - Greedy
Solutions that utilize greedy algorithms
Problems that demonstrate the effective use of greedy algorithms and techniques to solve algorithmic challenges.
2.6 - Recursion + Backtracking
Solutions that utilize recursion and backtracking techniques
Problems that demonstrate the effective use of recursion and backtracking techniques to solve algorithmic challenges.
2.7 - Divide and Conquer
Solutions that utilize divide and conquer algorithms
Problems that demonstrate the effective use of divide and conquer algorithms to solve algorithmic challenges.