🗒️ 플로우차트


1️⃣ 완전 탐색
1. 개념
- 가능한 모든 경우를 전부 다 해보는 방법
2. 예시
1) 비밀번호 찾기
- 비밀번호 4자리 숫자 / 0000 ~ 9999
- 9999 까지 다 입력해서 찾기 (느리지만 무조건 정답을 찾음)
2) 쇼핑몰 최저가 찾기
- 모든 쇼핑몰 가격 전부 비교 > 가장 싼 곳 선택
3. 장단점
1) 장점
- 생각하기 쉽다
- 구현이 단순함
- 정답을 절대 놓치지 않음
2) 단점
- 경우의 수가 많아지면 느림
- 숫자가 조금만 커져도 시간 폭발
- 문제가 작을 때만 쓰기 좋음
4. 반복문을 이용한 완전 탐색
1) 구현 방법
- for문 / while문
- 주어진 범위나 조건 파악 > 범위 내의 모든 경우를 반복문으로 확인
2) 대표적인 문제 예시: 두 수의 합이 target인 경우 찾기
- 문제 분석 및 풀이 아이디어
더보기
문제: 배열안에 두 수를 선택해서 두 수의 합이 target이 되는 조합을 찾기
- 목표: 주어진 배열에서 합이 target이 되는 두 수의 인덱스 반환
- 입력: 정수 배열과 목표값(target)
- 출력: 두 수의 인덱스를 담은 배열
- 조건:
1. 같은 요소를 두 번 사용할 수 없음
2. 답이 없는 경우 빈 배열 반환
접근 방법: 완전 탐색
풀이:
1. 배열 순회를 통해 첫 번째 수 선택 단계
1.1. 배열의 첫 번째 요소부터 순차적으로 선택
1.2. 선택한 수를 첫 번째 수로 지정
2. 두 번째 수 선택 및 검증 단계
2.1. 첫 번째 수 다음 인덱스부터 배열 끝까지 순회
2.2. 현재 선택된 두 수의 합이 target과 같은지 확인
2.3. target과 같다면 두 수의 인덱스 반환
2.4. target과 다르다면 다음 수 선택하여 2.2로 돌아가기
3. 결과 반환 단계
3.1. 조합을 찾은 경우 인덱스 배열 반환
3.2. 찾지 못한 경우 빈 배열 반환
시간복잡도:
1. 첫 번째 수 선택: O(N)
2. 두 번째 수 선택 및 검증: O(N)
3. 결과 반환: O(1)
→ 최종 시간복잡도: 이중 for문이므로 O(N²)
public class Solution {
public static int[] findPairs(int[] numbers, int target) {
// 1. 첫 번째 수 선택 단계
for (int i = 0; i < numbers.length; i++) {
// 1.1. 배열의 첫 번째 요소부터 순차적으로 선택
// 1.2. 선택한 수를 첫 번째 수로 지정
// 2. 두 번째 수 선택 및 검증 단계
// 2.1. 첫 번째 수 다음 인덱스부터 배열 끝까지 순회
for (int j = i + 1; j < numbers.length; j++) {
// 2.2. 현재 선택된 두 수의 합이 target과 같은지 확인
if (numbers[i] + numbers[j] == target) {
// 2.3. target과 같다면 두 수의 인덱스 반환
return new int[]{i, j};
}
// 2.4. target과 다르다면 다음 수 선택하여 2.2로 돌아가기
}
}
// 3. 결과 반환 단계
// 3.1. 조합을 찾은 경우 위에서 이미 반환
// 3.2. 찾지 못한 경우 빈 배열 반환
return new int[]{};
}
public static void main(String[] args) {
// 테스트용 입력값 설정
int[] numbers = {2, 11, 7, 15};
int target = 9; // 예상 결과: [0, 2] (2 + 7 = 9)
// findPairs 메서드 실행
int[] result = findPairs(numbers, target);
// 결과 출력 및 검증
if (result.length == 0) {
System.out.println("합이 " + target + "이 되는 두 수를 찾을 수 없습니다.");
} else {
System.out.printf("찾은 인덱스: [%d, %d]\n", result[0], result[1]);
System.out.printf("해당 값: %d + %d = %d\n",
numbers[result[0]], numbers[result[1]], target);
}
}
}
- 배열에서 두 개를 뽑아서 합이 target이 되는지 전부 확인
첫 번째 숫자 하나 고르고
→ 두 번째 숫자 전부 다 붙여보기
> 이중 for문 = O(N2제곱)
2️⃣ 그리디 알고리즘
1. 개념
- 지금 당장 제일 좋아 보이는 선택을 하는 방법
2. 예시
1) 거스름돈
상황: 거스름돈 1260원을 최소한의 동전으로 거슬러주기
고려사항: 500원, 100원, 50원, 10원 동전 사용
그리디 방식: 가장 큰 단위의 동전부터 최대한 사용하기
처리 결과
- 500원: 2개 (1000원)
- 100원: 2개 (200원)
- 50원: 1개 (50원)
- 10원: 1개 (10원)
> 최종 결과: 총 6개의 동전 사용
- 제일 큰 동전부터 씀 / 총 동전 개수 최소 / 빠르고 간단
2) 회의실 배정
상황: 1개의 회의실을 다수의 팀이 시간대별로 사용하고자 할 때,
최대한 많은 회의를 진행하기
예시 상황:
- 회의 개수 = 8
- 회의 시간 목록 =
1번팀: 1시 ~ 4시
2번팀: 3시 ~ 5시
3번팀: 0시 ~ 6시
4번팀: 5시 ~ 7시
5번팀: 3시 ~ 8시
6번팀: 5시 ~ 9시
7번팀: 6시 ~ 10시
8번팀: 8시 ~ 11시
그리디 방식:
1. 회의 종료 시각이 빠른 순서로 정렬하기
2. 이전 회의 종료 시각 이후에 시작하는 회의 중 가장 일찍 끝나는 회의 선택하기
처리 결과:
- 1번팀: 1시~4시 선택
- 4번팀: 5시~7시 선택
- 8번팀: 8시~11시 선택
> 최종 결과: 총 3개의 회의 진행 가능
- 빨리 끝나는 회의부터 선택 > 회의를 더 많이 넣을 수 있음
3. 장단점
1) 장점
- 빠르다
- 구현이 간단하다
- 직관적이다
2) 단점
- 항상 정답은 아님
- 아무 문제에나 쓰면 망함 = 적용할 수 있는 유형 한정적 > 조건 중요
4. 필요 조건
1) 탐욕 선택 속성
- 지금 선택한 게 나중에 봐도 틀리지 않아야 함
- 예시) 회의실 문제 > 최대 회의 수 목표 달성
2) 최적 부분 구조
- 작은 문제의 정답들이 모이면 큰 문제의 정답이 된다
- 예시) 거스름돈 문제 > 최소 동전 개수 사용
3) 두 조건의 차이점
- 탐욕 선택 속성: 문제 해결 과정에서 현재 선택의 정당성 보장 중요
> 각각의 선택에 초점 - 최적 부분 구조: 문제를 분할하여 최적해를 조합할 수 있는 구조인지 확인
> 문제의 구조에 초점 - 두 개 모두 조건을 만족해야 그리디 사용 가능
3️⃣ 완전 탐색 VS 그리디
1. 특징 비교
| 구분 | 완전 탐색 | 그리디 |
| 방법 | 전부 다 해봄 | 지금 최선만 선택 |
| 속도 | 느림 | 빠름 |
| 정답 보장 | 항상 가능 | 조건부 가능 |
| 사용 시기 | 문제 작을 때 | 구조가 맞을 때 |
2. 문제 풀이로 비교(거스름돈 문제)
1) 공통상황
- 철수가 거스름 돈을 받아야 함 / 최소 동전 개수 구하기
- 입력: 사용할 수 있는 동전의 종류: 500원, 100원, 50원, 10원
첫째 줄에 거슬러 주어야 할 금액 N이 주어집니다. (0 ≤ N ≤ 10,000) - 출력: 거스름돈 N원을 만들기 위한 최소 동전 개수를 출력
2) 그리디 알고리즘 풀이
- 큰 동전부터 사용 / 항상 정답이 아닐 수 있음
더보기
접근방법:
- 가장 큰 단위의 동전부터 최대한 거스름돈 주기(그리디)
1. 초기화 단계
1.1. 동전을 큰 단위부터 작은 단위 순으로 정렬
1.2. 거스름돈 금액을 저장할 변수 준비
1.3. 사용된 동전 개수를 저장할 변수 초기화
2. 거스름돈 계산 단계
2.1. 가장 큰 단위의 동전부터 순차적으로 진행
2.2. 현재 동전으로 거스름돈을 최대한 많이 거슬러줌
2.3. 남은 거스름돈에 대해 다음으로 큰 동전으로 반복
3. 결과 반환 단계
3.1. 남은 금액이 0이면 사용된 총 동전 개수 반환
3.2. 남은 금액이 0이 아니면, -1 반환
시간복잡도: O(K) - K은 동전의 종류 수
import java.util.*;
public class Solution {
public static int coinChange(int[] coins, int target) {
// 1. 초기화 단계
// 1.1. 동전을 큰 단위부터 작은 단위 순으로 정렬
Arrays.sort(coins);
// 1.2. 거스름돈 금액을 저장할 변수 초기화
int remainingAmount = target;
// 1.3. 사용된 동전 개수를 저장할 변수 초기화
int coinCount = 0;
// 2. 큰 동전부터 사용하는 단계
// 2.1. 가장 큰 단위의 동전부터 순차적으로 진행
for (int i=coins.length-1; i>=0; i--){
// 2.2. 현재 동전으로 거스름돈을 최대한 많이 거슬러줌
coinCount += remainingAmount / coins[i]; // 현재 동전으로 거슬러 줄 수 있는 개수
remainingAmount %= coins[i]; // 남은 금액 계산
// 2.3. 남은 거스름돈에 대해 다음으로 큰 동전으로 반복
}
// 3. 결과 반환 단계
// 3.1. 사용된 동전 개수 반환
return coinCount;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int[] coins = {500, 100, 50, 10};
// 거스름돈 금액 입력 받기
int target = scanner.nextInt();
// 결과 출력
System.out.println(coinChange(coins, target));
}
}
3) 완전 탐색 풀이
- 모든 경우를 다 해봐서 제일 적은 동전 개수 찾기
더보기
1. 초기화 단계
1.1. 각 동전 별로 사용 가능한 최대 개수 계산
- 목표 금액 / 동전 금액 = 최대 사용 가능 개수
예) 500원짜리는 최대 몇개, 100원짜리는 최대 몇개...
1.2. 최소 동전 개수를 저장할 변수를 최댓값으로 초기화
2. 모든 조합 시도 단계 (4중 for문 사용)
2.1. 500원: 0개부터 최대 개수까지 반복
2.2. 100원: 0개부터 최대 개수까지 반복
2.3. 50원: 0개부터 최대 개수까지 반복
2.4. 10원: 0개부터 최대 개수까지 반복
각 조합에 대해:
- 현재 조합으로 만들어지는 총 금액 계산
- 목표 금액과 일치하는지 확인
- 일치하면 사용된 동전 개수(현재 4중 for문의 각 인덱스 합)와
현재 최소값 비교하여 갱신
3. 결과 반환 단계
3.1. 찾은 최소 동전 개수 반환
3.2. 해를 찾지 못한 경우 -1 반환
시간복잡도: O(n₁ × n₂ × n₃ × n₄)
- 각 n은 각 동전으로 만들 수 있는 최대 개수입니다.
예를 들어 2000원을 만들 때:
500원: 최대 4개 (n₁ = N//500)
100원: 최대 20개 (n₂ = N//100)
50원: 최대 40개 (n₃ = N//50)
10원: 최대 200개 (n₄ = N//10)
최종 시간 복잡도: O(N^4)
public static int coinChange(int[] coins, int target) {
// 1. 초기화 단계
// 1.1. 각 동전 별로 사용 가능한 최대 개수 계산
// - 목표 금액 / 동전 금액 = 최대 사용 가능 개수
int[] maxCounts = new int[coins.length];
for (int i = 0; i < coins.length; i++) {
maxCounts[i] = target / coins[i];
}
// 1.2. 최소 동전 개수를 저장할 변수를 최댓값으로 초기화
int minCoins = Integer.MAX_VALUE;
// 2. 모든 조합 시도 단계 (4중 for문 사용)
// 2.1. 500원: 0개부터 최대 개수까지 반복
for (int i = 0; i <= maxCounts[0]; i++) {
// 2.2. 100원: 0개부터 최대 개수까지 반복
for (int j = 0; j <= maxCounts[1]; j++) {
// 2.3. 50원: 0개부터 최대 개수까지 반복
for (int k = 0; k <= maxCounts[2]; k++) {
// 2.4. 10원: 0개부터 최대 개수까지 반복
for (int l = 0; l <= maxCounts[3]; l++) {
// 각 조합에 대해:
// - 현재 조합으로 만들어지는 총 금액 계산
int currentSum = (coins[0] * i) + (coins[1] * j) +
(coins[2] * k) + (coins[3] * l);
// - 목표 금액과 일치하는지 확인
if (currentSum == target) {
// - 일치하면 사용된 동전 개수(현재 4중 for문의 각 인덱스 합)와
// 현재 최소값 비교하여 갱신
minCoins = Math.min(minCoins, i + j + k + l);
}
}
}
}
}
// 3. 결과 반환 단계
// 3.1. 찾은 최소 동전 개수 반환
// 3.2. 해를 찾지 못한 경우 -1 반환
return minCoins == Integer.MAX_VALUE ? -1 : minCoins;
}
4) 풀이 비교
| 구분 | 그리디 | 완전 탐색 |
| 생각 방법 | 지금 제일 좋은 선택 | 다 해봄 |
| 경우의 수 | 거의 없음 | 엄청 많음 |
| 속도 | 빠름 | 느림 |
| 정답 보장 | 조건 맞을 때만 | 항상 보장 |
| 코딩 테스트 | 자주 사용 | 위험 |
'🔌 SPARTA > Courses' 카테고리의 다른 글
| Spring 시작하기 (1) | 2026.01.29 |
|---|---|
| 배열과 컬렉션 (1) | 2026.01.28 |
| 알고리즘 개념 (1) | 2026.01.27 |
| 걷기반 4회차: 상속과 제네릭, enum (1) | 2026.01.23 |
| 걷기반 3회차: 생성자, this, Order (0) | 2026.01.21 |