3월 15일, 4월 12일 이틀을 끊어서 코딩 테스트 특강이 진행되었다.
특강에 앞서 코딩 테스트의 필요성에 대해 알아보았다.
회사 입장에서는 코딩 테스트를 통해 기본적인 문제해결능력을 함양하고 있는지, 자료구조 알고리즘 및 코드 구현 능력이 있는지 효율적으로 판단할 수 있다.
또 회사마다 원하는 인재상이 다르기 때문에 목표로 하는 회사에 따라 전략을 달리 해야 한다.
- 네이버, 카카오 등은 고난이도의 코딩 테스트 실력을 요구하며, CS 전공지식을 깊게 물어본다.
- 당근마켓, 토스 등은 코딩 테스트보다는 과제를 해결하는 데에 중점을 두며, 실제 개발 실력이 있는지 면접단계에서 철저히 검증한다. 또, 프로젝트 및 언어와 관련하여 깊은 지식을 테스트하는 편이다.
- 그 외의 작은 회사들은 간단한 코딩테스트를 통과하는지를 본다.
코딩 테스트에서 효율적으로 공부하기 위해서는
특별히 한 언어를 고집해야 하는 경우가 아니고, 어느 정도 여유가 있다면 파이썬으로 준비하는 것을 추천하는 편이라고 한다.
이 날은 그래프 문제 풀이법인 BFS, DFS를 주제로 진행되었다.
그래프는 객체 간의 연결 관계를 표현하는 자료구조로, 정점(vertex, node)과 이를 연결하는 간선(edge)으로 구성된다. 현실적으로 가장 많이 쓰이는 자료구조이기 때문에 이를 잘 사용하는 것이 관건이다.
(ex) 지하철 노선도
- 방향 그래프
- 무방향 그래프(주로 양방향)
- 다중그래프(정점과의 연결관계가 여러 개. 코딩 테스트에는 나오지 않음)
- 단순 그래프(중복된 연관관계 x)
- 가중치 그래프(연결관계마다 가중치를 가진 그래프) “다익스트라 알고리즘”
- 비가중치 그래프
- 순환 그래프(왔던 길을 돌아가지 않고도 시작했던 정점으로 다시 돌아올 수 있는 그래프)
- 비순환 그래프
코딩 테스트에서는 무방향, 단순, 비가중치, 순환 그래프가 자주 출제된다.
(가중치는 dijkstra, 비순환그래프는 DAG, Tree의 별개의 주제로 따로 출제된다.)
그래프 표현 방법에는 총 3개가지가 있다.
인접행렬 adjacency matrix
노드의 개수만큼 2차원 배열을 만들어서 위치를 정하는 방식이다. 장점은 표현하기 쉽다는 점이나, 연결이 되지 않은 경우를 표기할 때 불필요한 메모리를 사용하게 된다는 단점이 있다.
인접리스트 adjacency list
그래프를 해시맵이나 해시테이블로 구현하는 방법이다. 주로 이 방법을 사용하게 된다.
암시적 그래프 implicit graph
문제에서 정한 규칙과 주어진 조건대로 그리드를 형성하여 그래프로 해석하는 방식이다. 그 예시로, 1은 벽, 0은 길 식으로 문제에서 조건을 제공하는 것을 들 수 있다.
체계적으로 모든 노드를 방문하는 과정을 말한다. 이 과정에서 각 노드는 최소 한 번 이상 방문된다. 대표적인 알고리즘으로 BFS와 DFS가 있다.
특정 조건을 만족하는 노드를 찾는 과정으로, 이 과정에서 모든 노드를 방문할 수도, 목표 노드를 찾으면 탐색을 중단할 수도 있다. 마찬가지로 BFS, DFS를 사용하여 구현할 수 있다.
너비우선탐색으로, 시작점에 가까운 순서부터 탐색하는 알고리즘이다. 큐를 사용하며(FIFO), 이 큐에는 아직 방문하지 않은 노드들이 저장된다. 이름 그대로 같은 계층에 있는 노드를 전부 다 방문한 후에 다음 계층으로 넘어간다. 중복 예약을 피하기 위해 노드에 방문하기 전에 해당 노드를 방문한 노드 맵에 저장하며, 이 큐가 다 빌 때까지 while문을 사용해 방문을 예약해 체계적으로 모든 노드를 방문한다.
깊이우선탐색으로, 시작 노드에서 더 이상 연결된 노드가 없을 때까지 탐색을 반복한다. 먼저 현재 방문한 노드에서, 연결되어 있고 아직 방문하지 않은 노드를 찾고, 그 노드를 방문하여 이를 반복한다. BFS와는 반대로 노드 방문 시점에 해당 노드를 이미 방문한 노드 목록에 추가한다. 또, 재귀를 사용하여 연결된 노드를 다 뚫을 때까지 dfs를 호출하도록 짤 수도 있다.

class Solution {
static int rowLength, colLength;
static boolean[][] visited;
static int[] dr = {0,1,0,-1};
static int[] dc = {1,0,-1,0};
public static boolean isValid(int r, int c, char[][] grid){
return (r>=0&&r<rowLength) && (c>=0&&c<colLength)&&(grid[r][c] == '1');
}
public static void bfs(int r, int c, char[][] grid){
Queue<int[]> queue = new LinkedList<>();
queue.offer(new int[]{r,c});
visited[r][c] = true;
while (!queue.isEmpty()){
int[] curPos = queue.poll();
int curRow = curPos[0];
int curCol = curPos[1];
for (int i = 0; i <4 ; i++){
int nextRow = curRow + dr[i];
int nextCol = curCol+dc[i];
if (isValid(nextRow, nextCol, grid)){
if (!(visited[nextRow][nextCol])){
queue.offer(new int[]{nextRow, nextCol});
visited[nextRow][nextCol] = true;
}
}
}
}
}
public int numIslands(char[][] grid) {
int count = 0;
rowLength = grid.length;
colLength = grid[0].length;
visited = new boolean[rowLength][colLength];
for (int i=0; i<rowLength;i++){
for (int j = 0; j<colLength;j++){
if ((grid[i][j] == '1')&&(!visited[i][j])){
bfs(i,j,grid);
count++;
}
}
}
return count;
}
}