[면접준비] 정렬 알고리즘을 설명해 주세요. (24/04/11)

2024. 4. 11. 08:53공부/면접 준비

  • 선택 정렬(Selection Sort)

특징: 배열의 각 위치에 대해 나머지 부분에서 최소값을 찾아 위치를 교환한다.
장점: 구현이 간단하다.
단점: 시간 복잡도 O(n^2), 대규모 데이터셋에 비효율적이다.
메모리 사용: 추가 메모리 사용이 거의 없음 (In-place).

function selectionSort(arr) {
    for (let i = 0; i < arr.length; i++) {
        let minIdx = i;
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        [arr[i], arr[minIdx]] = [arr[minIdx], arr[i]];
    }
    return arr;
}
  • 버블 정렬(Bubble Sort)

특징: 인접한 요소를 비교하고 필요할 경우 교환한다.
장점: 구현이 매우 간단하다.
단점: 시간 복잡도 O(n^2) , 비효율적이며 느리다.
메모리 사용: 추가 메모리 사용이 거의 없음 (In-place)

function bubbleSort(arr) {
    let n = arr.length;
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}
  • 병합 정렬(Merge Sort)

특징: 분할 정복 방법을 사용하여 배열을 반으로 나누고, 정렬 후 합병한다.
장점: 시간 복잡도 O(nlogn), 대규모 데이터셋에서도 효율적이다.
단점: 추가적인 메모리가 필요하다.
메모리 사용: 배열 크기의 절반 정도의 추가 공간 필요.

function mergeSort(arr) {
    if (arr.length <= 1) {
        return arr;
    }

    const middle = Math.floor(arr.length / 2);
    const left = arr.slice(0, middle);
    const right = arr.slice(middle);

    return merge(mergeSort(left), mergeSort(right));
}

function merge(left, right) {
    let result = [];
    let indexLeft = 0;
    let indexRight = 0;

    while (indexLeft < left.length && indexRight < right.length) {
        if (left[indexLeft] < right[indexRight]) {
            result.push(left[indexLeft]);
            indexLeft++;
        } else {
            result.push(right[indexRight]);
            indexRight++;
        }
    }

    return result.concat(left.slice(indexLeft)).concat(right.slice(indexRight));
}
  • 삽입 정렬(Insertion Sort)

특징: 각 요소를 이미 정렬된 배열 부분에 적절한 위치에 삽입한다.
장점: 구현이 간단하고, 작은 데이터셋에서 효율적이다.
단점: 시간 복잡도 O(n^2), 큰 데이터셋에서 비효율적이다.
메모리 사용: 추가 메모리 사용이 거의 없음 (In-place).

function insertionSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        let key = arr[i];
        let j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
    return arr;
}
  • 퀵 정렬(Quick Sort)

특징: 분할 정복 방법을 사용하고, 피벗을 기준으로 부분 배열을 정렬한다.
장점: 평균적인 시간 복잡도 O(nlogn), 빠른 속도.
단점: 최악의 경우 시간 복잡도 O(n 2), 피벗 선택에 따라 성능 차이 큼.
메모리 사용: 추가 메모리 사용이 거의 없음 (In-place).

function quickSort(arr, low, high) {
    if (low < high) {
        let pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
    return arr;
}

function partition(arr, low, high) {
    let pivot = arr[high];
    let i = low - 1;
    for (let j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
    }
    [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
    return i + 1;
}
  • 힙 정렬(Heap Sort)

특징: 힙 자료구조를 이용하여 최대 또는 최소 힙을 구성하고 정렬한다.
장점: 시간 복잡도 O(nlogn), 추가 메모리 사용이 적다.
단점: 실제 사용에서 퀵 정렬보다 느릴 수 있고, 코드가 복잡하다.
메모리 사용: 추가 메모리 사용이 거의 없음 (In-place).

function heapSort(arr) {
    let n = arr.length;
    for (let i = Math.floor(n / 2 - 1); i >= 0; i--) {
        heapify(arr, n, i);
    }
    for (let i = n - 1; i > 0; i--) {
        [arr[0], arr[i]] = [arr[i], arr[0]];
        heapify(arr, i, 0);
    }
    return arr;
}

function heapify(arr, n, i) {
    let largest = i;
    let left = 2 * i + 1;
    let right = 2 * i + 2;
    if (left < n && arr[left] > arr[largest]) {
        largest = left;
    }
    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }
    if (largest != i) {
        [arr[i], arr[largest]] = [arr[largest], arr[i]];
        heapify(arr, n, largest);
    }
}