Skip to content

[alphaorderly] WEEK 14 Solutions - #2868

Merged
parkhojeong merged 1 commit into
DaleStudy:mainfrom
alphaorderly:week-14
Sep 27, 2026
Merged

parkhojeong merged 1 commit into
DaleStudy:mainfrom
alphaorderly:week-14

Conversation

@alphaorderly

@alphaorderly alphaorderly commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

답안 제출 문제

작성자 체크 리스트

  • Projects의 오른쪽 버튼(▼)을 눌러 확장한 뒤, Week를 현재 주차로 설정해주세요.
  • 문제를 모두 푸시면 프로젝트에서 Status를 In Review로 설정해주세요.
  • 코드 검토자 1분 이상으로부터 승인을 받으셨다면 PR을 병합해주세요.

검토자 체크 리스트

Important

본인 답안 제출 뿐만 아니라 다른 분 PR 하나 이상을 반드시 검토를 해주셔야 합니다!

  • 바로 이전에 올라온 PR에 본인을 코드 리뷰어로 추가해주세요.
  • 본인이 검토해야하는 PR의 답안 코드에 피드백을 주세요.
  • 토요일 전까지 PR을 병합할 수 있도록 승인해주세요.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

binary-tree-level-order-traversal/alphaorderly.py
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
"""
시간복잡도: O(n)
  - n: 트리의 모든 노드 개수 (각 노드를 한 번씩 방문)
공간복잡도: O(n)
  - n: 최악의 경우 큐(queue)에 저장되는 노드 수(가장 마지막 레벨의 노드 수와 동일)

BFS(너비 우선 탐색)을 활용하여 트리를 레벨별로 순회하며,
각 레벨마다 노드의 값을 리스트로 모아 결과 리스트에 추가합니다.
"""
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:

        if not root:
            return []

        queue = deque([root])
        ans = []

        while queue:
            temp = []
            Q = len(queue)
            for _ in range(Q):
                node = queue.popleft()
                temp.append(node.val)

                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            ans.append(temp)

        return ans
  • 패턴: BFS
  • 설명: 코드에서 트리를 level 단위로 순회하며 큐를 사용해 각 레벨의 노드 값을 모으는 방식으로 구현되어 BFS 패턴에 해당합니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(n)
Space O(n)

피드백: 모든 노드를 한 번 방문하고 큐에 최대 한 레벨의 노드들이 저장되므로 시간은 트리의 노드 수에 비례하고, 공간은 큐의 최대 크기와 결과 저장으로 결정됩니다.

개선 제안: 현재 구현은 일반적이고 안정적입니다. 트리의 깊이가 매우 커진 경우 큐의 크기가 커지므로 필요 시 레벨 구분 없이 인덱스 기반 순회로 최적화 고려 가능.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

counting-bits/alphaorderly.py
"""
시간복잡도: O(n)
  - n: 입력된 숫자 n까지의 범위
공간복잡도: O(n)
  - n: 결과값 리스트(ans)의 크기
"""
class Solution:
    def countBits(self, n: int) -> List[int]:
        ans = [0] * (n + 1)

        for i in range(1, n + 1):
            ans[i] = (i & 1) + ans[i // 2]

        return ans
  • 패턴: Dynamic Programming, Bit Manipulation
  • 설명: 정답 배열을 순차적으로 채우며 이전 값들을 이용해 현재 값을 계산하는 형태로, 비트 연산과 DP의 결합으로 각 i에 대한 비트 수를 구하는 패턴이다.

📊 시간/공간 복잡도 분석

복잡도
Time O(n)
Space O(n)

피드백: 배열 인덱스 i에 대해 i의 하위 구조를 활용해 앞선 값을 재활용하므로 한 번의 순회로 결과를 얻습니다.

개선 제안: 현재 구현이 적절해 보입니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

@dalestudy

dalestudy Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

📊 alphaorderly 님의 학습 현황

이번 주 제출 문제

문제 난이도 유형 분석
binary-tree-level-order-traversal Medium ✅ 의도한 유형
counting-bits Easy ✅ 의도한 유형
house-robber-ii Medium ✅ 의도한 유형
meeting-rooms-ii Medium ✅ 의도한 유형
word-search-ii Hard ✅ 의도한 유형

누적 학습 요약

  • 풀이한 문제: 65 / 75개
  • 이번 주 유형 일치율: 100% (5문제 중 5문제 일치)

문제 풀이 현황

카테고리 진행도 완료
Array ■■■■■■■ 10 / 10 (Medium 7, Easy 3)
Linked List ■■■■■■■ 6 / 6 (Easy 3, Hard 1, Medium 2)
Heap ■■■■■■■ 3 / 3 (Hard 2, Medium 1)
Dynamic Programming ■■■■■■□ 10 / 11 (Easy 1, Medium 9)
String ■■■■■■□ 9 / 10 (Medium 5, Hard 1, Easy 3)
Graph ■■■■■■□ 7 / 8 (Medium 7)
Binary ■■■■■■□ 4 / 5 (Easy 3, Medium 1)
Interval ■■■■■■□ 4 / 5 (Medium 3, Easy 1)
Matrix ■■■■■□□ 3 / 4 (Medium 3)
Tree ■■■■■□□ 10 / 14 (Hard 2, Medium 5, Easy 3)

🤖 이 댓글은 GitHub App을 통해 자동으로 작성되었습니다.

🔢 API 사용량 (gpt-5-nano)
요청 입력 토큰 출력 토큰 합계 비용
1 2,655 219 2,874 $0.000220

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

house-robber-ii/alphaorderly.py
"""
시간복잡도: O(n)
  - n: 입력된 배열 nums의 길이
공간복잡도: O(n)
  - n: DP 리스트(first, second)의 크기

집이 원형이라 첫 집과 마지막 집을 동시에 털 수 없으므로,
두 구간으로 나누어 각각 선형 House Robber를 적용합니다.

first는 마지막 집을 제외한 구간(0 ~ n-2),
second는 첫 집을 제외한 구간(1 ~ n-1)의 최대 금액을 저장합니다.

두 구간의 마지막 값 중 더 큰 값을 반환합니다.
"""
class Solution:
    def rob(self, nums: List[int]) -> int:

        if len(nums) <= 2:
            return max(nums)

        first = [nums[0], max(nums[0], nums[1])]
        second = [0, nums[1]]

        for i in range(2, len(nums)):
            if i != len(nums) - 1:
                first.append(max(nums[i] + first[i - 2], first[i - 1]))
            second.append(max(nums[i] + second[i - 2], second[i - 1]))

        return max(first[-1], second[-1])
  • 패턴: Dynamic Programming, Greedy, Divide and Conquer
  • 설명: 배열을 두 구간으로 나눠 각 구간에 대한 최적해를 구하는 DP 접근으로 최댓값을 비교합니다. 원형 배열의 특성을 이용해 선형 문제로 분해하는 아이디어는 Divide and Conquer에 부합합니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(n)
Space O(n)

피드백: 두 구간 각각 독립적으로 최적해를 구하므로 원형 제약을 회피하며 DP로 해결합니다.

개선 제안: 특정 예외 처리로 간결하게 개선될 수는 있지만 현재 구현도 적절합니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

meeting-rooms-ii/alphaorderly.py
"""
시간복잡도: O(n log n)
  - n: 입력된 배열 intervals의 길이
공간복잡도: O(n)
  - n: 진행 중인 회의의 종료 시각을 저장하는 최소 힙의 크기

intervals를 시작 시각 기준으로 정렬합니다.
최소 힙에는 아직 사용 중인 회의실의 종료 시각을 저장합니다.

각 회의의 시작 시각이 힙의 가장 이른 종료 시각 이후이면
그 회의실은 비었으므로 힙에서 제거하고,
현재 회의의 종료 시각을 힙에 넣습니다.
힙 크기의 최댓값이 필요한 최소 회의실 개수입니다.
"""
class Solution:
    def minMeetingRooms(self, intervals: List[List[int]]) -> int:
        intervals.sort()
        ans = 1
        heap = []

        for start, end in intervals:
            while heap and heap[0] <= start:
                heappop(heap)
            heappush(heap, end)
            ans = max(ans, len(heap))

        return ans
  • 패턴: Greedy, Heap / Priority Queue, Binary Search
  • 설명: 이 코드는 회의실 수를 최소로 하려는 아이디어로 시작 시점 정렬 후 종료 시점 관리가 핵심인 Greedy 패턴을 활용합니다. 남은 회의실의 끝나는 시간을 최소 힙에 저장해 가장 이른 종료 시점과 비교하며 필요 시 새 회의실을 배정하므로 Heap / Priority Queue 패턴이 적용됩니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(n log n)
Space O(n)

피드백: 정렬과 힙을 활용해 겹치는 구간을 효율적으로 추적하며 최댓 힙 크기가 필요 회의실 수를 의미합니다.

개선 제안: 현 구현이 일반적으로 충분합니다. 입력 크기가 커지면 힙 연산의 상수층을 고려한 최적화 가능성은 여전히 남아 있습니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

word-search-ii/alphaorderly.py
"""
시간복잡도: O(w + m * n * 4^l)
  - w: 모든 단어 길이의 합 (트라이 구성)
  - m, n: 보드의 행, 열
  - l: 단어의 최대 길이 (각 칸에서 상하좌우 DFS)
공간복잡도: O(w + l)
  - w: 트라이와 정답 집합
  - l: DFS 재귀 깊이와 현재 경로의 방문 집합

단어를 트라이에 넣은 뒤, 보드의 각 칸에서 DFS로 이어지는 단어를 찾습니다.
이미 찾은 단어는 is_end를 끄고, 더 이상 이어지지 않는 노드는 트라이에서 지워
같은 접두사를 다시 탐색하지 않습니다.
"""
class TrieNode:
    def __init__(self):
        self.is_end = False
        self.children = dict()

    def add_word(self, word: str):
        node = self

        for ch in word:
            if ch not in node.children:
                node.children[ch] = TrieNode()
            node = node.children[ch]

        # 이 글자에서 단어가 끝난다
        node.is_end = True


class Solution:
    def findWords(self, board: list[list[str]], words: list[str]) -> list[str]:
        ROW, COL = len(board), len(board[0])
        # 상하좌우
        DIR = [[1, 0], [0, 1], [-1, 0], [0, -1]]

        trie = TrieNode()
        for word in words:
            trie.add_word(word)

        def bound(row: int, col: int) -> bool:
            return 0 <= row < ROW and 0 <= col < COL

        # ans: 찾은 단어, visited: 현재 경로에서 지난 칸
        ans, visited = set(), set()

        def search(r: int, c: int, node: TrieNode, word: str):
            # 다음 글자가 트라이에 없거나, 같은 칸을 다시 밟으면 중단
            if board[r][c] not in node.children or (r, c) in visited:
                return

            visited.add((r, c))

            # 가지치기할 때 부모에서 현재 글자를 지우기 위해 보관
            parent = node
            ch = board[r][c]

            node = node.children[ch]
            word += ch

            if node.is_end:
                ans.add(word)
                # 같은 단어는 한 번만 찾는다
                node.is_end = False

            for dr, dc in DIR:
                tr, tc = r + dr, c + dc
                if not bound(tr, tc):
                    continue
                search(tr, tc, node, word)

            # 백트래킹: 이 칸을 다른 경로에서 다시 쓸 수 있게 되돌린다
            visited.remove((r, c))

            # 자식도 없고 여기서 끝나는 단어도 없으면 트라이에서 제거
            if not node.children and not node.is_end:
                del parent.children[ch]

        for r in range(ROW):
            for c in range(COL):
                search(r, c, trie, "")

        return list(ans)
  • 패턴: Trie, Backtracking, Depth-First Search, Hash Map / Hash Set
  • 설명: 트라이를 구성해 단어를 저장하고 보드에서 DFS로 탐색하며 경로를 백트래킹합니다. 방문 관리와 가지치기, 중간 노드 삭제를 통해 효율적으로 가능한 단어를 찾는 구조로, DFS 기반의 백트래킹 패턴이 핵심입니다.

📊 시간/공간 복잡도 분석

복잡도
Time O(w + m * n * 4^l)
Space O(w + l)

피드백: 사전 구성된 트라이와 가지치기로 중복 탐색을 줄이며 DFS를 통해 보드 전체를 탐색합니다.

개선 제안: 현재 아이디어가 효과적이지만, 트라이 제거 및 재탐색 비용을 더 줄일 수 있는 추가 최적화 여지가 있습니다.

💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!

@DaleSeo DaleSeo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추선 연휴에도 거르지 않고 문제푸는 모습 멋집니다! 👍

for c in range(COL):
search(r, c, trie, "")

return list(ans)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이 ans에 세트를 할당하고 마지막에 다시 리스트로 바꿀 이유가 있을까요? 같은 단어가 다시 추가될 수 있는 경우가 있는지 생각해보시면 좋을 것 같습니다.

시간복잡도: O(n)
- n: 입력된 배열 nums의 길이
공간복잡도: O(n)
- n: DP 리스트(first, second)의 크기

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

점화식을 보면 길이가 n인 리스트 대신에 변수 2개만 있어도 충분할 것 같네요? 메모리 최적화의 기회가 보입니다.

@parkhojeong parkhojeong left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번 주도 수고하셨습니다.

Comment on lines +24 to +27
for i in range(2, len(nums)):
if i != len(nums) - 1:
first.append(max(nums[i] + first[i - 2], first[i - 1]))
second.append(max(nums[i] + second[i - 2], second[i - 1]))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하나의 루프에서 두 가지 동작을 하네요. prev2, prev1 변수를 사용하는 하나의 헬퍼 함수를 두면 공간 복잡도가 O(1)로 줄고 가독성도 개선할 수 있을 거 같습니다.

@parkhojeong
parkhojeong merged commit 8c9f11e into DaleStudy:main Sep 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

No open projects
Status: Completed

Development

Successfully merging this pull request may close these issues.

3 participants