삼성전자 코딩테스트 — 자주 나오는 구현 모듈 (Java) 정리

📅 삼성 코테 준비하면서 반복적으로 등장하는 구현 모듈들을 Java 로 정리. 코드트리의 삼성 기출을 기준으로 작성.

함께 보면 좋은 글 — 5회 응시 후기:

참고: 코드트리 — 삼성 기출 모음


1. 회전시키기 — 90° 회전 (시계 / 반시계)

코드트리의 메이즈러너 · 예술성 문제에서 일부 좌표를 90° 회전해야 한다.

⚠️ 전체 격자가 아니라 부분 영역 회전이 나오는 게 핵심. start/end 좌표를 기준으로 상대 좌표를 잡아야 함.

int N = 5;
int[][] map = new int[N][N];
for (int i = 0; i < 5; i++) {
    map[i][0] = 1;
}

// 기존 map 이 있다고 가정.
int start_x = 0, end_x = N;
int start_y = 0, end_y = N;

// 90° 시계방향 회전
int[][] newMap = new int[N][N];
for (int i = start_x; i < end_x; i++) {
    for (int j = start_y; j < end_y; j++) {
        newMap[start_x + (j - start_y)][(end_y - 1) - (i - start_x)] = map[i][j];
    }
}

반시계 방향은 인덱스 swap 패턴만 반대로:

newMap[(end_x - 1) - (j - start_y)][start_y + (i - start_x)] = map[i][j];

2. Periodic Boundary Condition (주기 경계)

경계를 넘어가는 순간 그 다음 경계로 wrap-around. 코드트리 '포탑 부수기' 문제.

⚠️ index 를 0 부터 잡느냐 1 부터 잡느냐로 식이 달라진다.

1칸씩 step-by-step

// 0 ~ N-1 indexing
int new_x = (N + (x + dx[dir])) % N;
int new_y = (M + (y + dy[dir])) % M;

// 1 ~ N indexing (밖에 1을 더해주고 내부에서 1을 빼준다)
int new_x = (N + ((x - 1) + dx[dir])) % N + 1;
int new_y = (M + ((y - 1) + dy[dir])) % M + 1;

한 번에 크게 움직임

int distance = 150000000;

// 0 ~ N-1 indexing
int new_x = Math.abs((N + (x + distance))) % N;
int new_y = Math.abs((M + (y + distance))) % M;

// 1 ~ N indexing
int new_x = Math.abs((N + ((x - 1) + distance))) % N + 1;
int new_y = Math.abs((M + ((y - 1) + distance))) % M + 1;

Math.abs 안 쓰면 음수 좌표 들어왔을 때 깨짐. 음수 처리 + N 더해서 양수 만들고 mod 패턴.

3. 방향 거꾸로 바꾸는 문제

대표적으로 '주사위 문제 3' · '토끼와 경주' 가 있다. 이 부분은 이해 후 암기가 필요하다 — 방향 인덱스 매핑이 문제마다 살짝 다름.

주사위는 6 면 각각의 회전 후 윗면·아랫면 매핑이 정해져 있고, 토끼 경주는 격자 위 방향 dir 의 반대 방향이 (dir + 2) % 4 같은 패턴.

4. Sort — 다중 조건

최근 빈번하게 4–5 개 조건 으로 정렬 문제가 출제됨.

Integer — 표준 Comparator

Collections.sort(list, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) { // 양수면 swap, 음수면 유지
        if (o1 < o2) return 1;       // 내림차순
        else if (o1 == o2) return 0;
        else return -1;
    }
});

더 간단하게: return o1 - o2; (오름차순) 또는 return o2 - o1; (내림차순).

Long type — compareTo 권장

⚠️ Long 은 직접 빼면 overflow 위험. compareTo 사용.

List<Long> list2 = new ArrayList<>(Arrays.asList(1L, 30L, 0L, 0L, 1L, 3L, 2L, 1L, 0L, 5L));
Collections.sort(list2, new Comparator<Long>() {
    @Override
    public int compare(Long o1, Long o2) {
        if (o1 == 0) {
            if (o2 == 0) return 0;
            else return 1;   // o1 만 0 → 뒤로
        } else if (o2 == 0) {
            return -1;       // o2 만 0 → 뒤로
        } else {
            return o1.compareTo(o2);
        }
    }
});

다중 조건 — 우선순위로 chain

Collections.sort(list, (a, b) -> {
    if (a.cond1 != b.cond1) return a.cond1 - b.cond1;       // 1순위
    if (a.cond2 != b.cond2) return b.cond2 - a.cond2;       // 2순위 역순
    return a.cond3 - b.cond3;                                // 3순위
});

6. BFS — 최단거리

최단거리 문제는 대부분 BFS. Trace (경로) 까지 가져와야 할 때는 parent[] 배열 필요. 코드트리 '포탑 부수기' 가 대표.

⚠️ 일반 BFS 와 다른 점은 보통 Queue 에 (좌표, 거리) 만 넣지만, trace 필요 시 prev 좌표 도 함께 저장하거나 별도 parent[][] 로 추적.

7. Spiral — 나선형 좌표 + 방향 저장

백준 '상어와 블리자드', 코드트리 '스톰'.

⚠️ 단순히 좌표만 저장하면 안 되고, 방향 (dir, rDir) 도 같이 저장해야 하는 경우가 많음.

static void makeSpiral() {
    int[] dx = {0, 1, 0, -1};
    int[] dy = {-1, 0, 1, 0};

    int index = 0;
    int dir = 0, rDir = 2;
    int cur_depth = 0;
    int ref_depth = 1;
    int turn = 0;

    int x_0 = N / 2;
    int y_0 = N / 2;
    spiralMap.put(index, new Node(x_0, y_0, dir, rDir));

    while (true) {
        index++;
        if (index == N * N) break;

        int new_x = x_0 + dx[dir];
        int new_y = y_0 + dy[dir];

        spiralMap.put(index, new Node(new_x, new_y, dir, rDir));
        cur_depth++;

        if (cur_depth == ref_depth) {
            dir = (dir + 1) % 4;
            rDir = (dir + 2) % 4;
            cur_depth = 0;
            turn++;
            spiralMap.put(index, new Node(new_x, new_y, dir, rDir));
            if (turn == 2) {
                ref_depth++;
                turn = 0;
            }
        }

        x_0 = new_x;
        y_0 = new_y;
    }
}

핵심: 두 번 방향 전환마다 ref_depth 가 1씩 증가. (1, 1, 2, 2, 3, 3, 4, 4, ...) 패턴.


내가 빠진 함정 — 3 종

함정 1 — 전역 조건 vs 국부 조건

문제 읽을 때 가장 조심해야 할 부분. 어떤 조건이 시뮬레이션 전체에 적용되는지, 국부적으로만 해당하는지 확실히.

예시: - '포탑 부수기': 시계열에 따라 최근 공격한 포탑 vs 공격한 횟수 혼동. - '토끼와 경주': K 번 돌아가는 조건 내에서 한 번이라도 점프한 토끼전체 시뮬에서 한 번이라도 점프한 토끼 잘못 해석.

함정 2 — 동시성 문제 (List.remove + for loop)

List 에서 remove 하면서 for loop 돌리면 하나 건너뛰게 됨. 이 경우 remove 후 i-- 해서 동시성 해결.

for (int i = 0; i < list.size(); i++) {
    if (조건) {
        list.remove(i);
        i--;  // ← 건너뛰는 인덱스 보정
    }
}

예시: [1, 2, 3, 4, 5] → index 2 일 때 3 remove → [1, 2, 4, 5] 가 되지만 다음 index 가 3 이 되어 4 가 아닌 5 를 가리키게 됨.

'숨바꼭질' 문제에서도 i-- 안 해서 동시성 문제 발생.

함정 3 — Loop 빠질 때 break

'꼬리잡기' 문제 — 머리가 다음 갈 곳을 찾을 때 4 방향 탐색. 목표를 찾고 break 하지 않으면 이상한 값으로 덮어 씌워짐.

4 3 2 1         4 3 2 1        4 3 2 2         4 3 3 2        4 4 3 2
4 0 0 4    →    4 0 0 1   →    4 0 0 1   →     4 0 0 1   →    4 0 0 1
4 4 4 4         4 4 4 4        4 4 4 4         4 4 4 4        4 4 4 4

target 을 찾았으면 즉시 break — 안 그러면 뒤이은 방향 탐색이 결과를 덮어씀.

for (int dir = 0; dir < 4; dir++) {
    if (조건) {
        // 처리
        break;  // ← 핵심
    }
}

정리

  • 90° 회전: 부분 영역 좌표 변환 식 외워두기.
  • PBC: 0-base vs 1-base · 음수 처리 · big distance 도 Math.abs + N 더하기.
  • Sort: Long 은 compareTo · 다중 조건 chain.
  • Spiral: 방향 + ref_depth 패턴.
  • List.remove: i-- 잊지 말 것.
  • Loop 탐색: 찾으면 즉시 break.
  • 조건 범위: 전역 vs 국부 — 문제 읽을 때 가장 신경 쓸 부분.

5 회 응시 후기는 함께 보면 좋음:


📦 이 글은 제가 운영하던 티스토리 블로그에서 옮겨온(migration) 글입니다. 원문: taehyuklee.tistory.com/17

이 글 공유𝕏f

댓글