정렬 알고리즘은 컴퓨터 과학에서 가장 기본적이고 중요한 개념 중 하나이다. 특히, 삽입 정렬은 이해하기 쉽고 직관적인 알고리즘으로, 다양한 상황에서 응용될 수 있다. 이번 글에서는 백준의 “알고리즘 수업 - 삽입 정렬 1” 문제를 통해 삽입 정렬의 동작 방식을 자세히 살펴보고, 주어진 문제를 효과적으로 해결하는 방법을 알아보고자 한다.
문제 : https://www.acmicpc.net/problem/24051
문제 설명
백준 문제 24051번 “알고리즘 수업 - 삽입 정렬 1"은 N개의 서로 다른 양의 정수가 저장된 배열 A가 주어진다. 이 배열을 삽입 정렬을 사용하여 오름차순으로 정렬할 때, 배열 A에 K번째로 저장되는 수를 구하는 문제이다.
삽입 정렬의 기본적인 동작은 배열의 두 번째 원소부터 시작하여 현재 원소를 적절한 위치에 삽입함으로써 정렬을 진행하는 것이다. 문제에서 요구하는 것은 삽입 정렬 과정에서 배열에 저장되는 모든 연산 중 K번째로 저장되는 값을 찾는 것이다. 만약 저장 횟수가 K보다 작으면 -1을 출력해야 한다.
예를 들어, 배열 A가 [4, 5, 1, 3, 2]이고 K가 7이라면, 삽입 정렬 과정을 따라가며 저장되는 값을 세어 K번째 저장된 값을 출력해야 한다. 이 경우, K번째 저장된 값은 5이다. 반면, K가 저장 횟수보다 큰 경우에는 -1을 출력해야 한다.
접근 방식
이 문제를 해결하기 위해서는 삽입 정렬의 과정을 정확히 시뮬레이션하면서 저장되는 모든 연산을 추적해야 한다. 삽입 정렬은 각 단계에서 현재 원소를 적절한 위치에 삽입하기 위해 이전 원소들을 이동시키는 과정을 포함한다. 이때, 원소를 이동시킬 때마다 배열에 저장 연산이 발생하며, 이를 카운트해야 한다.
구체적으로, 다음과 같은 접근 방식을 사용한다:
- 초기 설정: 배열 A와 저장 횟수 K를 입력받는다. 저장 횟수를 세기 위한 카운터를 초기화한다.
- 삽입 정렬 시뮬레이션: 두 번째 원소부터 시작하여 각 원소를 적절한 위치에 삽입한다.
- 현재 원소를
newItem
으로 설정하고, 이전 원소들과 비교하면서 이동시킨다. - 이전 원소가
newItem
보다 크면 한 칸씩 이동시키고, 이동할 때마다 저장 연산을 카운트한다. - 적절한 위치를 찾았을 때
newItem
을 삽입하고, 이 또한 저장 연산을 카운트한다.
- 현재 원소를
- K번째 저장 확인: 저장 연산이 K번째에 도달하면 해당 값을 출력하고 종료한다.
- 결과 출력: 전체 삽입 정렬 과정이 끝났는데도 저장 횟수가 K보다 작으면 -1을 출력한다.
이러한 과정을 통해 삽입 정렬의 모든 저장 연산을 추적하고, K번째 저장된 값을 정확히 찾을 수 있다.
C++ 코드와 설명
|
|
코드의 동작 단계별 설명
- 입력 처리: 먼저 배열의 크기 N과 저장 횟수 K를 입력받고, 배열 A의 원소들을 입력받는다.
- 삽입 정렬 시작: 두 번째 원소부터 시작하여 배열을 정렬해 나간다.
- 원소 이동 및 저장 연산 카운트:
- 현재 삽입할 원소
newItem
을 설정하고, 이전 원소들과 비교하면서newItem
보다 큰 원소를 오른쪽으로 이동시킨다. - 원소를 이동시킬 때마다 저장 연산이 발생하므로
count
를 증가시킨다. - 만약
count
가 K와 같아지면 현재 저장된 값을 출력하고 프로그램을 종료한다.
- 현재 삽입할 원소
- 원소 삽입: 적절한 위치를 찾았다면
newItem
을 삽입하고, 이 또한 저장 연산이 발생하므로count
를 증가시킨다. K와 같아지면 값을 출력하고 종료한다. - 결과 출력: 모든 삽입 정렬 과정을 마쳤음에도
count
가 K보다 작으면 -1을 출력한다.
C++ without library 코드와 설명
|
|
코드의 동작 단계별 설명
- 입력 처리: 배열의 크기 N과 저장 횟수 K를 입력받고, 배열 A의 원소들을 동적 할당을 통해 입력받는다.
- 삽입 정렬 시작: 두 번째 원소부터 시작하여 배열을 정렬해 나간다.
- 원소 이동 및 저장 연산 카운트:
- 현재 삽입할 원소
newItem
을 설정하고, 이전 원소들과 비교하면서newItem
보다 큰 원소를 오른쪽으로 이동시킨다. - 원소를 이동시킬 때마다 저장 연산이 발생하므로
count
를 증가시킨다. - 만약
count
가 K와 같아지면 현재 저장된 값을 출력하고 동적 할당된 메모리를 해제한 후 프로그램을 종료한다.
- 현재 삽입할 원소
- 원소 삽입: 적절한 위치를 찾았다면
newItem
을 삽입하고, 이 또한 저장 연산이 발생하므로count
를 증가시킨다. K와 같아지면 값을 출력하고 동적 할당된 메모리를 해제한 후 종료한다. - 결과 출력: 모든 삽입 정렬 과정을 마쳤음에도
count
가 K보다 작으면 -1을 출력하고 메모리를 해제한다.
Python 코드와 설명
|
|
코드의 동작 단계별 설명
- 입력 처리: 표준 입력을 통해 배열의 크기 N과 저장 횟수 K를 입력받고, 배열 A의 원소들을 리스트로 저장한다.
- 삽입 정렬 시작: 두 번째 원소부터 시작하여 배열을 정렬해 나간다.
- 원소 이동 및 저장 연산 카운트:
- 현재 삽입할 원소
newItem
을 설정하고, 이전 원소들과 비교하면서newItem
보다 큰 원소를 오른쪽으로 이동시킨다. - 원소를 이동시킬 때마다 저장 연산이 발생하므로
count
를 증가시킨다. - 만약
count
가 K와 같아지면 현재 저장된 값을 출력하고 프로그램을 종료한다.
- 현재 삽입할 원소
- 원소 삽입: 적절한 위치를 찾았다면
newItem
을 삽입하고, 이 또한 저장 연산이 발생하므로count
를 증가시킨다. K와 같아지면 값을 출력하고 종료한다. - 결과 출력: 모든 삽입 정렬 과정을 마쳤음에도
count
가 K보다 작으면 -1을 출력한다.
결론
백준의 “알고리즘 수업 - 삽입 정렬 1” 문제는 삽입 정렬의 기본적인 동작 방식을 이해하고, 이를 정확히 시뮬레이션할 수 있는지를 평가하는 문제였다. 삽입 정렬의 각 단계에서 발생하는 저장 연산을 세는 과정을 통해 문제를 해결할 수 있었으며, 이를 통해 정렬 알고리즘의 세부 동작을 더욱 명확히 이해할 수 있었다.
이번 문제를 풀면서 시뮬레이션을 정확히 구현하는 것이 중요함을 다시 한 번 깨달았다. 또한, 효율적인 코드 작성을 통해 시간 복잡도를 관리하는 것도 중요한 요소임을 확인할 수 있었다. 추가적인 최적화 방안으로는 불필요한 조건 검사나 반복을 줄이는 방법을 고려할 수 있으며, 특히 큰 입력에 대해서도 안정적으로 동작할 수 있도록 코드를 작성하는 것이 필요하다.
삽입 정렬은 비록 시간 복잡도가 O(N²)이지만, 작은 크기의 데이터나 거의 정렬된 데이터에 대해서는 효율적으로 동작할 수 있는 유용한 알고리즘임을 재확인할 수 있었다. 앞으로도 다양한 정렬 알고리즘을 학습하고, 상황에 맞는 최적의 알고리즘을 선택하여 문제를 해결하는 능력을 키워나가야겠다.