diff --git a/problem-1/problem-1.test.js b/problem-1/problem-1.test.js index f54f840..e558b0b 100644 --- a/problem-1/problem-1.test.js +++ b/problem-1/problem-1.test.js @@ -1,4 +1,23 @@ const bubbleSort = (array) => { + let exchangeCount = 0; + + // i = 정렬이 완료될 원소의 개수 + // 첫 패스 때 1개의 정렬이 완료됨 + // 마지막 패스 때 array.length - 1개의 원소가 정렬됨 + // array.length - 1개가 원소가 정렬되면 마지막 원소는 정렬할 필요가 없다. + for (let i = 0; i < array.length; i++) { + for (let j = 0; j < array.length - 1 - i; j++) { + if (array[j] > array[j + 1]) { + exchangeCount += 1; + + [array[j + 1], array[j]] = [array[j], array[j + 1]]; + } + } + + if (i === 0 && exchangeCount === 0) { + return; + } + } }; test.each([ diff --git a/problem-2/problem-2.test.js b/problem-2/problem-2.test.js index 1e1fe4f..469f324 100644 --- a/problem-2/problem-2.test.js +++ b/problem-2/problem-2.test.js @@ -1,4 +1,29 @@ +const exchange = (array, a, b) => { + [array[b], array[a]] = [array[a], array[b]]; +}; + +const less = (a, b) => a < b; + +const findMinIndex = (array, startIndex) => { + let minIndex = startIndex; + + for (let i = startIndex; i < array.length; i++) { + if (less(array[i], array[minIndex])) { + minIndex = i; + } + } + + return minIndex; +}; + const selectionSort = (array) => { + for (let i = 0; i < array.length - 1; i++) { + const minIndex = findMinIndex(array, i); + + if (i !== minIndex) { + exchange(array, i, minIndex); + } + } }; test.each([ diff --git a/problem-3/problem-3.test.js b/problem-3/problem-3.test.js index e4450b5..abf46bb 100644 --- a/problem-3/problem-3.test.js +++ b/problem-3/problem-3.test.js @@ -1,4 +1,19 @@ +const exchange = (array, a, b) => { + [array[b], array[a]] = [array[a], array[b]]; +}; + +const less = (a, b) => a < b; + const insertionSort = (array) => { + for (let i = 1; i < array.length; i++) { + for (let j = i; j > 0; j--) { + if (less(array[j], array[j - 1])) { + exchange(array, j, j - 1); + } else { + break; + } + } + } }; test.each([ diff --git a/problem-4/problem-4.test.js b/problem-4/problem-4.test.js index 069ccdf..15b519a 100644 --- a/problem-4/problem-4.test.js +++ b/problem-4/problem-4.test.js @@ -1,4 +1,26 @@ -const shellSort = (array) => { +const exchange = (list, a, b) => { + [list[b], list[a]] = [list[a], list[b]]; +}; + +const less = (a, b) => a < b; + +const shellSort = (list) => { + let h = 1; + const { length } = list; + + while (h < (length / 3)) { + h = (3 * h) + 1; + } + + for (let k = h; k >= 1; k = Math.floor(k / 3)) { + for (let i = k; i < length; i++) { + for (let j = i; j >= k; j--) { + if (less(list[j], list[j - k])) { + exchange(list, j, j - k); + } + } + } + } }; test.each([ diff --git a/problem-5/problem-5.test.js b/problem-5/problem-5.test.js index 124ce9e..f72d4c8 100644 --- a/problem-5/problem-5.test.js +++ b/problem-5/problem-5.test.js @@ -1,4 +1,34 @@ -const mergeSort = (array) => { +const less = (a, b) => a < b; + +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++]; + } else if (right > end) { + array[i] = temp[left++]; + } else if (less(temp[left], temp[right])) { + array[i] = temp[left++]; + } else { + array[i] = temp[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); }; test.each([ diff --git a/problem-6/problem-6.test.js b/problem-6/problem-6.test.js index f335a1f..8adc1bc 100644 --- a/problem-6/problem-6.test.js +++ b/problem-6/problem-6.test.js @@ -1,4 +1,69 @@ -const quickSort = (array) => { +const exchange = (array, a, b) => { + [array[b], array[a]] = [array[a], array[b]]; +}; + +/** + * 1. 하나의 배열에 대해서 퀵 정렬을 실행 + * 2. pivot의 위치를 결정한다 + * 3. pivot의 위치를 기준으로 왼쪽, 오른쪽 부분 배열을 만든다 + * 4. 왼쪽, 오른쪽 부분 배열에 대해서 퀵 정렬을 실행한다 + */ +const quickSort = (array, start, end) => { + const startIndex = start || 0; + const endIndex = end || array.length - 1; + + const length = endIndex - startIndex + 1; + + if (length <= 1) { + return; + } + + const pivot = array[startIndex]; + + let left = startIndex; + let right = endIndex; + + while (right > left) { + for (left; left <= endIndex; left++) { + if (pivot < array[left]) { + break; + } + } + + if (left > endIndex) { + left = endIndex; + } + + for (right; right >= startIndex; right--) { + if (pivot >= array[right]) { + break; + } + } + + if (right < startIndex) { + right = startIndex; + } + + if (right <= left) { + break; + } + + exchange(array, left, right); + } + + const pivotIndex = right; + + exchange(array, pivotIndex, startIndex); + + // 왼쪽 배열 + if (pivotIndex - 1 >= startIndex) { + quickSort(array, startIndex, pivotIndex - 1); + } + + // 오른쪽 배열 + if (pivotIndex + 1 <= endIndex) { + quickSort(array, pivotIndex + 1, endIndex); + } }; test.each([ diff --git a/problem-7/problem-7.test.js b/problem-7/problem-7.test.js index 8d36fec..83baac5 100644 --- a/problem-7/problem-7.test.js +++ b/problem-7/problem-7.test.js @@ -1,4 +1,48 @@ +const exchange = (array, a, b) => { + [array[b], array[a]] = [array[a], array[b]]; +}; + +const less = (a, b) => a < b; + +const sink = (array, i, N) => { + // 자식 노드가 있을 때까지 반복 + while (2 * i <= N) { + // 왼쪽 자식 노드부터 탐색 + let j = 2 * i; + + // 오른쪽 자식 노드가 더 클 경우 + if (j < N && less(array[j], array[j + 1])) { + j++; + } + + // 더 이상 정렬할 필요가 없을 경우 + if (!less(array[i], array[j])) { + break; + } + + exchange(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) { + // 루트 노드와 제일 마지막 노드 교환 + exchange(array, 1, N); + + // 마지막 노드는 정렬되었으니 sync할 항목에서 제외 + N--; + + sink(array, 1, N); + } }; test.each([