diff --git a/package-lock.json b/package-lock.json index 4d52feb..248c0d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "dsa-week2-assignment-1", + "name": "dsa-week3-assignment-1", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "dsa-week2-assignment-1", + "name": "dsa-week3-assignment-1", "version": "1.0.0", "license": "ISC", "devDependencies": { diff --git a/problem-1/README.md b/problem-1/README.md index 99c4ed7..bad37fc 100644 --- a/problem-1/README.md +++ b/problem-1/README.md @@ -15,6 +15,8 @@ 2. 버블 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![버블 정렬 코드](./bubble.jpeg) + 3. 버블 정렬의 코드를 직접 구현해 주세요. 4. (추가 문제) 버블 정렬은 이미 정렬되어 있다면, 더 이상 정렬을 수행하지 않고 diff --git a/problem-1/bubble.jpeg b/problem-1/bubble.jpeg new file mode 100644 index 0000000..ece3bba Binary files /dev/null and b/problem-1/bubble.jpeg differ diff --git a/problem-1/problem-1.test.js b/problem-1/problem-1.test.js index f54f840..ca7e8ca 100644 --- a/problem-1/problem-1.test.js +++ b/problem-1/problem-1.test.js @@ -1,12 +1,46 @@ +const swap = (array, left, right) => { + [array[left], array[right]] = [array[right], array[left]]; +}; + const bubbleSort = (array) => { + let loopCount = 0; + for (let i = 0; i < array.length; i++) { + let swapped = false; + + for (let j = 0; j < array.length - i - 1; j++) { + loopCount++; + const left = array[j]; + const right = array[j + 1]; + + if (left > right) { + swap(array, j, j + 1); + + swapped = true; + } + } + + if (!swapped) { + break; + } + } + + return loopCount; }; -test.each([ - [[5, 4, 3, 2, 1]], - [[1, 2, 3, 4, 5]], - [[4, 2, 1, 5, 3]], -])('주어진 배열을 오름차순으로 정렬한다', (array) => { - bubbleSort(array); +test.each([[[5, 4, 3, 2, 1]], [[1, 2, 3, 4, 5]], [[4, 2, 1, 5, 3]]])( + '주어진 배열을 오름차순으로 정렬한다', + (array) => { + bubbleSort(array); + + expect(array).toEqual([1, 2, 3, 4, 5]); + } +); + +test('이미 정렬된 배열은 정렬하지 않는다', () => { + const array = [1, 2, 3, 4, 5]; + + const loopCount = bubbleSort(array); expect(array).toEqual([1, 2, 3, 4, 5]); + expect(loopCount).toBe(array.length - 1); }); diff --git a/problem-2/README.md b/problem-2/README.md index a5996a6..a8b40fd 100644 --- a/problem-2/README.md +++ b/problem-2/README.md @@ -14,4 +14,6 @@ 2. 선택 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![선택 정렬 코드](./selection.jpeg) + 3. 선택 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-2/problem-2.test.js b/problem-2/problem-2.test.js index 1e1fe4f..f9c8088 100644 --- a/problem-2/problem-2.test.js +++ b/problem-2/problem-2.test.js @@ -1,12 +1,36 @@ +const swap = (array, i, j) => { + [array[i], array[j]] = [array[j], array[i]]; +}; + +const findMinIndex = (array, startIndex) => { + let minIndex = startIndex; + + for (let i = startIndex; i < array.length; i++) { + if (array[minIndex] > array[i]) { + minIndex = i; + } + } + + return minIndex; +}; + const selectionSort = (array) => { + for (let i = 0; i < array.length - 1; i++) { + const minIndex = findMinIndex(array, i); + + if (minIndex === i) { + continue; + } + + swap(array, i, minIndex); + } }; -test.each([ - [[5, 4, 3, 2, 1]], - [[1, 2, 3, 4, 5]], - [[4, 2, 1, 5, 3]], -])('주어진 배열을 오름차순으로 정렬한다', (array) => { - selectionSort(array); +test.each([[[5, 4, 3, 2, 1]], [[1, 2, 3, 4, 5]], [[4, 2, 1, 5, 3]]])( + '주어진 배열을 오름차순으로 정렬한다', + (array) => { + selectionSort(array); - expect(array).toEqual([1, 2, 3, 4, 5]); -}); + expect(array).toEqual([1, 2, 3, 4, 5]); + } +); diff --git a/problem-2/selection.jpeg b/problem-2/selection.jpeg new file mode 100644 index 0000000..950d462 Binary files /dev/null and b/problem-2/selection.jpeg differ diff --git a/problem-3/README.md b/problem-3/README.md index 02ba68a..3085659 100644 --- a/problem-3/README.md +++ b/problem-3/README.md @@ -14,4 +14,6 @@ 2. 삽입 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![삽입정렬](./insertion.jpeg) + 3. 삽입 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-3/insertion.jpeg b/problem-3/insertion.jpeg new file mode 100644 index 0000000..fb97f37 Binary files /dev/null and b/problem-3/insertion.jpeg differ diff --git a/problem-3/problem-3.test.js b/problem-3/problem-3.test.js index e4450b5..b9b07fd 100644 --- a/problem-3/problem-3.test.js +++ b/problem-3/problem-3.test.js @@ -1,12 +1,24 @@ +const swap = (array, i, j) => { + [array[i], array[j]] = [array[j], array[i]]; +}; + const insertionSort = (array) => { + for (let i = 1; i < array.length; i++) { + for (let j = i; j > 0; j--) { + if (array[j] < array[j - 1]) { + swap(array, j, j - 1); + } else { + break; + } + } + } }; -test.each([ - [[5, 4, 3, 2, 1]], - [[1, 2, 3, 4, 5]], - [[4, 2, 1, 5, 3]], -])('주어진 배열을 오름차순으로 정렬한다', (array) => { - insertionSort(array); +test.each([[[5, 4, 3, 2, 1]], [[1, 2, 3, 4, 5]], [[4, 2, 1, 5, 3]]])( + '주어진 배열을 오름차순으로 정렬한다', + (array) => { + insertionSort(array); - expect(array).toEqual([1, 2, 3, 4, 5]); -}); + expect(array).toEqual([1, 2, 3, 4, 5]); + } +); diff --git a/problem-4/README.md b/problem-4/README.md index d8e566d..590d830 100644 --- a/problem-4/README.md +++ b/problem-4/README.md @@ -12,4 +12,6 @@ 2. 셀 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![셀 정렬 코드](./shell.jpeg) + 3. 셀 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-4/problem-4.test.js b/problem-4/problem-4.test.js index 069ccdf..403758d 100644 --- a/problem-4/problem-4.test.js +++ b/problem-4/problem-4.test.js @@ -1,4 +1,28 @@ +const swap = (array, i, j) => { + [array[i], array[j]] = [array[j], array[i]]; +}; + const shellSort = (array) => { + const { length } = array; + + let h = 1; + while (h < Math.floor(length / 3)) { + h = 3 * h + 1; + } + + while (h >= 1) { + for (let i = h; i < length; i++) { + for (let j = i; j >= 0; j = j - h) { + if (array[j] < array[j - h]) { + swap(array, j, j - h); + } else { + break; + } + } + } + + h = Math.floor(h / 3); + } }; test.each([ @@ -8,6 +32,7 @@ test.each([ ])('주어진 배열을 오름차순으로 정렬한다', (array) => { shellSort(array); - expect(array) - .toEqual([1, 5, 5, 5, 8, 12, 12, 12, 13, 15, 16, 18, 19, 19, 20, 24]); + expect(array).toEqual([ + 1, 5, 5, 5, 8, 12, 12, 12, 13, 15, 16, 18, 19, 19, 20, 24, + ]); }); diff --git a/problem-4/shell.jpeg b/problem-4/shell.jpeg new file mode 100644 index 0000000..dc050db Binary files /dev/null and b/problem-4/shell.jpeg differ diff --git a/problem-5/README.md b/problem-5/README.md index 01799bb..51cd8ba 100644 --- a/problem-5/README.md +++ b/problem-5/README.md @@ -13,4 +13,6 @@ 2. 머지 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![merge-sort](./merge.jpeg) + 3. 머지 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-5/merge.jpeg b/problem-5/merge.jpeg new file mode 100644 index 0000000..537c8b4 Binary files /dev/null and b/problem-5/merge.jpeg differ diff --git a/problem-5/problem-5.test.js b/problem-5/problem-5.test.js index 124ce9e..6c24422 100644 --- a/problem-5/problem-5.test.js +++ b/problem-5/problem-5.test.js @@ -1,4 +1,51 @@ -const mergeSort = (array) => { +const merge = (array, start, mid, end) => { + let left = start; + let right = mid + 1; + + const temp = [...array]; + + for (let i = start; i <= end; i++) { + if (left > mid) { + array[i] = temp[right]; + right++; + } else if (right > end) { + array[i] = temp[left]; + left++; + } else if (temp[left] < temp[right]) { + array[i] = temp[left]; + left++; + } else { + array[i] = temp[right]; + right++; + } + } +}; + +const mergeSort = (array, start = 0, end = array.length - 1) => { + if (start >= end) { + return; + } + + const mid = Math.floor((start + end) / 2); + + mergeSort(array, start, mid); + mergeSort(array, mid + 1, end); + merge(array, start, mid, end); +}; + +const mergeSortBottomUp = (array) => { + const { length } = array; + + for (let n = 1; n < length; n = n * 2) { + for (let start = 0; start < length - n; start = start + n * 2) { + merge( + array, + start, + start + (n - 1), + Math.min(start + (n * 2 - 1), length - 1) + ); + } + } }; test.each([ @@ -6,10 +53,13 @@ test.each([ [[1, 2, 3, 4, 5, 6, 7, 8]], [[8, 2, 1, 5, 3, 4, 7, 6]], ])('주어진 배열을 오름차순으로 정렬한다', (array) => { - mergeSort(array); + const array1 = [...array]; + const array2 = [...array]; + mergeSort(array1); + mergeSortBottomUp(array2); - expect(array) - .toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + expect(array1).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + expect(array2).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); }); test('홀수 개 배열에 대해서도 오름차순으로 정렬한다', () => { diff --git a/problem-6/README.md b/problem-6/README.md index 67c9f36..d0290fc 100644 --- a/problem-6/README.md +++ b/problem-6/README.md @@ -15,4 +15,6 @@ 2. 퀵 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +![퀵 정렬](./quick.jpeg) + 3. 퀵 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-6/problem-6.test.js b/problem-6/problem-6.test.js index f335a1f..75506af 100644 --- a/problem-6/problem-6.test.js +++ b/problem-6/problem-6.test.js @@ -1,4 +1,53 @@ +const swap = (array, i, j) => { + [array[i], array[j]] = [array[j], array[i]]; +}; + +const partition = (array, lo, hi) => { + let left = lo + 1; + let right = hi; + + const pivot = array[lo]; + + while (true) { + while (array[left] < pivot) { + if (left === hi) { + break; + } + + left++; + } + + while (pivot < array[right]) { + if (right === lo) { + break; + } + + right--; + } + + if (left >= right) { + break; + } + + swap(array, left, right); + } + + swap(array, lo, right); + return right; +}; + +const sort = (array, lo, hi) => { + if (lo >= hi) { + return; + } + + const j = partition(array, lo, hi); // j는 pivot의 위치이고, 이것을 기준으로 왼쪽과 오른쪽을 나누고 정렬한다. partition에서 swap을 통해 정렬이 이루어진다. + sort(array, lo, j - 1); + sort(array, j + 1, hi); +}; + const quickSort = (array) => { + sort(array, 0, array.length - 1); }; test.each([ @@ -8,6 +57,5 @@ test.each([ ])('주어진 배열을 오름차순으로 정렬한다', (array) => { quickSort(array); - expect(array) - .toEqual([1, 2, 3, 4, 5, 6, 7, 8]); + expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); }); diff --git a/problem-6/quick.jpeg b/problem-6/quick.jpeg new file mode 100644 index 0000000..81611a0 Binary files /dev/null and b/problem-6/quick.jpeg differ diff --git a/problem-7/README.md b/problem-7/README.md index 3210a01..dddb8e0 100644 --- a/problem-7/README.md +++ b/problem-7/README.md @@ -1,6 +1,6 @@ -# 자료구조와 알고리즘 3주차 과제 - 퀵 정렬 구현하기 +# 자료구조와 알고리즘 3주차 과제 - 힙 정렬 구현하기 -1. 다음은 퀵 정렬의 알고리즘입니다. 알고리즘을 보고 다음 값이 주어졌을 때 +1. 다음은 힙 정렬의 알고리즘입니다. 알고리즘을 보고 다음 값이 주어졌을 때 어떻게 정렬이 되는지 그림으로 그려보세요. > 힙 구성: 입력 배열을 최대 힙 구조로 만듭니다. @@ -9,9 +9,11 @@ > 빌 때까지 반복합니다. ``` -3, 5, 6, 8, 1, 2, 4, 7 +9, 11, 6, 8, 7, 2, 3, 4, 1, 10, 5 ``` -2. 퀵 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. +2. 힙 정렬의 코드를 보고 어떻게 동작하는지 그림을 그려보세요. 마찬가지로 위의 값이 주어졌다고 했을 때 코드를 따라가며 그림을 그려가며 이해해 보세요. -3. 퀵 정렬의 코드를 직접 구현해 주세요. +![힙 정렬](./heap.jpeg) + +3. 힙 정렬의 코드를 직접 구현해 주세요. diff --git a/problem-7/heap.jpeg b/problem-7/heap.jpeg new file mode 100644 index 0000000..a51af67 Binary files /dev/null and b/problem-7/heap.jpeg differ diff --git a/problem-7/problem-7.test.js b/problem-7/problem-7.test.js index 8d36fec..17cef46 100644 --- a/problem-7/problem-7.test.js +++ b/problem-7/problem-7.test.js @@ -1,4 +1,36 @@ +const swap = (array, a, b) => { + [array[a], array[b]] = [array[b], array[a]]; +}; + +const sink = (array, i, N) => { + while (2 * i <= N) { + let j = 2 * i; + if (j < N && array[j] < array[j + 1]) { + j++; + } + + if (array[i] >= array[j]) { + break; + } + + swap(array, i, j); + i = j; + } +}; + const heapSort = (array) => { + let N = array.length - 1; + + for (let i = Math.floor(N / 2); i >= 1; i--) { + sink(array, i, N); + } + + while (N > 1) { + swap(array, 1, N); // 가장 큰 요소를 힙의 끝으로 보낸다. + N--; // 힙의 크기를 줄인다. + sink(array, 1, N); // 힙을 복원한다. + // 이 과정에서 가장 끝부터 정렬된다. + } }; test.each([ @@ -8,6 +40,5 @@ test.each([ ])('주어진 배열을 오름차순으로 정렬한다', (array) => { heapSort(array); - expect(array) - .toEqual([undefined, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); + expect(array).toEqual([undefined, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); });