Algorithms and Data Structures in TypeScript: From Beginner to Professional
Algorithms and data structures are the foundation upon which all modern software development is built. Whether you are writing a frontend in React, a backend in Node.js, or a mobile application, a deep understanding of these concepts allows you to create efficient, scalable, and fast applications. In this article, we will thoroughly examine key algorithms and data structures in TypeScript — a language that combines the flexibility of JavaScript with the rigor of static typing.
TypeScript is ideal for learning algorithms: strict typing helps avoid errors at the compilation stage, and support for generics allows you to create universal and reusable data structures. We will start with simple concepts and gradually move to more complex topics, accompanying each step with clear code examples.
1. Basic Data Structures: Stack and Queue
Stack and Queue are basic linear data structures that are used everywhere: from managing browser history to scheduling tasks in operating systems. Let's look at their implementation in TypeScript.
Stack (LIFO — Last In, First Out)
The stack works on the principle of "last in, first out". The main operations are: push (adding an element) and pop (removing the last element).
class Stack<T> { private items: T[] = [];
push(element: T): void { this.items.push(element); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }
isEmpty(): boolean { return this.items.length === 0; }
size(): number { return this.items.length; }}
// Example usageconst stack = new Stack<number>();stack.push(1);stack.push(2);stack.push(3);console.log(stack.pop()); // 3console.log(stack.peek()); // 2Queue (FIFO — First In, First Out)
The queue works on the principle of "first in, first out". The main operations are: enqueue (adding to the end) and dequeue (removing from the beginning).
class Queue<T> { private items: T[] = [];
enqueue(element: T): void { this.items.push(element); }
dequeue(): T | undefined { return this.items.shift(); }
front(): T | undefined { return this.items[0]; }
isEmpty(): boolean { return this.items.length === 0; }
size(): number { return this.items.length; }}
// Example usageconst queue = new Queue<string>();queue.enqueue("First");queue.enqueue("Second");queue.enqueue("Third");console.log(queue.dequeue()); // "First"console.log(queue.front()); // "Second"2. Search Algorithms: Linear and Binary Search
Searching for data is one of the most common operations in programming. We will look at two main approaches: linear search (simple but slow) and binary search (fast but requires sorted data).
Linear Search
The algorithm sequentially checks each element of the array until it finds the target. Time complexity: O(n).
function linearSearch<T>(arr: T[], target: T): number { for (let i = 0; i < arr.length; i++) { if (arr[i] === target) { return i; // Returns the index of the found element } } return -1; // Element not found}
// Exampleconst numbers = [10, 20, 30, 40, 50];console.log(linearSearch(numbers, 30)); // 2console.log(linearSearch(numbers, 60)); // -1Binary Search
Binary search only works on sorted arrays. It divides the array in half at each step, discarding one half