Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions algorithm/BinarySearchTree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
class Node {
constructor(data) {
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
}

class BinarySearchTree {
constructor() {
this.root = null;
}

insert(value) {
const newNode = new Node(value);
if (this.root === null) {
this.root = newNode;
return;
}
let current = this.root;
while (current) {
if (value < current.data) {
if (current.leftChild === null) {
current.leftChild = newNode;
return;
}
current = current.leftChild;
} else if (value > current.data) {
if (current.rightChild === null) {
current.rightChild = newNode;
return;
}
current = current.rightChild;
} else {
return;
}
}
}

find(value) {
let current = this.root;
while (current !== null) {
if (value === current.data) return current;
if (value < current.data) {
current = current.leftChild;
} else {
current = current.rightChild;
}
}
return null;
}

remove(value) {
this.root = this._removeNode(this.root, value);
}

_removeNode(node, value) {
if (node === null) return null;

if (value < node.data) {
node.leftChild = this._removeNode(node.leftChild, value);
return node;
}

if (value > node.data) {
node.rightChild = this._removeNode(node.rightChild, value);
return node;
}

if (node.leftChild === null && node.rightChild === null) {
return null;
}

if (node.leftChild === null) return node.rightChild;
if (node.rightChild === null) return node.leftChild;

let successor = node.rightChild;
while (successor.leftChild !== null) {
successor = successor.leftChild;
}
node.data = successor.data;
node.rightChild = this._removeNode(node.rightChild, successor.data);
return node;
}

preorder(node = this.root, result = []) {
if (node === null) return result;
result.push(node.data);
this.preorder(node.leftChild, result);
this.preorder(node.rightChild, result);
return result;
}

inorder(node = this.root, result = []) {
if (node === null) return result;
this.inorder(node.leftChild, result);
result.push(node.data);
this.inorder(node.rightChild, result);
return result;
}

postorder(node = this.root, result = []) {
if (node === null) return result;
this.postorder(node.leftChild, result);
this.postorder(node.rightChild, result);
result.push(node.data);
return result;
}
}

const bst = new BinarySearchTree();

// 1. insert - 트리에 값 추가
//
// 50
// / \
// 30 70
// / \ / \
// 20 40 60 80
//
bst.insert(50);
bst.insert(30);
bst.insert(70);
bst.insert(20);
bst.insert(40);
bst.insert(60);
bst.insert(80);

console.log('inorder:', bst.inorder()); // [20, 30, 40, 50, 60, 70, 80]
console.log('preorder:', bst.preorder()); // [50, 30, 20, 40, 70, 60, 80]
console.log('postorder:', bst.postorder()); // [20, 40, 30, 60, 80, 70, 50]

// 2. find - 값으로 노드 검색
const found = bst.find(40);
console.log('find 40:', found.data); // find 40: 40
console.log('find 40 left:', found.leftChild); // find 40 left: null
console.log('find 40 right:', found.rightChild); // find 40 right: null

const notFound = bst.find(99);
console.log('find 99:', notFound); // find 99: null

// 3. remove - 리프 노드 삭제 (자식 없음)
//
// 50
// / \
// 30 70
// \ / \
// 40 60 80
//
bst.remove(20);
console.log('remove 20:', bst.inorder()); // [30, 40, 50, 60, 70, 80]

// 4. remove - 자식이 하나인 노드 삭제
//
// 50
// / \
// 40 70
// / \
// 60 80
//
bst.remove(30);
console.log('remove 30:', bst.inorder()); // [40, 50, 60, 70, 80]

// 5. remove - 자식이 둘인 노드 삭제 (후속자로 대체)
//
// 60
// / \
// 40 70
// \
// 80
//
bst.remove(50);
console.log('remove 50:', bst.inorder()); // [40, 60, 70, 80]
console.log('new root:', bst.root.data); // new root: 60
142 changes: 142 additions & 0 deletions algorithm/DoublyLinkedList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
class Node {
constructor(data) {
this.data = data;
this.next = null;
this.prev = null;
}
}

class DoublyLinkedList {
constructor() {
this.head = null;
this.tail = null;
}

toString() {
let resStr = '|';
let iterator = this.head;
while (iterator !== null) {
resStr += ` ${iterator.data} |`;
iterator = iterator.next;
}
return resStr;
}

findNodeAt(index) {
if (index < 0) return null;
let iterator = this.head;
for (let i = 0; i < index; i++) {
if (iterator === null) return null;
iterator = iterator.next;
}
return iterator;
}

findNode(value) {
let iterator = this.head;
while (iterator !== null) {
if (iterator.data === value) return iterator;
iterator = iterator.next;
}
return null;
}

addToTail(value) {
const newNode = new Node(value);
if (this.head === null) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}
}

addToHead(value) {
const newNode = new Node(value);
if (this.head === null) {
this.head = newNode;
this.tail = newNode;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
}
}

insertAfter(targetValue, newValue) {
const prevNode = this.findNode(targetValue);
if (prevNode === null) return;

const newNode = new Node(newValue);
if (prevNode === this.tail) {
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
} else {
newNode.next = prevNode.next;
newNode.prev = prevNode;
prevNode.next.prev = newNode;
prevNode.next = newNode;
}
}

removeNode(value) {
const nodeToDelete = this.findNode(value);
if (nodeToDelete === null) return null;

if (nodeToDelete === this.head && nodeToDelete === this.tail) {
this.head = null;
this.tail = null;
} else if (nodeToDelete === this.head) {
this.head = this.head.next;
if (this.head) this.head.prev = null;
} else if (nodeToDelete === this.tail) {
this.tail = this.tail.prev;
if (this.tail) this.tail.next = null;
} else {
nodeToDelete.prev.next = nodeToDelete.next;
nodeToDelete.next.prev = nodeToDelete.prev;
}

return nodeToDelete.data;
}
}

const list = new DoublyLinkedList();

// 1. addToTail - 리스트 뒤쪽에 노드 추가
list.addToTail(10);
list.addToTail(20);
list.addToTail(30);
console.log(list.toString()); // | 10 | 20 | 30 |

// 2. addToHead - 리스트 앞쪽에 노드 추가
list.addToHead(5);
list.addToHead(1);
console.log(list.toString()); // | 1 | 5 | 10 | 20 | 30 |

// 3. findNode - 값으로 노드 검색
const found = list.findNode(20);
console.log('found:', found.data); // found: 20

const notFound = list.findNode(99);
console.log('notFound:', notFound); // notFound: null

// 4. insertAfter - 특정 값 뒤에 새 노드 삽입
list.insertAfter(10, 15);
console.log(list.toString()); // | 1 | 5 | 10 | 15 | 20 | 30 |

list.insertAfter(30, 40);
console.log(list.toString()); // | 1 | 5 | 10 | 15 | 20 | 30 | 40 |

// 5. removeNode - 특정 값을 가진 노드 삭제
list.removeNode(1);
console.log(list.toString()); // | 5 | 10 | 15 | 20 | 30 | 40 |

list.removeNode(15);
console.log(list.toString()); // | 5 | 10 | 20 | 30 | 40 |

list.removeNode(40);
console.log(list.toString()); // | 5 | 10 | 20 | 30 |
Loading