-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Added Deque implementation (#13)
- Loading branch information
Showing
3 changed files
with
435 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
# Deque | ||
|
||
A double-ended queue (deque) implementation that efficiently supports insertion and removal of elements at both ends. | ||
|
||
## Usage | ||
|
||
```typescript | ||
import { Deque } from 'jsr:@msk/data-structures'; | ||
|
||
const deque = new Deque<number>(); | ||
``` | ||
|
||
## API Reference | ||
|
||
### Properties | ||
|
||
- `size: number` - Number of elements in the deque | ||
- `isEmpty(): boolean` - Whether the deque is empty | ||
|
||
### Methods | ||
|
||
#### Adding Elements | ||
|
||
```typescript | ||
// Add to front - O(1) | ||
deque.addFirst(1); | ||
|
||
// Add to back - O(1) | ||
deque.addLast(2); | ||
``` | ||
|
||
#### Removing Elements | ||
|
||
```typescript | ||
// Remove from front - O(1) | ||
const first = deque.removeFirst(); | ||
|
||
// Remove from back - O(1) | ||
const last = deque.removeLast(); | ||
``` | ||
|
||
#### Accessing Elements | ||
|
||
```typescript | ||
// Peek at front element - O(1) | ||
const first = deque.peekFirst(); | ||
|
||
// Peek at back element - O(1) | ||
const last = deque.peekLast(); | ||
|
||
// Check if element exists - O(n) | ||
const exists = deque.contains(1); | ||
``` | ||
|
||
#### Iteration | ||
|
||
```typescript | ||
// Forward iteration (front to back) | ||
for (const value of deque) { | ||
console.log(value); | ||
} | ||
|
||
// Reverse iteration (back to front) | ||
for (const value of deque.reverseIterator()) { | ||
console.log(value); | ||
} | ||
``` | ||
|
||
#### Other Operations | ||
|
||
```typescript | ||
// Remove all elements - O(1) | ||
deque.clear(); | ||
|
||
// Convert to array - O(n) | ||
const array = deque.toArray(); | ||
``` | ||
|
||
## Examples | ||
|
||
### Basic Usage with Both Ends | ||
|
||
```typescript | ||
const deque = new Deque<number>(); | ||
|
||
// Add elements at both ends | ||
deque.addFirst(1); // [1] | ||
deque.addLast(2); // [1, 2] | ||
deque.addFirst(0); // [0, 1, 2] | ||
|
||
console.log([...deque]); // [0, 1, 2] | ||
``` | ||
|
||
### Queue Operations (FIFO) | ||
|
||
```typescript | ||
const queue = new Deque<string>(); | ||
|
||
// Enqueue | ||
queue.addLast('first'); | ||
queue.addLast('second'); | ||
|
||
// Dequeue | ||
const first = queue.removeFirst(); // "first" | ||
const second = queue.removeFirst(); // "second" | ||
``` | ||
|
||
### Stack Operations (LIFO) | ||
|
||
```typescript | ||
const stack = new Deque<number>(); | ||
|
||
// Push | ||
stack.addFirst(1); | ||
stack.addFirst(2); | ||
|
||
// Pop | ||
const top = stack.removeFirst(); // 2 | ||
``` | ||
|
||
## Error Handling | ||
|
||
```typescript | ||
try { | ||
const empty = new Deque<number>(); | ||
empty.removeFirst(); // Throws EmptyStructureError | ||
} catch (error) { | ||
if (error instanceof EmptyStructureError) { | ||
console.log('Deque is empty!'); | ||
} | ||
} | ||
``` | ||
|
||
## Performance Advantages | ||
|
||
Key advantages of Deque: | ||
|
||
1. O(1) operations at both ends | ||
2. Can be used as both a queue and a stack | ||
3. Bidirectional iteration support | ||
4. Memory-efficient implementation |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { EmptyStructureError } from '../errors/index.ts'; | ||
import { DoublyLinkedList } from './doubly-linked-list.ts'; | ||
|
||
/** | ||
* A generic double-ended queue (Deque) implementation. | ||
* | ||
* This class provides a deque data structure that allows insertion and removal | ||
* of elements from both ends with O(1) time complexity. It internally uses a | ||
* DoublyLinkedList for efficient operations at both ends. | ||
* | ||
* Features: | ||
* - O(1) insertions at both ends | ||
* - O(1) removals from both ends | ||
* - FIFO (First-In-First-Out) when used from one end | ||
* - LIFO (Last-In-First-Out) when used from one end | ||
* - Bidirectional operations | ||
* - Implements Iterable interface for use in for...of loops | ||
* - Type-safe implementation using generics | ||
* | ||
* @template T The type of elements stored in the deque | ||
* | ||
* @example | ||
* ```typescript | ||
* const deque = new Deque<number>(); | ||
* deque.addFirst(1); | ||
* deque.addLast(2); | ||
* deque.addFirst(0); | ||
* console.log(deque.toArray()); // [0, 1, 2] | ||
* console.log(deque.removeFirst()); // 0 | ||
* console.log(deque.removeLast()); // 2 | ||
* ``` | ||
*/ | ||
export class Deque<T> implements Iterable<T> { | ||
private list: DoublyLinkedList<T>; | ||
|
||
/** | ||
* Creates an empty deque | ||
*/ | ||
constructor() { | ||
this.list = new DoublyLinkedList<T>(); | ||
} | ||
|
||
/** | ||
* Returns the number of elements in the deque | ||
*/ | ||
get size(): number { | ||
return this.list.size; | ||
} | ||
|
||
/** | ||
* Checks if the deque is empty | ||
* @returns {boolean} true if the deque contains no elements | ||
*/ | ||
isEmpty(): boolean { | ||
return this.list.isEmpty(); | ||
} | ||
|
||
/** | ||
* Adds an element to the front of the deque | ||
* @param value The value to add | ||
*/ | ||
addFirst(value: T): void { | ||
this.list.prepend(value); | ||
} | ||
|
||
/** | ||
* Adds an element to the end of the deque | ||
* @param value The value to add | ||
*/ | ||
addLast(value: T): void { | ||
this.list.append(value); | ||
} | ||
|
||
/** | ||
* Removes and returns the first element | ||
* @throws {EmptyStructureError} If the deque is empty | ||
* @returns The first element in the deque | ||
*/ | ||
removeFirst(): T { | ||
if (this.isEmpty()) { | ||
throw new EmptyStructureError(); | ||
} | ||
return this.list.removeFirst(); | ||
} | ||
|
||
/** | ||
* Removes and returns the last element | ||
* @throws {EmptyStructureError} If the deque is empty | ||
* @returns The last element in the deque | ||
*/ | ||
removeLast(): T { | ||
if (this.isEmpty()) { | ||
throw new EmptyStructureError(); | ||
} | ||
return this.list.removeLast(); | ||
} | ||
|
||
/** | ||
* Returns the first element without removing it | ||
* @throws {EmptyStructureError} If the deque is empty | ||
* @returns The first element in the deque | ||
*/ | ||
peekFirst(): T { | ||
if (this.isEmpty()) { | ||
throw new EmptyStructureError(); | ||
} | ||
return this.list.get(0); | ||
} | ||
|
||
/** | ||
* Returns the last element without removing it | ||
* @throws {EmptyStructureError} If the deque is empty | ||
* @returns The last element in the deque | ||
*/ | ||
peekLast(): T { | ||
if (this.isEmpty()) { | ||
throw new EmptyStructureError(); | ||
} | ||
return this.list.get(this.size - 1); | ||
} | ||
|
||
/** | ||
* Checks if the deque contains the specified element | ||
* @param value The value to check for | ||
* @returns {boolean} true if the element exists in the deque | ||
*/ | ||
contains(value: T): boolean { | ||
return this.list.contains(value); | ||
} | ||
|
||
/** | ||
* Removes all elements from the deque | ||
*/ | ||
clear(): void { | ||
this.list.clear(); | ||
} | ||
|
||
/** | ||
* Converts the deque to an array | ||
* @returns An array containing all elements in the deque (front to back) | ||
*/ | ||
toArray(): T[] { | ||
return this.list.toArray(); | ||
} | ||
|
||
/** | ||
* Creates a forward iterator for the deque (front to back) | ||
*/ | ||
[Symbol.iterator](): Iterator<T> { | ||
return this.list[Symbol.iterator](); | ||
} | ||
|
||
/** | ||
* Creates a reverse iterator for the deque (back to front) | ||
* @returns An iterator that traverses the deque from back to front | ||
*/ | ||
reverseIterator(): IterableIterator<T> { | ||
return this.list.reverseIterator(); | ||
} | ||
} |
Oops, something went wrong.