문제 링크: https://school.programmers.co.kr/learn/courses/30/lessons/150366
MERGE
기능으로 인해 특정 셀에 업데이트가 발생하면 병합된 모든 셀에 반영해줘야 하기 때문에 Union-find로 풀면 좋을 것이라 판단했다.
구현해야 할 기능이 많기 때문에 solution
함수 내부에 기능별로 중첩 함수를 구현하고 commands
를 순회하면서 전역 변수 cell
과 parent
를 업데이트해주는 식으로 풀었다.
function solution(commands) {
let answer = [];
const cell = Array(51)
.fill()
.map((_) => Array(51).fill(''));
const parent = Array(51)
.fill()
.map((_, i) =>
Array(51)
.fill()
.map((_, j) => [i, j])
);
for (const commandStr of commands) {
const [command, ...rest] = commandStr.split(' ');
switch (command) {
case 'UPDATE':
if (rest.length === 3) update(rest);
else replace(rest);
break;
case 'MERGE':
merge(rest);
break;
case 'UNMERGE':
unmerge(rest);
break;
case 'PRINT':
answer = [...answer, print(rest)];
break;
}
}
return answer;
function updateAll(target, value, type) {
const _target = target.toString();
for (let i = 0; i < 51; i++) {
for (let j = 0; j < 51; j++) {
if (parent[i][j].toString() === _target) {
if (type === 'value') cell[i][j] = value;
else parent[i][j] = value;
}
}
}
}
function update(param) {
const [r, c, value] = param;
const target = find([r, c]);
updateAll(target, value, 'value');
}
function replace(param) {
const [value1, value2] = param;
for (let i = 0; i < 51; i++) {
for (let j = 0; j < 51; j++) {
if (cell[i][j] === value1) {
cell[i][j] = value2;
}
}
}
}
function merge(param) {
const [r1, c1, r2, c2] = param;
if (r1 === r2 && c1 === c2) return;
const value = cell[r1][c1] !== '' ? cell[r1][c1] : cell[r2][c2];
const parent1 = find([r1, c1]);
const parent2 = find([r2, c2]);
if (parent1.toString() !== parent2.toString()) {
parent[r2][c2] = parent1;
}
updateAll(parent2, parent1, 'parent');
updateAll(parent1, value, 'value');
}
function unmerge(param) {
const [r, c] = param;
const value = cell[r][c];
const _target = find([r, c]).toString();
for (let i = 0; i < 51; i++) {
for (let j = 0; j < 51; j++) {
if (parent[i][j].toString() === _target) {
cell[i][j] = '';
parent[i][j] = [i, j];
}
}
}
cell[r][c] = value;
}
function print(param) {
const [r, c] = param;
return cell[r][c] === '' ? 'EMPTY' : cell[r][c];
}
}
코드가 지저분해 보였다. 원래 updateAll
을 통해서 코드의 중복을 줄여보려고 했었는데, 이중 for
문 때문에 여전히 지저분해 보이기는 마찬가지였다.
반복문 내에서의 범위는 같았고 조건과 작업만 살짝씩 달랐기 때문에 callback 함수를 줘서 반복문을 돌리는 iterateAll
함수를 만들었고, toString
으로 parent
를 비교하는 부분들도 지저분해 보여서 isSameCoords
함수를 만들어 리팩토링했다.
함수의 순서도 살짝 조정했다.
function solution(commands) {
let answer = [];
const cell = Array(51)
.fill()
.map((_) => Array(51).fill(''));
const parent = Array(51)
.fill()
.map((_, i) =>
Array(51)
.fill()
.map((_, j) => [i, j])
);
for (const commandStr of commands) {
const [command, ...rest] = commandStr.split(' ');
switch (command) {
case 'UPDATE':
if (rest.length === 3) update(rest);
else replace(rest);
break;
case 'MERGE':
merge(rest);
break;
case 'UNMERGE':
unmerge(rest);
break;
case 'PRINT':
answer = [...answer, print(rest)];
break;
}
}
return answer;
function update(param) {
const [r, c, value] = param;
const target = find([r, c]);
iterateAll((i, j) => {
if (isSameCoords(parent[i][j], target)) cell[i][j] = value;
});
}
function replace(param) {
const [value1, value2] = param;
iterateAll((i, j) => {
if (cell[i][j] === value1) cell[i][j] = value2;
});
}
function merge(param) {
const [r1, c1, r2, c2] = param;
if (r1 === r2 && c1 === c2) return;
const value = cell[r1][c1] !== '' ? cell[r1][c1] : cell[r2][c2];
const parent1 = find([r1, c1]);
const parent2 = find([r2, c2]);
if (!isSameCoords(parent1, parent2)) {
parent[r2][c2] = parent1;
}
iterateAll((i, j) => {
if (isSameCoords(parent[i][j], parent2)) {
parent[i][j] = parent1;
}
if (isSameCoords(parent[i][j], parent1)) {
cell[i][j] = value;
}
});
}
function unmerge(param) {
const [r, c] = param;
const value = cell[r][c];
const target = find([r, c]);
iterateAll((i, j) => {
if (isSameCoords(parent[i][j], target)) {
cell[i][j] = '';
parent[i][j] = [i, j];
}
});
cell[r][c] = value;
}
function print(param) {
const [r, c] = param;
return cell[r][c] === '' ? 'EMPTY' : cell[r][c];
}
function find(coord) {
const [r, c] = coord;
if (isSameCoords([r, c], parent[r][c])) {
return [Number(r), Number(c)];
}
parent[r][c] = find(parent[r][c]);
return parent[r][c];
}
function isSameCoords(coord1, coord2) {
return coord1.toString() === coord2.toString();
}
function iterateAll(callbackFn) {
for (let i = 0; i < 51; i++) {
for (let j = 0; j < 51; j++) {
callbackFn(i, j);
}
}
}
}