diff --git a/problem-1-1/README.md b/problem-1-1/README.md index 2b698ce..9ae8499 100644 --- a/problem-1-1/README.md +++ b/problem-1-1/README.md @@ -2,9 +2,12 @@ 1. 백의 정의에 따라서 API를 설계해 주세요. -| 함수 시그니처 | 설명 | -| ----------- | ----------- | -| sample(string): number | 문자열을 입력으로 받아 숫자로 변환해서 반환합니다 | +| 함수 시그니처 | 설명 | +|--------------------|----------------------------------------------------| +| `Bag()` | 백을 생성합니다. | +| `size(): number` | 백에 들어있는 아이템 개수를 반환합니다. | +| `isEmpty(): boolean` | 백에 아이템이 없다면 `true`, 아이템이 하나라도 있다면 `false` 를 반환합니다. | +| `add(item: any): void` | 백에 아이템을 추가합니다. | 2. 다음 값들을 백에 추가했을 때 어떻게 되는지 그림으로 그려보세요. diff --git a/problem-1-1/problem-1-1.test.js b/problem-1-1/problem-1-1.test.js index 6ab9348..2d9a596 100644 --- a/problem-1-1/problem-1-1.test.js +++ b/problem-1-1/problem-1-1.test.js @@ -1,4 +1,51 @@ +class Node { + item; + + next; +} + class Bag { + #first; + + #numberOfItems; + + constructor() { + this.#numberOfItems = 0; + } + + size() { + return this.#numberOfItems; + } + + isEmpty() { + return this.#numberOfItems === 0; + } + + add(item) { + const oldFirst = this.#first; + + this.#first = new Node(); + this.#first.item = item; + this.#first.next = oldFirst; + + this.#numberOfItems += 1; + } + + [Symbol.iterator]() { + let current = this.#first; + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } } test('백은 비어있는 상태로 생성된다', () => { diff --git a/problem-1-2/problem-1-2.test.js b/problem-1-2/problem-1-2.test.js index f2956b6..c4c710e 100644 --- a/problem-1-2/problem-1-2.test.js +++ b/problem-1-2/problem-1-2.test.js @@ -1,4 +1,77 @@ +class Node { + item; + + next; +} + +class Bag { + #first; + + #numberOfItems; + + constructor() { + this.#numberOfItems = 0; + } + + size() { + return this.#numberOfItems; + } + + isEmpty() { + return this.#numberOfItems === 0; + } + + add(item) { + const oldFirst = this.#first; + + this.#first = new Node(); + this.#first.item = item; + this.#first.next = oldFirst; + + this.#numberOfItems += 1; + } + + sum() { + let sum = 0; + let current = this.#first; + + while (current !== undefined) { + sum += current.item; + current = current.next; + } + + return sum; + } + + average() { + return Math.floor(this.sum() / this.size()); + } + + [Symbol.iterator]() { + let current = this.#first; + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } +} + const solution = (numbers) => { + const bag = new Bag(); + + numbers.forEach((number) => { + bag.add(number); + }); + + return bag.average(); }; test('숫자 배열의 평균을 반환한다', () => { diff --git a/problem-2-1/README.md b/problem-2-1/README.md index 2f9a3e6..af8ce40 100644 --- a/problem-2-1/README.md +++ b/problem-2-1/README.md @@ -1,10 +1,18 @@ # 자료구조와 알고리즘 2주차 과제 - 스택 구현하기 1. 스택의 정의에 따라서 API를 설계해 주세요. + - LIFO: Last-In, First-Out (가장 마지막에 담긴 아이템이 가장 먼저 나온다.) + - 입력과 출력이 항상 같은 곳에서 일어난다. + - 순회할 때는 역순으로 순회한다. | 함수 시그니처 | 설명 | | ----------- | ----------- | -| sample(string): number | 문자열을 입력으로 받아 숫자로 변환해서 반환합니다 | +| `Stack()` | 스택을 생성합니다. | +| `isEmpty(): boolean` | 스택에 아이템이 없다면 `true`, 아이템이 하나라도 있다면 `false` 를 반환합니다. | +| `size(): number` | 스택에 담긴 아이템의 개수를 반환합니다. | +| `push(item: any): void` | 스택에 아이템을 추가합니다. | +| `pop(): item` | 스택에 가장 마지막에 담긴 아이템을 반환합니다. | + 2. 다음 값들을 스택에 추가했을 때 어떻게 되는지 그림으로 그려보세요. `(-)`은 값을 꺼내는 것을 의미합니다. diff --git a/problem-2-1/problem-2-1.test.js b/problem-2-1/problem-2-1.test.js index 17412df..2393dba 100644 --- a/problem-2-1/problem-2-1.test.js +++ b/problem-2-1/problem-2-1.test.js @@ -1,4 +1,65 @@ +class Node { + item; + + next; +} + class Stack { + #first; + + #numberOfItem; + + constructor() { + this.#numberOfItem = 0; + } + + isEmpty() { + return this.#numberOfItem === 0; + } + + size() { + return this.#numberOfItem; + } + + push(item) { + // todo : 맨 앞에 아이템을 넣는 방식. + const oldFirst = this.#first; + + this.#first = new Node(); + this.#first.item = item; + this.#first.next = oldFirst; + + this.#numberOfItem += 1; + } + + pop() { + if (this.isEmpty()) { + throw new Error('스택이 비어있습니다'); + } + // todo: push 할 때 맨 앞에 가장 최근 아이템이 들어오니까 pop 할 때도 맨 앞에 있는 아이템을 꺼내야 한다. + const latestItem = this.#first.item; + this.#first = this.#first.next; + + this.#numberOfItem -= 1; + + return latestItem; + } + + [Symbol.iterator]() { + let current = this.#first; + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } } test('스택을 생성하면 비어있다', () => { diff --git a/problem-2-2/problem-2-2.test.js b/problem-2-2/problem-2-2.test.js index dab5212..0e5b231 100644 --- a/problem-2-2/problem-2-2.test.js +++ b/problem-2-2/problem-2-2.test.js @@ -1,4 +1,91 @@ +class Node { + item; + + next; +} + +class Stack { + #first; + + #numberOfItem; + + constructor() { + this.#numberOfItem = 0; + } + + isEmpty() { + return this.#numberOfItem === 0; + } + + size() { + return this.#numberOfItem; + } + + push(item) { + // todo : 맨 앞에 아이템을 넣는 방식. + const oldFirst = this.#first; + + this.#first = new Node(); + this.#first.item = item; + this.#first.next = oldFirst; + + this.#numberOfItem += 1; + } + + pop() { + if (this.isEmpty()) { + throw new Error('스택이 비어있습니다'); + } + // todo: push 할 때 맨 앞에 가장 최근 아이템이 들어오니까 pop 할 때도 맨 앞에 있는 아이템을 꺼내야 한다. + const latestItem = this.#first.item; + this.#first = this.#first.next; + + this.#numberOfItem -= 1; + + return latestItem; + } + + [Symbol.iterator]() { + let current = this.#first; + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } +} + const solution = (string) => { + const stack = new Stack(string.length); + const brackets = { '[': ']', '{': '}', '(': ')' }; + + if (string === '') { + return stack.isEmpty(); + } + + for (const char of string) { + if (brackets[char]) { // 현재 문자가 열린 괄호라면 = [, {, ( + stack.push(char); + } else { // 현재 문자가 닫힌 괄호라면 = ], }, ) + if (stack.isEmpty()) { // 현재 스택이 비어 있다면 false + return false; + } + + const topChar = stack.pop(); + if (brackets[topChar] !== char) { // 가장 위의 아이템을 꺼내서 짝이 맞는지 비교한다. + return false; + } + } + } + + return stack.isEmpty(); }; test('문자열에 포함된 괄호의 짝이 맞을 때 true를 반환한다', () => { diff --git a/problem-3-1/README.md b/problem-3-1/README.md index 4d27b95..e7cd238 100644 --- a/problem-3-1/README.md +++ b/problem-3-1/README.md @@ -2,9 +2,14 @@ 1. 큐의 정의에 따라서 API를 설계해 주세요. -| 함수 시그니처 | 설명 | -| ----------- | ----------- | -| sample(string): number | 문자열을 입력으로 받아 숫자로 변환해서 반환합니다 | +| 함수 시그니처 | 설명 | +|----------------------------|----------------------------------------------------| +| `Queue()` | 큐를 생성합니다. | +| `isEmpty(): void` | 큐에 아이템이 없다면 `true` 를, 아이템이 하나라도 있다면 `false` 를 반환합니다. | +| `size(): number` | 큐에 담긴 아이템의 개수를 반환합니다. | +| `enqueue(item: any): void` | 큐에 아이템을 추가합니다. | +| `dequeue(): item` | 큐의 첫 번째 아이템을 꺼냅니다. | + 2. 다음 값들을 큐에 추가했을 때 어떻게 되는지 그림으로 그려보세요. `(-)`은 값을 꺼내는 것을 의미합니다. diff --git a/problem-3-1/problem-3-1.test.js b/problem-3-1/problem-3-1.test.js index 6ae251f..3e86be4 100644 --- a/problem-3-1/problem-3-1.test.js +++ b/problem-3-1/problem-3-1.test.js @@ -1,4 +1,84 @@ +class Node { + item; + + next; +} + class Queue { + #first; + + #last; + + #numberOfItems; + + constructor() { + this.#numberOfItems = 0; + } + + isEmpty() { + return this.#numberOfItems === 0; + } + + size() { + return this.#numberOfItems; + } + + enqueue(item) { + const oldLast = this.#last; + this.#last = new Node(); + this.#last.item = item; + + if (this.isEmpty()) { + this.#first = this.#last; + } else { + oldLast.next = this.#last; + } + + this.#numberOfItems += 1; + } + + dequeue() { + if (this.isEmpty()) { + throw new Error('큐가 비어있습니다'); + } + + const firstItem = this.#first.item; + this.#first = this.#first.next; + + // todo: 아이템 개수를 먼저 줄인 다음, isEmpty() 를 확인해서 #last 를 undefined 처리한다. + this.#numberOfItems -= 1; + + if (this.isEmpty()) { + this.#last = undefined; + } + + return firstItem; + } + + first() { + return this.#first; + } + + last() { + return this.#last; + } + + [Symbol.iterator]() { + let current = this.#first; + + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } } test('큐를 생성하면 비어있다', () => { @@ -33,7 +113,7 @@ test('큐에서 요소를 제거하면 개수가 감소한다', () => { expect(newSize - oldSize).toEqual(-1); }); -test('가장 나중에 삽입한게 먼저 나온다', () => { +test('가장 먼저 삽입한게 먼저 나온다', () => { const queue = new Queue(); queue.enqueue('D'); @@ -43,6 +123,24 @@ test('가장 나중에 삽입한게 먼저 나온다', () => { expect(queue.dequeue()).toBe('D'); expect(queue.dequeue()).toBe('S'); expect(queue.dequeue()).toBe('A'); + + expect(queue.first()).toBeUndefined(); + expect(queue.last()).toBeUndefined(); +}); + +test('아이템을 모두 제거하면 first, last 는 모두 undefined 를 가리켜야 한다.', () => { + const queue = new Queue(); + + queue.enqueue('D'); + queue.enqueue('S'); + queue.enqueue('A'); + + expect(queue.dequeue()).toBe('D'); + expect(queue.dequeue()).toBe('S'); + expect(queue.dequeue()).toBe('A'); + + expect(queue.first()).toBeUndefined(); + expect(queue.last()).toBeUndefined(); }); test('큐이 비어있는데 dequeue을 하면 예외를 던진다', () => { diff --git a/problem-3-2/problem-3-2.test.js b/problem-3-2/problem-3-2.test.js index 21bd836..8180ade 100644 --- a/problem-3-2/problem-3-2.test.js +++ b/problem-3-2/problem-3-2.test.js @@ -1,4 +1,101 @@ -const solution = (N, M) => { +class Node { + item; + + next; +} + +class Queue { + #first; + + #last; + + #numberOfItems; + + constructor() { + this.#numberOfItems = 0; + } + + isEmpty() { + return this.#numberOfItems === 0; + } + + size() { + return this.#numberOfItems; + } + + enqueue(item) { + const oldLast = this.#last; + this.#last = new Node(); + this.#last.item = item; + + if (this.isEmpty()) { + this.#first = this.#last; + } else { + oldLast.next = this.#last; + } + + this.#numberOfItems += 1; + } + + dequeue() { + if (this.isEmpty()) { + throw new Error('큐가 비어있습니다'); + } + + const firstItem = this.#first.item; + this.#first = this.#first.next; + + this.#numberOfItems -= 1; + + return firstItem; + } + + [Symbol.iterator]() { + let current = this.#first; + + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.item; + current = current.next; + + return { done: false, value }; + }, + }; + } +} + +const createNumberArray = (itemCount) => { + const resultArray = []; + + for (let index = 1; index <= itemCount; index++) { + resultArray.push(index); + } + + return resultArray; +}; + +const solution = (numberOfPersons, deathLocation) => { + const persons = createNumberArray(numberOfPersons); + + const queue = new Queue(); + persons.forEach((person) => { + queue.enqueue(person); + }); + + while (queue.size() > 1) { + for (let index = 0; index < deathLocation - 1; index += 1) { + const personToMove = queue.dequeue(); + queue.enqueue(personToMove); + } + + queue.dequeue(); + } + + return queue.dequeue(); }; test('N명의 사람이 있을 때 M번째 사람을 없앨 때 마지막에 죽는 사람의 순서를 반환한다', () => {