From f4590758a7365ce87a2dda58c1d60410a6245372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Sat, 16 Jan 2021 22:05:19 +0900 Subject: [PATCH 1/6] CS: bubble sort --- CS/Sort/kyu9341/bubbleSort.js | 39 +++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 CS/Sort/kyu9341/bubbleSort.js diff --git a/CS/Sort/kyu9341/bubbleSort.js b/CS/Sort/kyu9341/bubbleSort.js new file mode 100644 index 0000000..438f899 --- /dev/null +++ b/CS/Sort/kyu9341/bubbleSort.js @@ -0,0 +1,39 @@ +/* +거품 정렬(Bubble sort)은 두 인접한 원소를 검사하여 정렬하는 방법이다. 시간 복잡도가 O(n^2)로 상당히 느리지만, +코드가 단순하기 때문에 자주 사용된다. 원소의 이동이 거품이 수면으로 올라오는 듯한 모습을 보이기 때문에 지어진 이름이다. + +출처 - 위키백과 +*/ + +const bubbleSort = (arr, compare = (prev, next) => prev - next) => { + const swap = (arr, curIdx, nextIdx) => { + const curVal = arr[curIdx]; + arr[curIdx] = arr[nextIdx]; + arr[nextIdx] = curVal; + }; + + const len = arr.length; + + for (let i = 0; i < len; i++) + for (let j = 0; j < len - i - 1; j++) + if (compare(arr[j], arr[j + 1]) > 0) swap(arr, j, j + 1); +}; + +(() => { + const testCase = [ + { arr: [5, 4, 2, 6, 1, 9, 3] }, + { arr: [11, 10, 34, 5, 4, 2, 6, 1, 9, 3] }, + { + arr: [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + compare: (prev, next) => next - prev, + }, + { + arr: [[5, 6, 7, 1, 2, 3], [], [1, 2, 3], [4, 2], [6, 4, 7, 3, 1]], + compare: (prev, next) => prev.length - next.length, + }, + ]; + testCase.forEach(({ arr, compare }) => { + bubbleSort(arr, compare); + console.log(arr); + }); +})(); From 790322958bf7e0611612ad263ffb0bae9f3419d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Sun, 17 Jan 2021 15:59:56 +0900 Subject: [PATCH 2/6] CS: selection sort --- CS/Sort/kyu9341/selectionSort.js | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 CS/Sort/kyu9341/selectionSort.js diff --git a/CS/Sort/kyu9341/selectionSort.js b/CS/Sort/kyu9341/selectionSort.js new file mode 100644 index 0000000..8d30a0f --- /dev/null +++ b/CS/Sort/kyu9341/selectionSort.js @@ -0,0 +1,39 @@ +/* +선택 정렬은 제자리 정렬 알고리즘의 하나로, 다음와 같은 순서로 이루어진다. + +1. 주어진 리스트 중에 최소값을 찾는다 +2. 그 값을 맨 앞에 위치한 값과 교체한다.(패스) +3. 맨 처음 위치를 뺀 나머지 리스트를 같은 방법으로 교체한다. +*/ + +const selectionSort = arr => { + const swap = (arr, curIdx, targetIdx) => { + const curVal = arr[curIdx]; + arr[curIdx] = arr[targetIdx]; + arr[targetIdx] = curVal; + }; + + const len = arr.length; + let minIdx; + + for (let i = 0; i < len; i++) { + minIdx = i; + + for (let j = i; j < len; j++) { + minIdx = arr[minIdx] > arr[j] ? j : minIdx; + } + + swap(arr, i, minIdx); + } +}; + +(() => { + const testCase = [ + [5, 4, 2, 6, 1, 9, 3], + [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + ]; + testCase.forEach(arr => { + selectionSort(arr); + console.log(arr); + }); +})(); From d65fdb128b80f5acf7907362e72e1713e5cef552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Mon, 18 Jan 2021 23:55:34 +0900 Subject: [PATCH 3/6] CS: insertion sort --- CS/Sort/kyu9341/insertionSort.js | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 CS/Sort/kyu9341/insertionSort.js diff --git a/CS/Sort/kyu9341/insertionSort.js b/CS/Sort/kyu9341/insertionSort.js new file mode 100644 index 0000000..25c3a10 --- /dev/null +++ b/CS/Sort/kyu9341/insertionSort.js @@ -0,0 +1,36 @@ +/* +삽입 정렬은 두 번째 원소부터 시작하여 그 앞(왼쪽)의 원소들과 비교하여 삽입할 위치를 지정한 후 원소를 뒤로 옮기고 지정한 자리에 원소를 삽입하여 정렬하는 알고리즘이다. +즉, 두 번째 원소는 첫 번째 원소, 세 번째 원소는 두 번째와 첫 번째 원소, 네 번째 원소는 세 번째, 두 번째, 첫 번째 원소와 비교한 후 원소가 삽입될 위치를 찾는다. 원소가 삽입될 위치를 찾았다면 그 위치에 원소를 삽입하기 위해 원소를 한 칸씩 뒤로 이동시킨다. +처음 Key 값은 두 번째 원소부터 시작한다. +*/ + +const insertionSort = arr => { + const swap = (arr, curIdx, targetIdx) => { + const curVal = arr[curIdx]; + arr[curIdx] = arr[targetIdx]; + arr[targetIdx] = curVal; + }; + + const len = arr.length; + let cur; + for (let i = 1; i < len; i++) { + cur = i; + + while (arr[cur] < arr[cur - 1]) { + swap(arr, cur, cur - 1); + cur -= 1; + } + console.log(arr); + } +}; + +(() => { + const testCase = [ + [5, 4, 2, 6, 1, 9, 3], + [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + ]; + testCase.forEach(arr => { + insertionSort(arr); + console.log(arr); + }); +})(); From f334a1fdd775b07cfbf40e78fdc420f53bcf6ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Tue, 19 Jan 2021 17:30:01 +0900 Subject: [PATCH 4/6] CS: quick sort --- CS/Sort/kyu9341/quickSort.js | 56 ++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 CS/Sort/kyu9341/quickSort.js diff --git a/CS/Sort/kyu9341/quickSort.js b/CS/Sort/kyu9341/quickSort.js new file mode 100644 index 0000000..7712558 --- /dev/null +++ b/CS/Sort/kyu9341/quickSort.js @@ -0,0 +1,56 @@ +/* +Quick Sort + +아래 코드는 피벗 값을 첫 번째 원소로 설정했기 때문에 우측부터 확인하여 피벗 값보다 작은 원소를 찾는다. +- 작은 원소가 없으면 첫 번째 값까지 올 수 있도록 한다. +작은 원소를 찾았다면 이제 좌측부터 피벗 값보다 큰 원소를 찾는다. +- 이 때, 우측부터 찾았던 end 값보다 커지지 않도록 범위를 지정한다. +피벗 값보다 작은 값과 큰 값을 찾았다면 start, end의 값을 서로 교환한다. +start, end가 같아질 때까지 찾지 못했다면 해당 값과 피벗을 교환한다. +결과적으로 피벗 기준으로 왼쪽에는 피벗보다 작은 수, 오른쪽에는 피벗보다 큰 수가 남는다. + +*/ +const swap = (arr, curIdx, targetIdx) => { + const curVal = arr[curIdx]; + arr[curIdx] = arr[targetIdx]; + arr[targetIdx] = curVal; +}; + +const sortPartition = (arr, left, right) => { + const pivotIdx = left; + const pivot = arr[pivotIdx]; + + let start = left; + let end = right; + + while (start < end) { + while (arr[end] > pivot) end -= 1; + while (start < end && arr[start] <= pivot) start += 1; // 초기에는 arr[start] === pivot + + swap(arr, start, end); + } + + swap(arr, pivotIdx, start); + + return start; +}; + +const quickSort = (arr, left, right) => { + if (left >= right) return; + + const pivotIdx = sortPartition(arr, left, right); + quickSort(arr, left, pivotIdx - 1); + quickSort(arr, pivotIdx + 1, right); +}; + +(() => { + const testCase = [ + [5, 4, 2, 6, 1, 9, 3], + [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + ]; + + testCase.forEach(arr => { + quickSort(arr, 0, arr.length - 1); + console.log(arr); + }); +})(); From d5c0d201b67d155a430377942c45e3bba16b440f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Wed, 20 Jan 2021 17:16:03 +0900 Subject: [PATCH 5/6] CS: heap sort --- CS/Sort/kyu9341/heapSort.js | 79 +++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 CS/Sort/kyu9341/heapSort.js diff --git a/CS/Sort/kyu9341/heapSort.js b/CS/Sort/kyu9341/heapSort.js new file mode 100644 index 0000000..7536a02 --- /dev/null +++ b/CS/Sort/kyu9341/heapSort.js @@ -0,0 +1,79 @@ +/* +힙 정렬(Heap Sort)이란 최대 힙 트리나 최소 힙 트리를 구성해 정렬을 하는 방법으로서, 내림차순 정렬을 위해서는 최대 힙을 구성하고 오름차순 정렬을 위해서는 최소 힙을 구성하면 된다. 최대 힙을 구성하여 정렬하는 방법은 아래 예와 같다. + +n개의 노드에 대한 완전 이진 트리를 구성한다. 이때 루트 노드부터 부모노드, 왼쪽 자식노드, 오른쪽 자식노드 순으로 구성한다. +최대 힙을 구성한다. 최대 힙이란 부모노드가 자식노드보다 큰 트리를 말하는데, 단말 노드를 자식노드로 가진 부모노드부터 구성하며 아래부터 루트까지 올라오며 순차적으로 만들어 갈 수 있다. +가장 큰 수(루트에 위치)를 가장 작은 수와 교환한다. +2와 3을 반복한다. +출처 - 위키백과 +*/ + +function* range(start, end) { + for (let i = start; i <= end; i++) yield i; +} + +const swap = (arr, curIdx, targetIdx) => { + [arr[curIdx], arr[targetIdx]] = [arr[targetIdx], arr[curIdx]]; +}; + +const insertIntoHeap = (heap, value) => { + heap.push(value); + let index = heap.length - 1; + + while (index !== 0) { + const parent = Math.floor(index / 2); + if (heap[parent] < heap[index]) swap(heap, index, parent); + index = parent; + } +}; + +const deleteMax = heap => { + const top = heap[0]; + swap(heap, 0, heap.length - 1); + heap.pop(); + + let parent = 0; + let child = 1; + const lastIdx = heap.length - 1; + + const next = () => { + parent = child; + child = Math.floor(child * 2) + 1; + }; + + while (child <= lastIdx) { + if (child + 1 <= lastIdx && heap[child] < heap[child + 1]) child += 1; + if (heap[parent] < heap[child]) swap(heap, parent, child); + next(); + } + + return top; +}; + +const heapSort = arr => { + const heap = []; + + arr.forEach(val => { + insertIntoHeap(heap, val); + }); + + const heapSize = heap.length; + const result = []; + + for (const i of range(1, heapSize)) { + result.push(deleteMax(heap)); + } + + return result; +}; + +(() => { + const testCase = [ + [5, 4, 2, 6, 1, 9, 3], + [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + [5, 2, 43, 6, 7, 7, 1, 2], + ]; + testCase.forEach(arr => { + console.log(heapSort(arr)); + }); +})(); From dd206005fb40c0bc5583fbd9dc549ae63658ef5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=AF=E1=86=AB=E1=84=8B=E1=85=A7=E1=86=BC?= =?UTF-8?q?=E1=84=8B=E1=85=A5=E1=86=AB?= Date: Sat, 23 Jan 2021 18:02:37 +0900 Subject: [PATCH 6/6] CS: merge sort --- CS/Sort/kyu9341/mergeSort.js | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 CS/Sort/kyu9341/mergeSort.js diff --git a/CS/Sort/kyu9341/mergeSort.js b/CS/Sort/kyu9341/mergeSort.js new file mode 100644 index 0000000..a8786d3 --- /dev/null +++ b/CS/Sort/kyu9341/mergeSort.js @@ -0,0 +1,57 @@ +/* +합병 정렬 또는 병합 정렬(merge sort)은 O(n log n) 비교 기반 정렬 알고리즘이다. 일반적인 방법으로 구현했을 때 이 정렬은 안정 정렬에 속하며, 분할 정복 알고리즘의 하나이다. 존 폰 노이만이 1945년에 개발했다. 하향식 합병 정렬에 대한 자세한 설명과 분석은 1948년 초 헤르만 골드스타인과 폰 노이만의 보고서에 등장하였다. +합병 정렬은 다음과 같이 작동한다. + +1. 리스트의 길이가 1 이하이면 이미 정렬된 것으로 본다. 그렇지 않은 경우에는 +2. 분할(divide) : 정렬되지 않은 리스트를 절반으로 잘라 비슷한 크기의 두 부분 리스트로 나눈다. +3. 정복(conquer) : 각 부분 리스트를 재귀적으로 합병 정렬을 이용해 정렬한다. +4. 결합(combine) : 두 부분 리스트를 다시 하나의 정렬된 리스트로 합병한다. 이때 정렬 결과가 임시배열에 저장된다. +5. 복사(copy) : 임시 배열에 저장된 결과를 원래 배열에 복사한다. + +출처 - 위키백과 +*/ + +const mergeSort = (() => { + const sorted = []; + + const merge = (arr, left, mid, right) => { + let l = left; + let m = mid + 1; + let idx = left; + + while (l <= mid && m <= right) { + if (arr[l] <= arr[m]) sorted[idx++] = arr[l++]; + else sorted[idx++] = arr[m++]; + } + + while (m <= right) sorted[idx++] = arr[m++]; // 왼쪽이 먼저 삽입된 경우 + while (l <= mid) sorted[idx++] = arr[l++]; + + for (let i = left; i <= right; i++) arr[i] = sorted[i]; + }; + + return (divideAndMerge = (arr, left, right) => { + let mid; + + if (left < right) { + mid = Math.floor((left + right) / 2); + + divideAndMerge(arr, left, mid); + divideAndMerge(arr, mid + 1, right); + merge(arr, left, mid, right); + } + }); +})(); + +(() => { + const testCase = [ + [5, 4, 2, 6, 1, 9, 3], + [11, 10, 34, 5, 4, 2, 6, 1, 9, 3], + [5, 2, 43, 6, 7, 7, 1, 2], + ]; + + testCase.forEach(arr => { + mergeSort(arr, 0, arr.length - 1); + console.log(arr); + }); +})();