diff --git a/.github/workflows/cpp-tests.yml b/.github/workflows/cpp-tests.yml deleted file mode 100644 index e30208c..0000000 --- a/.github/workflows/cpp-tests.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: C++ Tests - -on: - # push: - # branches: - # - main - pull_request: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Install Dependencies - run: sudo apt-get install -y g++ - - - name: Run Tests - run: | - cd cpp/LinkedList/singly - g++ -std=c++14 list_test.cc -o list_test - ./list_test diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml deleted file mode 100644 index be6f70b..0000000 --- a/.github/workflows/go-tests.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Go Tests - -on: - # push: - # branches: - # - main - pull_request: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: '^1.16' - - - name: Set up dependencies - run: | - cd go/LinkedList/singly - go mod tidy - - - name: Run tests - run: | - cd go/LinkedList/singly - go test -v . diff --git a/.github/workflows/nodejs-tests.yml b/.github/workflows/nodejs-tests.yml deleted file mode 100644 index e355216..0000000 --- a/.github/workflows/nodejs-tests.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Node.js Tests - -on: - # push: - # branches: - # - main - pull_request: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [16.x, 18.x] - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - - - name: Install dependencies - run: | - cd js/LinkedList/singly - npm install - - - name: Run tests - run: | - cd js/LinkedList/singly - npm test diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml deleted file mode 100644 index 5fbcc6c..0000000 --- a/.github/workflows/python-tests.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Python Tests - -on: - # push: - # branches: - # - main - pull_request: - branches: - - main - -jobs: - test: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.12.3' - - - name: Run tests - run: python -m unittest discover -s python/LinkedList/singly -p 'list_test.py' diff --git a/.gitignore b/.gitignore index 600d2d3..0ad56a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.vscode \ No newline at end of file +.vscode +.venv/ diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..05b309a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +harris.ahmad55@gmail.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index bbf7f48..bcdb026 100644 --- a/README.md +++ b/README.md @@ -16,21 +16,6 @@ Welcome to the ultimate repository for data structures implemented across multip This repository aims to provide a comprehensive collection of data structures implemented in C++, Go, JavaScript, and Python. Each implementation will include practical applications to demonstrate their usage in real-world scenarios. -## Features - -- **C++ Implementations**: - - ➖ Singly Linked List - - 📄 Code file (`list.h`) - -- **Go Implementations**: - - ➖ Singly Linked List (`LinkedList.go`) - -- **JavaScript Implementations**: - - ➖ Singly Linked List (`list.js`) - -- **Python Implementations**: - - ➖ Singly Linked List (`list.py`) - ## Getting Started To get started with any of the data structures, navigate to the respective directory and follow the instructions provided in the comments or documentation within the code. diff --git a/c/.gitignore b/c/.gitignore new file mode 100644 index 0000000..6d5206b --- /dev/null +++ b/c/.gitignore @@ -0,0 +1,52 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf \ No newline at end of file diff --git a/c/LinkedList/doubly/list.c b/c/LinkedList/doubly/list.c new file mode 100644 index 0000000..b531f79 --- /dev/null +++ b/c/LinkedList/doubly/list.c @@ -0,0 +1,259 @@ +#include +#include + +typedef struct Node { + void *data; + struct Node *next; + struct Node *prev; + int ref_count; +} Node; + +typedef struct DoublyLinkedList { + Node *head; + Node *tail; +} DoublyLinkedList; + +void incrementRefCount(Node *node) { + if (node) { + node->ref_count++; + } +} + +void decrementRefCount(Node *node) { + if (node) { + node->ref_count--; + if (node->ref_count <= 0) { + free(node); + } + } +} + +Node *createNode(void *data) { + Node *newNode = (Node *)malloc(sizeof(Node)); + newNode->data = data; + newNode->next = NULL; + newNode->prev = NULL; + newNode->ref_count = 1; + return newNode; +} + +DoublyLinkedList *createDoublyLinkedList() { + DoublyLinkedList *list = (DoublyLinkedList *)malloc(sizeof(DoublyLinkedList)); + list->head = NULL; + list->tail = NULL; + return list; +} + +Node *getHead(DoublyLinkedList *list) { + return list->head; +} + +Node *getTail(DoublyLinkedList *list) { + return list->tail; +} + +void prepend(DoublyLinkedList *list, void *data) { + Node *newNode = createNode(data); + if (!list->head) { + list->head = newNode; + list->tail = newNode; + } else { + newNode->next = list->head; + list->head->prev = newNode; + list->head = newNode; + } +} + +void append(DoublyLinkedList *list, void *data) { + Node *newNode = createNode(data); + if (!list->head) { + list->head = newNode; + list->tail = newNode; + } else { + newNode->prev = list->tail; + list->tail->next = newNode; + list->tail = newNode; + } +} + +void insertBetween(DoublyLinkedList *list, void *data, void *after) { + Node *newNode = createNode(data); + Node *curr = list->head; + + while (curr && curr->data != after) { + curr = curr->next; + } + + if (curr) { + newNode->next = curr->next; + newNode->prev = curr; + if (curr->next) { + curr->next->prev = newNode; + } else { + list->tail = newNode; + } + curr->next = newNode; + } +} + +void deleteHead(DoublyLinkedList *list) { + if (list->head) { + Node *temp = list->head; + list->head = list->head->next; + if (list->head) { + list->head->prev = NULL; + } else { + list->tail = NULL; + } + decrementRefCount(temp); + } +} + +void deleteTail(DoublyLinkedList *list) { + if (!list->tail) { + return; + } + if (list->tail == list->head) { + decrementRefCount(list->tail); + list->head = NULL; + list->tail = NULL; + } else { + Node *temp = list->tail; + list->tail = list->tail->prev; + list->tail->next = NULL; + decrementRefCount(temp); + } +} + +void deleteAny(DoublyLinkedList *list, void *target) { + Node *curr = list->head; + + while (curr && curr->data != target) { + curr = curr->next; + } + + if (curr) { + if (curr->prev) { + curr->prev->next = curr->next; + } else { + list->head = curr->next; + } + + if (curr->next) { + curr->next->prev = curr->prev; + } else { + list->tail = curr->prev; + } + + decrementRefCount(curr); + } +} + +int size(DoublyLinkedList *list) { + int count = 0; + Node *curr = list->head; + while (curr) { + count++; + curr = curr->next; + } + return count; +} + +void reverseList(DoublyLinkedList *list) { + Node *curr = list->head; + Node *temp = NULL; + + while (curr) { + temp = curr->prev; + curr->prev = curr->next; + curr->next = temp; + curr = curr->prev; + } + + if (temp) { + list->head = temp->prev; + } +} + +Node *find(DoublyLinkedList *list, void *data) { + Node *curr = list->head; + while (curr) { + if (curr->data == data) { + return curr; + } + curr = curr->next; + } + return NULL; +} + +void clear(DoublyLinkedList *list) { + Node *curr = list->head; + Node *next; + while (curr) { + next = curr->next; + decrementRefCount(curr); + curr = next; + } + list->head = NULL; + list->tail = NULL; +} + +void printListForward(DoublyLinkedList *list) { + Node *curr = list->head; + while (curr) { + printf("%p ", curr->data); + curr = curr->next; + } + printf("\n"); +} + +void printListBackward(DoublyLinkedList *list) { + Node *curr = list->tail; + while (curr) { + printf("%p ", curr->data); + curr = curr->prev; + } + printf("\n"); +} + +int main() { + DoublyLinkedList *list = createDoublyLinkedList(); + + int a = 1, b = 2, c = 3, d = 4; + append(list, &a); + append(list, &b); + prepend(list, &c); + insertBetween(list, &d, &a); + + printf("List printed forward: "); + printListForward(list); + + printf("List printed backward: "); + printListBackward(list); + + deleteHead(list); + printf("List after deleting head: "); + printListForward(list); + + deleteTail(list); + printf("List after deleting tail: "); + printListForward(list); + + deleteAny(list, &b); + printf("List after deleting element b: "); + printListForward(list); + + printf("List size: %d\n", size(list)); + + reverseList(list); + printf("List after reversing: "); + printListForward(list); + + clear(list); + printf("List after clearing: "); + printListForward(list); + + free(list); + + return 0; +} diff --git a/go/LinkedList/doubly/LinkedList.go b/go/LinkedList/doubly/LinkedList.go new file mode 100644 index 0000000..a2b4634 --- /dev/null +++ b/go/LinkedList/doubly/LinkedList.go @@ -0,0 +1,163 @@ +package doubly + +import "fmt" + +type Node struct { + data interface{} + next *Node + prev *Node +} + +type DoublyLinkedList struct { + head *Node + tail *Node + length int +} + +func NewNode(data interface{}) *Node { + return &Node{data: data} +} + +func NewDoublyLinkedList() *DoublyLinkedList { + return &DoublyLinkedList{ + head: nil, + tail: nil, + length: 0, + } +} + +func (dll *DoublyLinkedList) Prepend(data interface{}) { + newNode := NewNode(data) + if dll.head == nil { + dll.head = newNode + dll.tail = newNode + } else { + newNode.next = dll.head + dll.head.prev = newNode + dll.head = newNode + } +} + +func (dll *DoublyLinkedList) Append(data interface{}) { + newNode := NewNode(data) + if dll.head == nil { + dll.head = newNode + dll.tail = newNode + } else { + newNode.prev = dll.tail + dll.tail.next = newNode + dll.tail = newNode + } +} + +func (dll *DoublyLinkedList) InsertBetween(data interface{}, after interface{}) { + current := dll.head + for current != nil && current.data != after { + current = current.next + } + if current != nil { + newNode := NewNode(data) + newNode.next = current.next + newNode.prev = current + if current.next != nil { + current.prev.next = newNode + } + current.next = newNode + if newNode.next == nil { + dll.tail = newNode + } + } +} + +func (dll *DoublyLinkedList) DeleteHead() { + if dll.head != nil { + if dll.head.next != nil { + dll.head = dll.head.next + dll.head.prev = nil + } else { + dll.head = nil + dll.tail = nil + } + } +} + +func (dll *DoublyLinkedList) DeleteTail() { + if dll.tail != nil { + if dll.tail.prev != nil { + dll.tail = dll.tail.prev + dll.tail.next = nil + } else { + dll.head = nil + dll.tail = nil + } + } +} + +func (dll *DoublyLinkedList) DeleteAny(data interface{}) { + current := dll.head + for current != nil && current.data != data { + current = current.next + } + if current != nil { + if current.prev != nil { + current.prev.next = current.next + } else { + dll.head = current.next + } + if current.next != nil { + current.next.prev = current.prev + } else { + dll.tail = current.prev + } + } +} + +func (dll *DoublyLinkedList) Size() int { + count := 0 + current := dll.head + for current != nil { + count++ + current = current.next + } + return count +} + +func (dll *DoublyLinkedList) Reverse() { + current := dll.head + var prev *Node + dll.tail = current + for current != nil { + prev = current.prev + current.prev = current.next + current.next = prev + if current.prev == nil { + dll.head = current + } + current = current.prev + } +} + +func (dll *DoublyLinkedList) Find(data interface{}) *Node { + current := dll.head + for current != nil { + if current.data == data { + return current + } + current = current.next + } + return nil +} + +func (dll *DoublyLinkedList) Clear() { + dll.head = nil + dll.tail = nil +} + +func (dll *DoublyLinkedList) PrintList() { + current := dll.head + for current != nil { + fmt.Print(current.data, " ") + current = current.next + } + fmt.Println() +} diff --git a/go/LinkedList/go.mod b/go/LinkedList/go.mod new file mode 100644 index 0000000..246f412 --- /dev/null +++ b/go/LinkedList/go.mod @@ -0,0 +1,3 @@ +module github.com/harris-ahmad/DataStructuresAndAlgorithms/go/LinkedList + +go 1.23.1 \ No newline at end of file diff --git a/go/LinkedList/main.go b/go/LinkedList/main.go new file mode 100644 index 0000000..cde4938 --- /dev/null +++ b/go/LinkedList/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "fmt" + DoublyLinkedList "github.com/harris-ahmad/DataStructuresAndAlgorithms/go/LinkedList/doubly" +) + +func main() { + dll := DoublyLinkedList{} + + // Test appending and prepending + fmt.Println("Testing append and prepend:") + dll.Append(1) + dll.Append(2) + dll.Append(3) + dll.Prepend(0) + dll.PrintList() // Expected Output: 0 1 2 3 + + // Test inserting between nodes + fmt.Println("Testing insert between:") + dll.InsertBetween(1.5, 1) + dll.PrintList() // Expected Output: 0 1 1.5 2 3 + + // Test deleting head + fmt.Println("Testing delete head:") + dll.DeleteHead() + dll.PrintList() // Expected Output: 1 1.5 2 3 + + // Test deleting tail + fmt.Println("Testing delete tail:") + dll.DeleteTail() + dll.PrintList() // Expected Output: 1 1.5 2 + + // Test deleting any node + fmt.Println("Testing delete any:") + dll.DeleteAny(1.5) + dll.PrintList() // Expected Output: 1 2 + + // Test reversing the list + fmt.Println("Testing reverse list:") + dll.Reverse() + dll.PrintList() // Expected Output: 2 1 + + dll.Clear() + dll.Append(1) + + // Test finding a node + fmt.Println("Testing find:") + node := dll.Find(1) + if node != nil { + fmt.Println(node.data) // Expected Output: 1 + } else { + fmt.Println("Node not found") + } + + // Test clearing the list + fmt.Println("Testing clear:") + dll.Clear() + dll.PrintList() // Expected Output: (empty list) + + // Test reading a list + fmt.Println("Testing read list:") + dll.Append(10) + dll.Append(20) + dll.Append(30) + dll.PrintList() // Expected Output: 10 20 30 + + // Test size of the list + fmt.Println("Testing size:") + fmt.Println(dll.Size()) // Expected Output: 3 +} diff --git a/js/LinkedList/doubly/list.js b/js/LinkedList/doubly/list.js new file mode 100644 index 0000000..48ec394 --- /dev/null +++ b/js/LinkedList/doubly/list.js @@ -0,0 +1,112 @@ +class Node { + constructor(data) { + this.data = data; + this.next = null; + this.prev = null; + } +} + +class LinkedList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + this.nodeMap = new Map(); + } + + getHead() { + if (!this.head) throw Error("List is empty!"); + return this.head; + } + + getTail() { + if (!this.head) throw Error("List is empty!"); + return this.tail; + } + + prepend(data) { + const newNode = new Node(data); + if (!this.head) { + this.head = newNode; + this.tail = newNode; + } else { + this.head.prev = newNode; + newNode.next = this.head; + this.head = newNode; + } + this.nodeMap.set(data.id, newNode) + this.length++; + } + + append(data) { + const newNode = new Node(data); + if (!this.head) { + this.head = newNode; + this.tail = newNode; + } else { + this.tail.next = newNode; + newNode.prev = this.tail; + this.tail = newNode; + } + this.nodeMap.set(data.id, newNode); + this.length++; + } + + insertBetween(data, before, after) { + const newNode = new Node(data); + let curr = this.head; + + // finding the node with the `after` data + while (curr && curr.data.id !== after.id) { + curr = curr.next; + } + + if (curr) { + // finding the node with the `before` data + let nextNode = curr.next; + while (nextNode && nextNode.data.id !== before.id) { + curr = nextNode; + nextNode = nextNode.next; + } + + // if the `before` node is found, insert the new node + if (nextNode && nextNode.data.id === before.id) { + newNode.next = nextNode; + newNode.prev = curr; + curr.next = newNode; + nextNode.prev = newNode; + this.nodeMap.set(data.id, newNode); + this.length++; + } else { + throw Error("Before node not found"); + } + } else { + throw Error("After node not found"); + } + } + + deleteHead() { + if (!this.head) { + throw Error("List is empty!"); + } else { + this.nodeMap.delete(this.head.data.id); + this.head = this.head.next; + if (this.head) { + this.head.prev = null; + } else { + this.tail = null; + } + this.length--; + } + } + + print() { + let curr = this.head; + let result = [] + while (curr) { + result.push(curr.data.toString()); + curr = curr.next; + } + console.log(result.join(" <-> ")); + } +} \ No newline at end of file diff --git a/js/LinkedList/singly/list.mjs b/js/LinkedList/singly/list.js similarity index 83% rename from js/LinkedList/singly/list.mjs rename to js/LinkedList/singly/list.js index 7ff2ff8..0a71e72 100644 --- a/js/LinkedList/singly/list.mjs +++ b/js/LinkedList/singly/list.js @@ -1,5 +1,5 @@ /** - * Node class representing a single node in a singly linked list. + * Node class representing a single node in a doubly linked list. */ class Node { /** @@ -9,15 +9,16 @@ class Node { constructor(data) { this.data = data; this.next = null; + this.prev = null; // Added for doubly linked list } } /** -* LinkedList class representing a singly linked list. +* DoublyLinkedList class representing a doubly linked list. */ -class LinkedList { +class DoublyLinkedList { /** - * Create an empty LinkedList. + * Create an empty DoublyLinkedList. */ constructor() { this.head = null; @@ -53,6 +54,7 @@ class LinkedList { this.tail = newNode; } else { newNode.next = this.head; + this.head.prev = newNode; this.head = newNode; } this.nodeMap.set(data.id, newNode); @@ -70,6 +72,7 @@ class LinkedList { this.tail = newNode; } else { this.tail.next = newNode; + newNode.prev = this.tail; this.tail = newNode; } this.nodeMap.set(data.id, newNode); @@ -108,7 +111,9 @@ class LinkedList { if (nextNode && nextNode.data.id === before.id) { console.log('Found node before:', nextNode.data); newNode.next = nextNode; + newNode.prev = curr; curr.next = newNode; + nextNode.prev = newNode; this.nodeMap.set(data.id, newNode); this.length++; } else { @@ -125,8 +130,11 @@ class LinkedList { deleteHead() { if (this.head) { this.nodeMap.delete(this.head.data.id); - this.head = this.head.next; - if (!this.head) { + if (this.head.next) { + this.head = this.head.next; + this.head.prev = null; + } else { + this.head = null; this.tail = null; } this.length--; @@ -145,13 +153,9 @@ class LinkedList { this.head = null; this.tail = null; } else { - let prevNode = this.head; - while (prevNode.next && prevNode.next.next) { - prevNode = prevNode.next; - } - this.nodeMap.delete(prevNode.next.data.id); - prevNode.next = null; - this.tail = prevNode; + this.nodeMap.delete(this.tail.data.id); + this.tail = this.tail.prev; + this.tail.next = null; } this.length--; } @@ -166,26 +170,19 @@ class LinkedList { } if (this.head.data.id === target.id) { - this.nodeMap.delete(this.head.data.id); - this.head = this.head.next; - if (!this.head) { - this.tail = null; - } - this.length--; + this.deleteHead(); + } else if (this.tail.data.id === target.id) { + this.deleteTail(); } else { - let prevNode = this.head; - let curr = this.head.next; + let curr = this.head; while (curr && curr.data.id !== target.id) { - prevNode = curr; curr = curr.next; } if (curr) { - prevNode.next = curr.next; - if (!curr.next) { - this.tail = prevNode; - } + curr.prev.next = curr.next; + curr.next.prev = curr.prev; this.nodeMap.delete(curr.data.id); this.length--; } @@ -204,18 +201,19 @@ class LinkedList { * Reverse the linked list in place. */ reverseList() { - let prev = null; let curr = this.head; - let next = null; + let prev = null; this.tail = this.head; while (curr) { - next = curr.next; + const next = curr.next; curr.next = prev; + curr.prev = next; prev = curr; curr = next; } + this.head = prev; } @@ -248,8 +246,8 @@ class LinkedList { result.push(curr.data.toString()); curr = curr.next; } - console.log(result.join(" -> ")); + console.log(result.join(" <-> ")); } } -export default LinkedList; \ No newline at end of file +export default DoublyLinkedList; diff --git a/js/LinkedList/singly/list_test.mjs b/js/LinkedList/singly/list_test.mjs deleted file mode 100644 index 9bb6b8b..0000000 --- a/js/LinkedList/singly/list_test.mjs +++ /dev/null @@ -1,131 +0,0 @@ -import { expect } from 'chai'; -import LinkedList from "./list.mjs"; - -describe('LinkedList', () => { - let list; - - beforeEach(() => { - list = new LinkedList(); - }); - - describe('getHead', () => { - it('should return null for an empty list', () => { - expect(list.getHead()).to.be.null; - }); - - it('should return the head node of the list', () => { - list.prepend({ id: 1, value: 'first' }); - expect(list.getHead().data).to.deep.equal({ id: 1, value: 'first' }); - }); - }); - - describe('getTail', () => { - it('should return null for an empty list', () => { - expect(list.getTail()).to.be.null; - }); - - it('should return the tail node of the list', () => { - list.append({ id: 1, value: 'first' }); - expect(list.getTail().data).to.deep.equal({ id: 1, value: 'first' }); - }); - }); - - describe('prepend', () => { - it('should add a node to the beginning of the list', () => { - list.prepend({ id: 1, value: 'first' }); - expect(list.getHead().data).to.deep.equal({ id: 1, value: 'first' }); - expect(list.size()).to.equal(1); - }); - - it('should correctly update the head and tail for the first node', () => { - list.prepend({ id: 1, value: 'first' }); - expect(list.getHead().data).to.deep.equal({ id: 1, value: 'first' }); - expect(list.getTail().data).to.deep.equal({ id: 1, value: 'first' }); - }); - }); - - describe('append', () => { - it('should add a node to the end of the list', () => { - list.append({ id: 1, value: 'first' }); - expect(list.getTail().data).to.deep.equal({ id: 1, value: 'first' }); - expect(list.size()).to.equal(1); - }); - - it('should correctly update the tail when appending nodes', () => { - list.append({ id: 1, value: 'first' }); - list.append({ id: 2, value: 'second' }); - expect(list.getTail().data).to.deep.equal({ id: 2, value: 'second' }); - }); - }); - - describe('insertBetween', () => { - it('should insert a node between two nodes', () => { - list.append({ id: 1, value: 'first' }); - list.append({ id: 3, value: 'third' }); - list.insertBetween({ id: 2, value: 'second' }, { id: 3, value: 'third' }, { id: 1, value: 'first' }); - expect(list.find(2).data).to.deep.equal({ id: 2, value: 'second' }); - }); - }); - - describe('deleteHead', () => { - it('should delete the head node of the list', () => { - list.append({ id: 1, value: 'first' }); - list.deleteHead(); - expect(list.getHead()).to.be.null; - }); - }); - - describe('deleteTail', () => { - it('should delete the tail node of the list', () => { - list.append({ id: 1, value: 'first' }); - list.deleteTail(); - expect(list.getTail()).to.be.null; - }); - }); - - describe('deleteAny', () => { - it('should delete any node matching the target data', () => { - list.append({ id: 1, value: 'first' }); - list.append({ id: 2, value: 'second' }); - list.deleteAny({ id: 1 }); - expect(list.find(1)).to.be.null; - }); - }); - - describe('size', () => { - it('should return the correct size of the list', () => { - list.append({ id: 1, value: 'first' }); - list.append({ id: 2, value: 'second' }); - expect(list.size()).to.equal(2); - }); - }); - - describe('reverseList', () => { - it('should reverse the linked list', () => { - list.append({ id: 1, value: 'first' }); - list.append({ id: 2, value: 'second' }); - list.reverseList(); - expect(list.getHead().data).to.deep.equal({ id: 2, value: 'second' }); - }); - }); - - describe('find', () => { - it('should find a node by its id', () => { - list.append({ id: 1, value: 'first' }); - expect(list.find(1).data).to.deep.equal({ id: 1, value: 'first' }); - }); - - it('should return null if the node is not found', () => { - expect(list.find(999)).to.be.null; - }); - }); - - describe('clear', () => { - it('should clear the list and remove all nodes', () => { - list.append({ id: 1, value: 'first' }); - list.clear(); - expect(list.getHead()).to.be.null; - expect(list.size()).to.equal(0); - }); - }); -}); diff --git a/js/LinkedList/singly/package-lock.json b/js/LinkedList/singly/package-lock.json deleted file mode 100644 index ab74f1f..0000000 --- a/js/LinkedList/singly/package-lock.json +++ /dev/null @@ -1,850 +0,0 @@ -{ - "name": "singly", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "chai": "^5.1.1", - "mocha": "^10.6.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/chai": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", - "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "engines": { - "node": ">= 16" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "engines": { - "node": "*" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "bin": { - "he": "bin/he" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/loupe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", - "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.6.0.tgz", - "integrity": "sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/js/LinkedList/singly/package.json b/js/LinkedList/singly/package.json deleted file mode 100644 index 0b45b18..0000000 --- a/js/LinkedList/singly/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "scripts": { - "test": "mocha list_test.mjs" - }, - "dependencies": { - "chai": "^5.1.1", - "mocha": "^10.6.0" - } -} diff --git a/python/LinkedList/singly/list.py b/python/LinkedList/singly/list.py deleted file mode 100644 index f5c20a7..0000000 --- a/python/LinkedList/singly/list.py +++ /dev/null @@ -1,211 +0,0 @@ -class Node: - """ - Node class representing a single node in a singly linked list. - """ - - def __init__(self, data): - """ - Create a Node. - - :param data: The data to be stored in the node. - """ - self.data = data - self.next = None - - -class LinkedList: - """ - LinkedList class representing a singly linked list. - """ - - def __init__(self): - """ - Create an empty LinkedList. - """ - self.head = None - self.tail = None - self.length = 0 - self.node_map = {} # Dictionary for O(1) lookups - - def get_head(self): - """ - Get the head node of the list. - - :return: The head node or None if the list is empty. - """ - return self.head - - def get_tail(self): - """ - Get the tail node of the list. - - :return: The tail node or None if the list is empty. - """ - return self.tail - - def prepend(self, data): - """ - Prepend a node to the beginning of the list. - - :param data: The data to be stored in the new node. - """ - new_node = Node(data) - if not self.head: - self.head = new_node - self.tail = new_node - else: - new_node.next = self.head - self.head = new_node - self.node_map[data.id] = new_node - self.length += 1 - - def append(self, data): - """ - Append a node to the end of the list. - - :param data: The data to be stored in the new node. - """ - new_node = Node(data) - if not self.head: - self.head = new_node - self.tail = new_node - else: - self.tail.next = new_node - self.tail = new_node - self.node_map[data.id] = new_node - self.length += 1 - - def insert_between(self, data, before, after): - """ - Insert a node between two existing nodes in the list. - - :param data: The data to be stored in the new node. - :param before: The data of the node before which to - insert the new node. - :param after: The data of the node after which to insert the new node. - """ - new_node = Node(data) - curr = self.head - - while curr and curr.data != after: - curr = curr.next - - if curr and curr.next and curr.next.data == before: - new_node.next = curr.next - curr.next = new_node - self.node_map[data.id] = new_node - self.length += 1 - - def delete_head(self): - """ - Delete the head node of the list. - """ - if self.head: - self.node_map.pop(self.head.data.id, None) - self.head = self.head.next - if not self.head: - self.tail = None - self.length -= 1 - - def delete_tail(self): - """ - Delete the tail node of the list. - """ - if not self.head: - return - if not self.head.next: - self.node_map.pop(self.head.data.id, None) - self.head = None - self.tail = None - else: - prev_node = self.head - while prev_node.next and prev_node.next.next: - prev_node = prev_node.next - self.node_map.pop(prev_node.next.data.id, None) - prev_node.next = None - self.tail = prev_node - self.length -= 1 - - def delete_any(self, target): - """ - Delete any node from the list that matches the given target data. - - :param target: The target data to match for deletion. - """ - if not self.head: - return - - if self.head.data.id == target.id: - self.node_map.pop(self.head.data.id, None) - self.head = self.head.next - if not self.head: - self.tail = None - self.length -= 1 - else: - prev_node = self.head - curr = self.head.next - - while curr and curr.data.id != target.id: - prev_node = curr - curr = curr.next - - if curr: - prev_node.next = curr.next - if not curr.next: - self.tail = prev_node - self.node_map.pop(curr.data.id, None) - self.length -= 1 - - def size(self): - """ - Get the size of the list. - - :return: The number of nodes in the list. - """ - return self.length - - def reverse_list(self): - """ - Reverse the linked list in place. - """ - prev = None - curr = self.head - next_node = None - - self.tail = self.head - - while curr: - next_node = curr.next - curr.next = prev - prev = curr - curr = next_node - self.head = prev - - def find(self, id): - """ - Find a node in the list by its id. - - :param id: The id of the node to find. - :return: The node with the given id or None if not found. - """ - return self.node_map.get(id, None) - - def clear(self): - """ - Clear the list. - """ - self.head = None - self.tail = None - self.length = 0 - self.node_map = {} - - def print_list(self): - """ - Print the list data in a readable format. - """ - curr = self.head - result = [] - while curr: - result.append(str(curr.data)) - curr = curr.next - print(" -> ".join(result)) diff --git a/python/LinkedList/singly/list_test.py b/python/LinkedList/singly/list_test.py deleted file mode 100644 index be6821f..0000000 --- a/python/LinkedList/singly/list_test.py +++ /dev/null @@ -1,126 +0,0 @@ -import unittest -from list import LinkedList - - -class TestLinkedList(unittest.TestCase): - - class TestData: - def __init__(self, id, value): - self.id = id - self.value = value - - def __str__(self): - return f"({self.id}: {self.value})" - - def setUp(self): - self.linked_list = LinkedList() - - def test_prepend(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.prepend(data1) - self.linked_list.prepend(data2) - self.assertEqual(self.linked_list.get_head().data, data2) - self.assertEqual(self.linked_list.get_tail().data, data1) - self.assertEqual(self.linked_list.size(), 2) - - def test_append(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.assertEqual(self.linked_list.get_head().data, data1) - self.assertEqual(self.linked_list.get_tail().data, data2) - self.assertEqual(self.linked_list.size(), 2) - - def test_insert_between(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - data3 = self.TestData(3, "data3") - self.linked_list.append(data1) - self.linked_list.append(data3) - self.linked_list.insert_between(data2, before=data3, after=data1) - self.assertEqual(self.linked_list.size(), 3) - self.assertEqual(self.linked_list.get_head().next.data, data2) - self.assertEqual(self.linked_list.get_head().next.next.data, data3) - - def test_delete_head(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.linked_list.delete_head() - self.assertEqual(self.linked_list.get_head().data, data2) - self.assertEqual(self.linked_list.size(), 1) - self.linked_list.delete_head() - self.assertIsNone(self.linked_list.get_head()) - self.assertIsNone(self.linked_list.get_tail()) - self.assertEqual(self.linked_list.size(), 0) - - def test_delete_tail(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.linked_list.delete_tail() - self.assertEqual(self.linked_list.get_tail().data, data1) - self.assertEqual(self.linked_list.size(), 1) - self.linked_list.delete_tail() - self.assertIsNone(self.linked_list.get_tail()) - self.assertIsNone(self.linked_list.get_head()) - self.assertEqual(self.linked_list.size(), 0) - - def test_delete_any(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - data3 = self.TestData(3, "data3") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.linked_list.append(data3) - self.linked_list.delete_any(data2) - self.assertEqual(self.linked_list.size(), 2) - self.assertEqual(self.linked_list.get_head().next.data, data3) - self.linked_list.delete_any(data1) - self.assertEqual(self.linked_list.size(), 1) - self.assertEqual(self.linked_list.get_head().data, data3) - self.linked_list.delete_any(data3) - self.assertEqual(self.linked_list.size(), 0) - self.assertIsNone(self.linked_list.get_head()) - self.assertIsNone(self.linked_list.get_tail()) - - def test_reverse_list(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - data3 = self.TestData(3, "data3") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.linked_list.append(data3) - self.linked_list.reverse_list() - self.assertEqual(self.linked_list.get_head().data, data3) - self.assertEqual(self.linked_list.get_head().next.data, data2) - self.assertEqual(self.linked_list.get_head().next.next.data, data1) - self.assertEqual(self.linked_list.get_tail().data, data1) - - def test_find(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.assertEqual(self.linked_list.find(1), self.linked_list.get_head()) - self.assertEqual(self.linked_list.find(2), self.linked_list.get_tail()) - self.assertIsNone(self.linked_list.find(3)) - - def test_clear(self): - data1 = self.TestData(1, "data1") - data2 = self.TestData(2, "data2") - self.linked_list.append(data1) - self.linked_list.append(data2) - self.linked_list.clear() - self.assertIsNone(self.linked_list.get_head()) - self.assertIsNone(self.linked_list.get_tail()) - self.assertEqual(self.linked_list.size(), 0) - self.assertEqual(self.linked_list.node_map, {}) - - -if __name__ == "__main__": - unittest.main() diff --git a/python/cleanup.sh b/python/cleanup.sh new file mode 100755 index 0000000..9399f1a --- /dev/null +++ b/python/cleanup.sh @@ -0,0 +1,4 @@ +# !/bin/bash + +find . -type d -name "__pycache__" -exec rm -r {} + +find . -type d -name ".pytest_cache" -exec rm -r {} + \ No newline at end of file diff --git a/python/src/__init__.py b/python/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/src/linkedlist/__init__.py b/python/src/linkedlist/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/LinkedList/doubly/list.py b/python/src/linkedlist/doubly/list.py similarity index 100% rename from python/LinkedList/doubly/list.py rename to python/src/linkedlist/doubly/list.py diff --git a/python/src/linkedlist/singly/__init__.py b/python/src/linkedlist/singly/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/src/linkedlist/singly/list.py b/python/src/linkedlist/singly/list.py new file mode 100644 index 0000000..59accef --- /dev/null +++ b/python/src/linkedlist/singly/list.py @@ -0,0 +1,206 @@ +import os +from typing import Dict, Optional, Any + +from utils.logger import Logger + +class Node: + def __init__(self, data: Any): + self.data = data + self.next: Optional["Node"] = None + self._id = os.urandom(16).hex() + + def __repr__(self) -> str: + return f"Node({self._id}: {self.data})" + +class LinkedList: + def __init__(self): + self.head: Optional[Node] = None + self.tail: Optional[Node] = None + self.length: int = 0 + # dictionary to store nodes by their IDs for faster lookup and deletion + self.node_map: Dict[str, Node] = {} + + def get_head(self) -> Optional[Node]: + """Get the head node of the list.""" + return self.head + + def get_tail(self) -> Optional[Node]: + """Get the tail node of the list.""" + return self.tail + + def prepend(self, data: Any) -> None: + """Prepend a node to the start of the list.""" + + new_node = Node(data) + new_node.next = self.head + self.head = new_node + + if not self.tail: + self.tail = new_node + + self.node_map[new_node._id] = new_node.data + self.length += 1 + Logger.info(f"Prepend node: {data}") + + def append(self, data): + """Append a node to the end of the list.""" + + new_node = Node(data) + if not self.head: + self.head = new_node + self.tail = new_node + else: + assert self.tail is not None, "Tail node is not set" + + self.tail.next = new_node + self.tail = new_node + + self.node_map[new_node._id] = new_node.data + self.length += 1 + Logger.info(f"Append node: {data}") + + def insert_between(self, data, before, after): + """ + Insert a node between two existing nodes in the list. + + :param data: The data to be stored in the new node. + :param before: The data of the node before which to + insert the new node. + :param after: The data of the node after which to insert the new node. + """ + if not self.head: + raise Exception("List is empty, nothing to insert") + + new_node = Node(data) + curr = self.head + + while curr and curr.data != after: + curr = curr.next + + if curr and curr.next and curr.next.data == before: + new_node.next = curr.next + curr.next = new_node + self.node_map[new_node._id] = new_node.data + self.length += 1 + + def delete_head(self): + """Delete the head node of the list.""" + + if not self.head: + raise Exception("List is empty, nothing to delete") + self.node_map.pop(self.head._id, None) + self.head = self.head.next + if not self.head: + self.tail = None + self.length -= 1 + + def delete_tail(self): + """ + Delete the tail node of the list. + """ + if not self.head: + raise Exception("List is empty, nothing to delete") + + if not self.head.next or self.head == self.tail: + self.node_map.pop(self.head._id, None) + self.head = self.tail = None + else: + prev_node = self.head + while prev_node.next != self.tail: + prev_node = prev_node.next + self.node_map.pop(self.tail._id, None) + self.tail = prev_node + self.tail.next = None + + self.length -= 1 + + def delete_any(self, target: Node): + """Delete any node from the list that matches the given target data.""" + if not isinstance(target, Node): + raise ValueError("Target must be a Node instance") + + if target._id not in self.node_map: + raise Exception(f"Node with ID {target._id} not found in the list") + + if self.head._id == target._id: + self.delete_head() + return + else: + prev_node: Node = self.head + curr: Node = self.head.next + + while curr and curr._id != target._id: + prev_node = curr + curr = curr.next + + if curr: + prev_node.next = curr.next + if not curr.next: + self.tail = prev_node + self.node_map.pop(curr._id, None) + self.length -= 1 + else: + raise Exception(f"Node with ID {target._id} not found in the list") + + def size(self): + """Get the number of nodes in the list.""" + + return self.length + + def reverse_list(self): + """ + Reverse the linked list in place. + """ + + prev = None + curr = self.head + next_node = None + + self.tail = self.head + + while curr: + next_node = curr.next + curr.next = prev + prev = curr + curr = next_node + + self.head = prev + + def find(self, node: Node) -> Optional[Node]: + """ + Find a node in the list by its id. + + :param id: The id of the node to find. + :return: The node with the given id or None if not found. + """ + return self.node_map.get(node._id, None) + + def clear(self): + """ + Clear the list. + """ + self.head = None + self.tail = None + self.length = 0 + self.node_map.clear() + + def __len__(self) -> int: + return self.length + + def __iter__(self): + current = self.head + while current: + yield current + current = current.next + + def __repr__(self) -> str: + nodes = [str(node) for node in self] + return " -> ".join(nodes) + + def print_list(self): + """ + Print the list data in a readable format. Since we have a + custom __iter__ and __repr__ methods, this method is not necessary. Simply + use a print statement to print the list data. + """ + print(self) \ No newline at end of file diff --git a/python/src/utils/__init__.py b/python/src/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/src/utils/logger.py b/python/src/utils/logger.py new file mode 100644 index 0000000..1d06171 --- /dev/null +++ b/python/src/utils/logger.py @@ -0,0 +1,46 @@ +import sys +import time +from datetime import datetime +from colorama import init, Fore, Style + +init(autoreset=True) + +class Logger: + LEVELS = { + "INFO": Fore.CYAN, + "DEBUG": Fore.BLUE, + "WARNING": Fore.YELLOW, + "ERROR": Fore.RED, + "SUCCESS": Fore.GREEN, + } + + @staticmethod + def log(level: str, message: str): + if level not in Logger.LEVELS: + raise ValueError(f"Invalid log level: {level}") + + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + formatted_mssg = ( + f"{Style.BRIGHT}[{timestamp}] {Logger.LEVELS[level]}[{level}] {Style.RESET_ALL}{message}" + ) + print(formatted_mssg, file=sys.stdout) + + @staticmethod + def info(message: str): + Logger.log("INFO", message) + + @staticmethod + def debug(message: str): + Logger.log("DEBUG", message) + + @staticmethod + def warning(message: str): + Logger.log("WARNING", message) + + @staticmethod + def error(message: str): + Logger.log("ERROR", message) + + @staticmethod + def success(message: str): + Logger.log("SUCCESS", message) \ No newline at end of file diff --git a/python/tests/conftest.py b/python/tests/conftest.py new file mode 100644 index 0000000..c149a93 --- /dev/null +++ b/python/tests/conftest.py @@ -0,0 +1,18 @@ +import os +import sys +import subprocess +from src.utils.logger import Logger + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src"))) + +def pytest_sessionfinish(session, exitstatus): + cleanup_script = os.path.abspath(os.path.join(os.path.dirname(__file__), "../cleanup.sh")) + if os.path.exists(cleanup_script): + Logger.log("INFO", f"Running cleanup script: {cleanup_script}") + try: + subprocess.run(["bash", cleanup_script], check=True) + Logger.log("INFO", "Cleanup script completed successfully") + except subprocess.CalledProcessError as e: + Logger.error(f"Error running cleanup script: {e}") + else: + Logger.error(f"Cleanup script not found: {cleanup_script}") \ No newline at end of file diff --git a/python/tests/linkedlist/__init__.py b/python/tests/linkedlist/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/tests/linkedlist/singly/__init__.py b/python/tests/linkedlist/singly/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/tests/linkedlist/singly/test_singly.py b/python/tests/linkedlist/singly/test_singly.py new file mode 100644 index 0000000..4cfd784 --- /dev/null +++ b/python/tests/linkedlist/singly/test_singly.py @@ -0,0 +1,41 @@ +from src.linkedlist.singly.list import LinkedList + +import pytest + +NUM_ELEMENTS = 100_000 + + +@pytest.fixture +def linked_list_append_fixture() -> LinkedList: + ll = LinkedList() + + for i in range(NUM_ELEMENTS): + ll.append(f"node({i})") + + return ll + + +@pytest.fixture +def linked_list_prepend_fixture() -> LinkedList: + ll = LinkedList() + + for i in range(NUM_ELEMENTS): + ll.prepend(f"node({i})") + + return ll + + +def test_append(linked_list_append_fixture): + ll = linked_list_append_fixture + + assert len(ll) == NUM_ELEMENTS + assert ll.get_tail().data == f"node({NUM_ELEMENTS-1})" + assert ll.get_head().data == "node(0)" + + +def test_prepend(linked_list_prepend_fixture): + ll = linked_list_prepend_fixture + + assert len(ll) == NUM_ELEMENTS + assert ll.get_tail().data == f"node({0})" + assert ll.get_head().data == f"node({NUM_ELEMENTS-1})" \ No newline at end of file