[알고리즘] 04. 선택구조
순차구조만으로는 "조건에 따라 다르게" 행동할 수 없다. 합격/불합격을 가르거나, 짝수/홀수를 판별하려면 갈림길이 필요하다. 이 갈림길이 선택구조다. 수업에서 Raptor로 푼 예제들을 단순 분기 → 중첩 분기 → 다중 분기 순으로 정리했다.
1. 선택구조란?
- 참·거짓을 가르는 조건식
- 조건이 참(Yes)일 때 실행할 명령 블록
- 조건이 거짓(No)일 때 실행할 명령 블록
Raptor에서는 마름모에 조건식을 쓰고, 참이면 왼쪽(Yes), 거짓이면 오른쪽(No) 경로로 분기한다. 짝수/홀수 판별을 순서도로 그리면 이렇다.

조건식에 쓰는 연산자는 두 종류다.
| 종류 | 연산자 | 예 |
|---|---|---|
| 관계 | = !=(/=) < <= > >= |
3 < 4 → Yes |
| 논리 | and or xor not |
(3<4) and (10<20) → Yes |
여러 조건을 조합할 땐 관계 연산자가 먼저 평가되고 논리 연산자가 나중에 평가된다(우선순위: 함수 → 괄호 → 거듭제곱 → 곱·나눗셈 → 덧·뺄셈 → 관계 → not → and → xor → or). 헷갈리면 괄호로 묶어 의도를 분명히 한다.
2. 중첩 선택 (예제 4-7: 이차방정식 판별)
조건 안에 또 조건을 두면 셋 이상으로 갈래를 나눌 수 있다. 이차방정식 ax²+bx+c=0의 판별식 d=b²-4ac 부호로 근의 종류를 가르는 예제가 중첩 선택의 전형이다.
GET a, b, c
d ← b^2 - 4 * a * c
IF d > 0
out ← "Different real roots" // 서로 다른 두 실근
ELSE
IF d < 0
out ← "Imaginary roots" // 허근
ELSE
out ← "Multiple root" // 중근 (d = 0)
PUT out
d>0이 아니면(거짓 경로) 그 안에서 다시 d<0인지 따져 허근/중근을 가른다. "큰 갈래를 먼저 나누고, 그 안에서 더 잘게 나눈다"는 게 중첩 선택의 사고법이었다. 직접 그려보니 마름모 안에 마름모가 들어가는 트리 모양이 됐다.
3. 다중 분기 사다리 (예제 4-8: 학점 판정)
같은 변수를 여러 구간으로 나눌 땐 IF ~ ELSE IF 사다리를 쓴다. 중간·기말 평균으로 A~F 학점을 매기는 예제다.
GET x, y
avg ← (x + y) / 2
IF avg >= 90 grade ← "A"
ELSE IF avg >= 80 grade ← "B"
ELSE IF avg >= 70 grade ← "C"
ELSE IF avg >= 60 grade ← "D"
ELSE grade ← "F"
PUT grade // 98, 96 → 평균 97 → A
여기서 순서가 핵심이다. avg>=70을 맨 위에 두면 97점도 거기서 먼저 걸려 C가 되어버린다. 큰(좁은) 조건부터 위에 두고, 한 번 걸리면 아래는 검사하지 않는다는 점을 직접 점수를 바꿔가며 확인했다.
4. 다중 분기 + 복합 조건 (예제 4-10: 급여 계산)
등급별 시급에 초과근무 가산까지 붙는 예제다. 다중 분기(등급)와 단일 분기(초과근무)가 한 문제에 섞여 있다.
GET grade, hours
IF grade = 1 cost ← 12000
ELSE IF grade = 2 cost ← 8000
ELSE cost ← 6000
IF hours > 50 // 50시간 초과분은 1.5배
overtime ← hours - 50
pay ← cost * 50 + cost * overtime * 1.5
ELSE
pay ← cost * hours
PUT "Pay="+pay // 2등급 60시간 → 520000
"등급으로 시급을 정하고, 근무시간으로 가산을 정한다"는 두 단계의 분기가 차례로 이어진다. 조건을 한 번에 다 처리하려 하기보다 단계로 쪼개니 흐름이 깔끔했다.
5. 직접 풀어볼 유제
- 유제 4-2: 입력 x가 1·2·3이면 각각 인사말, 그 외엔 "Try Again" (다중 분기)
- 유제 4-3: 구입 수량별 할인율(10~19개 20%, … 100개↑ 50%) 적용해 금액 계산 (구간 분기)
- 유제 4-7: 정수가 양수/음수/0이면 PLUS/MINUS/ZERO (중첩 선택)
- 유제 4-8: 구입액 구간별 할인(20만 미만 0% … 80만↑ 30%)으로 지불액 계산
- 유제 4-9: 네 수 중 최솟값 찾기 (세 수 비교를 한 단계 확장)
- 유제 4-10: 한우 등급·무게로 가격 계산 + 총액 구간별 추가 할인 (등급 분기 + 금액 구간 분기)
특히 유제 4-3·4-8은 "구간을 나누는 사다리"라, 학점 예제(4-8)의 순서 규칙을 그대로 적용하면 풀린다.
6. C 코드로 풀이 흐름 정리
선택구조는 조건을 한 줄로 압축하기보다, 먼저 계산할 값을 만들고 그 값을 기준으로 분기하면 디버깅이 쉽다.
예제 4-7: 이차방정식 판별
Raptor로 그리면 판별식 계산 뒤 마름모가 두 번 나온다. 먼저 d > 0을 판단하고, 거짓일 때만 다시 d < 0을 판단한다.
int iA;
int iB;
int iC;
int iDiscriminant;
printf("a b c 입력: ");
scanf("%d %d %d", &iA, &iB, &iC);
iDiscriminant = iB * iB - 4 * iA * iC;
if (iDiscriminant > 0)
{
printf("서로 다른 두 실근\n");
}
else if (iDiscriminant < 0)
{
printf("허근\n");
}
else
{
printf("중근\n");
}
| 입력 a,b,c | 판별식 | 출력 |
|---|---|---|
| 1, -3, 2 | 1 | 서로 다른 두 실근 |
| 1, 2, 1 | 0 | 중근 |
| 1, 1, 1 | -3 | 허근 |
유제: 구입 수량별 할인
구간 분기는 마름모가 사다리처럼 이어진다. 한 조건에서 Yes가 나오면 할인율을 정하고, No일 때만 다음 조건으로 내려간다.
int iCount;
int iUnitPrice;
int iTotalPrice;
int iDiscountRate;
int iPayPrice;
printf("수량과 단가 입력: ");
scanf("%d %d", &iCount, &iUnitPrice);
iTotalPrice = iCount * iUnitPrice;
iDiscountRate = 0;
if (iCount >= 100)
{
iDiscountRate = 50;
}
else if (iCount >= 50)
{
iDiscountRate = 30;
}
else if (iCount >= 10)
{
iDiscountRate = 20;
}
iPayPrice = iTotalPrice - iTotalPrice * iDiscountRate / 100;
printf("할인율: %d%%\n", iDiscountRate);
printf("지불액: %d\n", iPayPrice);
구간 분기는 큰 조건부터 내려오면 겹침을 줄이기 쉽다. 100개 이상, 50개 이상, 10개 이상 순서로 검사하면 한 입력이 정확히 한 구간에 걸린다.
오늘 느낀 점
- 선택구조의 핵심은 문법이 아니라 "조건을 어떤 순서로 세우느냐"였다. 학점 예제에서 순서를 뒤집어 97점이 C로 나오는 걸 직접 보고 확실히 박혔다.
- 이차방정식 판별처럼 "큰 갈래를 먼저, 그 안에서 잘게" 나누는 중첩 선택이 트리 모양으로 그려지는 게 흥미로웠다. 분기를 단계로 쪼개니 복잡한 문제도 풀렸다.
- 급여 예제처럼 다중 분기와 단일 분기가 섞여도, 한 번에 처리하려 하지 말고 단계로 끊으니 흐름이 깔끔해졌다.
한 걸음 더
IF~ELSE IF사다리에서 조건 순서가 결과를 가른다. 구간을 나눌 땐 "넓은 범위부터 좁혀 내려갈지, 좁은 범위부터 넓혀 올라갈지" 한 방향으로 일관되게 배치해야 빠짐·겹침이 없다. 모든 입력이 정확히 하나의 가지로 가는지 점검하는 습관이 버그를 줄인다.- 패스워드 비교(유제 4-5)처럼 "1234"(문자열)와 1234(숫자)를 구분해야 하는 경우가 있다. 앞자리 0이나 비교 방식 때문에 자료형을 잘못 잡으면 같은 값인데도 다르다고 판정될 수 있다. "무엇을 어떤 타입으로 비교하는가"를 늘 의식하자.
- 중첩 선택이 깊어지면 읽기 어려워진다. 그래서 실제 코드에서는 깊은 중첩 대신, 조건을 논리 연산자로 묶거나(
a and b) 이른 반환(early return)으로 평평하게 펴는 기법을 쓴다. Raptor의 트리형 분기를 코드로 옮길 때 한 번쯤 고민해볼 지점이다.