CS
[알고리즘] 문제 해결 패턴 - 기본: Divied and Conquer
seo dori
2023. 8. 28. 11:23
3. Divied and Conquer
분할 정복(Divide and Conquer)"은 어려운 문제를 해결하기 위해 문제를 더 작은 부분으로 나누고, 나눠진 부분을 각각(재귀적으로) 해결하여 원래 문제의 해답을 얻는 알고리즘 디자인 패러다임입니다. 이 패러다임은 문제를 작은 부분으로 쪼개서 해결하는 것을 강조
가장 대표적인 예로 “Binary Search”가 있습니다. 이 외에도 Merge Sort(병합 정렬), Quick Sort(퀵 정렬), Binary Search(이진 검색) 등이 있습니다.
일반적으로 "분할", "정복", "결합" 단계로 구성됩니다.
- 분할(Divide): 복잡한 문제를 해결하기 쉬운 작은 부분으로 나눕니다. 이 단계에서는 문제를 더 작은 부분으로 분해하고, 각 부분에 대한 해법을 찾습니다.
- 정복(Conquer): 나눈 작은 부분 문제들을 각각 해결합니다. 이 단계에서는 작은 부분 문제들을 재귀적으로 해결하거나 다른 알고리즘을 사용하여 해결합니다.
- 결합(Combine): 작은 부분 문제들의 해답을 결합하여 원래 문제의 해답을 얻습니다. 이 단계에서는 작은 부분 문제들의 결과를 합치거나 조합하여 원래 문제의 해답을 얻습니다.
대표적인 문제 예시
LeetCode - Maximum Subarray
: 주어진 정수 배열에서 연속된 부분 배열 중 가장 큰 합을 구하는 문제입니다.
data example 1
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1]
data example 2
Input: nums = [1]
Output: 1
data example 3
Input: nums = [5,4,-1,7,8]
Output: 23
data example 4
Input: nums = [-1]
Output: -1
Bad Solution : O(n^2)
하나의 접근 방법은 모든 가능한 부분 배열의 합을 구하고, 그 중 가장 큰 값을 찾는 것입니다. 하지만 이렇게 하면 시간 복잡도가 O(n^2)가 됩니다.
function maxSubArrayBad(nums) {
let maxSum = -Infinity;
for (let start = 0; start < nums.length; start++) {
let sum = 0;
for (let end = start; end < nums.length; end++) {
sum += nums[end];
maxSum = Math.max(maxSum, sum);
}
}
return maxSum;
}
알고리즘 적용 : O(n logn)
분할 정복 알고리즘을 활용하여 문제를 효율적으로 풀 수 있습니다. 배열을 반으로 나누어 왼쪽, 오른쪽, 중앙을 고려하여 부분 배열의 최대 합을 구하고 이를 합쳐서 전체 최대 합을 찾습니다
function maxSubArray(nums) {
if (nums.length === 1) {
return nums[0];
}
const mid = Math.floor(nums.length / 2);
const leftMax = maxSubArray(nums.slice(0, mid));
const rightMax = maxSubArray(nums.slice(mid));
const crossMax = maxCrossingSubArray(nums, mid);
return Math.max(leftMax, rightMax, crossMax);
}
function maxCrossingSubArray(nums, mid) {
let leftSum = -Infinity;
let sum = 0;
for (let i = mid - 1; i >= 0; i--) {
sum += nums[i];
leftSum = Math.max(leftSum, sum);
}
let rightSum = -Infinity;
sum = 0;
for (let i = mid; i < nums.length; i++) {
sum += nums[i];
rightSum = Math.max(rightSum, sum);
}
return leftSum + rightSum;
}