Skip to content

Conversation

@uyeon0
Copy link
Collaborator

@uyeon0 uyeon0 commented Dec 3, 2025

User description

오늘도 멋져요 👍✨


PR Type

Enhancement


Description

  • 프로그래머스 3개 문제 새로운 솔루션 추가

  • 각 문제에 대해 최적화된 알고리즘 구현

  • 스택, 연결 리스트 등 자료구조 활용


@uyeon0 uyeon0 added the programmers Programmers 문제 풀이 label Dec 3, 2025
@github-actions
Copy link

github-actions bot commented Dec 3, 2025

PR Reviewer Guide 🔍

🧪 No relevant tests
⚡ Recommended focus areas for review

성능 최적화

스택을 활용한 O(n) 시간 복잡도 알고리즘으로 주식 가격 문제를 효율적으로 해결.
단일 순회로 모든 가격 유지 시간을 계산하여 공간 및 시간 복잡도 개선.

function solution3(prices) {
  const stack = [];
  const answer = Array(prices.length).fill(0);

  for (let idx = 0; idx < prices.length; idx++) {
    while (stack.length > 0 && prices[stack[stack.length - 1]] > prices[idx]) {
      const top = stack.pop();
      answer[top] = idx - top;
    }
    stack.push(idx);
  }

  while (stack.length > 0) {
    const top = stack.pop();
    answer[top] = prices.length - top - 1;
  }

  return answer;
}
자료구조 최적화

이중 연결 리스트를 사용하여 행 삭제 및 복구 연산의 시간 복잡도를 O(1)로 최적화.
메모리 효율적인 방식으로 표 편집 문제 해결.

function solution3(n, k, cmd) {
  const row = Array.from({ length: n }, (_, idx) => ({
    prev: idx === 0 ? null : idx - 1,
    isDeleted: false,
    next: idx === n - 1 ? null : idx + 1,
  }));

  const deleted = [];
  let cur = k;
  for (let idx = 0; idx < cmd.length; idx++) {
    const [op, argstr] = cmd[idx].split(" ");
    const arg = Number(argstr);
    switch (op) {
      case "U":
        for (let cnt = 0; cnt < arg; cnt++) {
          cur = row[cur].prev;
        }
        break;
      case "D":
        for (let cnt = 0; cnt < arg; cnt++) {
          cur = row[cur].next;
        }
        break;
      case "C":
        // 현재 선택된 행을 삭제
        row[cur].isDeleted = true;
        deleted.push(cur);
        // 현재 선택된 행이 가장 마지막 행인 경우 현재 행을 그 위 행으로
        if (row[cur].next === null) {
          cur = row[cur].prev;
          if (cur) row[cur].next = null;
        } else {
          // 그 외에는 아래 행 선택
          if (row[cur].prev !== null) row[row[cur].prev].next = row[cur].next;
          row[row[cur].next].prev = row[cur].prev;
          cur = row[cur].next;
        }
        break;
      case "Z":
        // 최근에 삭제된 것 복구
        const deletedIdx = deleted.pop();
        if (deletedIdx === undefined) break;
        if (row[deletedIdx].prev !== null)
          row[row[deletedIdx].prev].next = deletedIdx;
        if (row[deletedIdx].next !== null)
          row[row[deletedIdx].next].prev = deletedIdx;
        row[deletedIdx].isDeleted = false;
        break;
    }
  }

  const answer = row.reduce(
    (acc, { isDeleted }) => (acc += isDeleted ? "X" : "O"),
    ""
  );
  return answer;
}
알고리즘 개선

보드 데이터를 사전 처리하여 스택 기반 접근 방식으로 인형뽑기 게임 로직 최적화.
단일 순회로 보드 상태 전환 및 효율적인 인형 제거 구현.

function solution3(board, moves) {
  const boardStack = Array.from({ length: board.length }, () => new Array());
  board.reverse().forEach((row) => {
    for (let idx = 0; idx < row.length; idx++) {
      row[idx] && boardStack[idx].push(row[idx]);
    }
  });

  const basket = [];
  let answer = 0;
  for (let idx = 0; idx < moves.length; idx++) {
    const target = boardStack[moves[idx] - 1].pop();
    if (!target) continue;
    if (basket.length > 0 && target === basket[basket.length - 1]) {
      basket.pop();
      answer += 2;
    } else {
      basket.push(target);
    }
  }

  return answer;
}

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
메서드 분리로 코드 가독성 개선

현재 구현은 복잡한 로직을 포함하고 있습니다. 각 명령어 처리 로직을 별도의 메서드로 분리하여 코드의 가독성과 유지보수성을 높일 수 있습니다.

Programmers/Level3/81303_표_편집.js [16-72]

 function solution3(n, k, cmd) {
   const row = Array.from({ length: n }, (_, idx) => ({
     prev: idx === 0 ? null : idx - 1,
     isDeleted: false,
     next: idx === n - 1 ? null : idx + 1,
   }));
 
   const deleted = [];
   let cur = k;
-  for (let idx = 0; idx < cmd.length; idx++) {
-    const [op, argstr] = cmd[idx].split(" ");
+
+  const moveUp = (count) => {
+    for (let i = 0; i < count; i++) {
+      cur = row[cur].prev;
+    }
+  };
+
+  const moveDown = (count) => {
+    for (let i = 0; i < count; i++) {
+      cur = row[cur].next;
+    }
+  };
+
+  const deleteRow = () => {
+    row[cur].isDeleted = true;
+    deleted.push(cur);
+
+    if (row[cur].next === null) {
+      cur = row[cur].prev;
+      if (cur) row[cur].next = null;
+    } else {
+      if (row[cur].prev !== null) row[row[cur].prev].next = row[cur].next;
+      row[row[cur].next].prev = row[cur].prev;
+      cur = row[cur].next;
+    }
+  };
+
+  const restoreRow = () => {
+    const deletedIdx = deleted.pop();
+    if (deletedIdx === undefined) return;
+
+    if (row[deletedIdx].prev !== null)
+      row[row[deletedIdx].prev].next = deletedIdx;
+    if (row[deletedIdx].next !== null)
+      row[row[deletedIdx].next].prev = deletedIdx;
+    row[deletedIdx].isDeleted = false;
+  };
+
+  for (const command of cmd) {
+    const [op, argstr] = command.split(" ");
     const arg = Number(argstr);
+
     switch (op) {
-      case "U":
-        for (let cnt = 0; cnt < arg; cnt++) {
-          cur = row[cur].prev;
-        }
-        break;
-      case "D":
-        for (let cnt = 0; cnt < arg; cnt++) {
-          cur = row[cur].next;
-        }
-        break;
-      case "C":
-        row[cur].isDeleted = true;
-        deleted.push(cur);
-        if (row[cur].next === null) {
-          cur = row[cur].prev;
-          if (cur) row[cur].next = null;
-        } else {
-          if (row[cur].prev !== null) row[row[cur].prev].next = row[cur].next;
-          row[row[cur].next].prev = row[cur].prev;
-          cur = row[cur].next;
-        }
-        break;
-      case "Z":
-        const deletedIdx = deleted.pop();
-        if (deletedIdx === undefined) break;
-        if (row[deletedIdx].prev !== null)
-          row[row[deletedIdx].prev].next = deletedIdx;
-        if (row[deletedIdx].next !== null)
-          row[row[deletedIdx].next].prev = deletedIdx;
-        row[deletedIdx].isDeleted = false;
-        break;
+      case "U": moveUp(arg); break;
+      case "D": moveDown(arg); break;
+      case "C": deleteRow(); break;
+      case "Z": restoreRow(); break;
     }
   }
 
-  const answer = row.reduce(
-    (acc, { isDeleted }) => (acc += isDeleted ? "X" : "O"),
-    ""
-  );
-  return answer;
+  return row.reduce((acc, { isDeleted }) => acc + (isDeleted ? "X" : "O"), "");
 }
Suggestion importance[1-10]: 8

__

Why: 제안된 리팩토링은 복잡한 로직을 별도의 메서드로 분리하여 코드의 가독성과 유지보수성을 크게 향상시킵니다. 각 작업의 책임을 명확히 분리했습니다.

Medium
배열 순회 방식 최적화

현재 구현은 작동하지만, 배열을 불필요하게 뒤집고 있습니다. 이는 메모리와 성능에 비효율적입니다. 대신 역방향 순회를 통해 스택을 직접 구성할 수
있습니다.

Programmers/Level1/64061_크레인_인형뽑기_게임.js [9-31]

 function solution3(board, moves) {
-  const boardStack = Array.from({ length: board.length }, () => new Array());
-  board.reverse().forEach((row) => {
-    for (let idx = 0; idx < row.length; idx++) {
-      row[idx] && boardStack[idx].push(row[idx]);
+  const boardStack = Array.from({ length: board[0].length }, () => []);
+  for (let col = 0; col < board[0].length; col++) {
+    for (let row = board.length - 1; row >= 0; row--) {
+      if (board[row][col]) {
+        boardStack[col].push(board[row][col]);
+      }
     }
-  });
+  }
 
   const basket = [];
   let answer = 0;
-  for (let idx = 0; idx < moves.length; idx++) {
-    const target = boardStack[moves[idx] - 1].pop();
+  for (const move of moves) {
+    const target = boardStack[move - 1].pop();
     if (!target) continue;
     if (basket.length > 0 && target === basket[basket.length - 1]) {
       basket.pop();
       answer += 2;
     } else {
       basket.push(target);
     }
   }
 
   return answer;
 }
Suggestion importance[1-10]: 7

__

Why: 제안된 코드는 board.reverse()를 제거하고 더 직관적인 열 기반 순회를 제안합니다. 이는 메모리 효율성을 개선하고 코드의 가독성을 높입니다.

Medium
변수명 및 로직 가독성 개선

현재 구현은 스택을 사용하여 효율적으로 문제를 해결하고 있습니다. 가독성을 높이기 위해 변수명과 로직을 약간 개선할 수 있습니다.

Programmers/Level2/42584_주식가격.js [9-72]

 function solution3(prices) {
-  const stack = [];
-  const answer = Array(prices.length).fill(0);
+  const priceIndices = [];
+  const duration = Array(prices.length).fill(0);
 
-  for (let idx = 0; idx < prices.length; idx++) {
-    while (stack.length > 0 && prices[stack[stack.length - 1]] > prices[idx]) {
-      const top = stack.pop();
-      answer[top] = idx - top;
+  for (let currentIdx = 0; currentIdx < prices.length; currentIdx++) {
+    while (priceIndices.length && prices[priceIndices[priceIndices.length - 1]] > prices[currentIdx]) {
+      const prevIdx = priceIndices.pop();
+      duration[prevIdx] = currentIdx - prevIdx;
     }
-    stack.push(idx);
+    priceIndices.push(currentIdx);
   }
 
-  while (stack.length > 0) {
-    const top = stack.pop();
-    answer[top] = prices.length - top - 1;
+  while (priceIndices.length) {
+    const remainingIdx = priceIndices.pop();
+    duration[remainingIdx] = prices.length - remainingIdx - 1;
   }
 
-  return answer;
+  return duration;
 }
Suggestion importance[1-10]: 6

__

Why: 제안된 변경사항은 변수명을 더 의미 있게 만들고 로직을 약간 명확히 합니다. 그러나 기본 알고리즘은 동일하므로 큰 성능 개선은 없습니다.

Low

cur 존재 여부 확인 조건 명확하게 수정
@yoouyeon yoouyeon added the ready-to-merge pr을 머지해주세요 label Dec 3, 2025
@uyeon0 uyeon0 merged commit acbbd84 into main Dec 3, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

programmers Programmers 문제 풀이 ready-to-merge pr을 머지해주세요

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants