[essaysir] WEEK 08 Solutions - #2814
Conversation
📊 essaysir 님의 학습 현황이번 주 제출 문제
누적 학습 요약
문제 풀이 현황
🤖 이 댓글은 GitHub App을 통해 자동으로 작성되었습니다. 🔢 API 사용량 (gpt-5-nano)
|
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
clone-graph/essaysir.java
/*
// Definition for a Node.
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
*/
class Solution {
public Node cloneGraph(Node node) {
if ( node == null ) return null;
// 똑같은 그래프를 만드는 게 목적
// node.val -> 숫자( id 로 생각 )
// neighbors -> 인접한 id 들
Map<Integer, Node> cloned = new HashMap<>();
cloned.put(node.val, new Node(node.val));
Queue<Node> queue = new ArrayDeque<>();
queue.offer(node);
while( !queue.isEmpty()){
Node curNode = queue.poll();
Node cloneNode = cloned.get(curNode.val);
for ( Node nei : curNode.neighbors ){
List<Node> curs = nei.neighbors;
if (!cloned.containsKey(nei.val)) {
cloned.put(nei.val, new Node(nei.val));
queue.offer(nei);
}
cloneNode.neighbors.add(cloned.get(nei.val));
}
}
return cloned.get(node.val);
}
}- 패턴: Breadth-First Search, Hash Map / Hash Set
- 설명: 그래프를 깊이별이 아닌 너비로 탐색하며 노드를 순회하고, 이미 방문한 노드를 HashMap으로 저장해 중복 생성 방지 및 연결 관계를 복제한다. 큐를 이용한 BFS와 해시 맵을 이용한 노드 재사용이 핵심이다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(V + E) |
| Space | O(V) |
피드백: 그래프의 각 노드와 간선을 한 번씩 처리하므로 전체 시간은 정점과 간선 수에 비례한다. 해시맵과 큐를 사용해 중복 복제를 막고 있다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
longest-repeating-character-replacement/essaysir.java
class Solution {
public int characterReplacement(String s, int k) {
Map<Character, Integer> count = new HashMap<>();
int left = 0;
int answer = 0;
int size = 0;
for (int right = 0; right < s.length(); right++) {
// 1) right 문자를 윈도우에 넣는다
count.merge(s.charAt(right), 1, Integer::sum);
size ++;
// 2) 윈도우가 조건을 어기는 동안 left를 오른쪽으로 민다
int maxCount = Collections.max(count.values());
while ( size - maxCount > k ) {
count.merge(s.charAt(left), -1, Integer::sum);
size --;
left++;
}
// 3) 지금 윈도우는 유효하니까 답 갱신
answer = Math.max(answer,size);
}
return answer;
}
}- 패턴: Sliding Window, Hash Map / Hash Set
- 설명: 문자 재배치를 위한 윈도우를 좌우로 이동시키며 조건을 만족하는 최대 길이를 찾는 대표적 슬라이딩 윈도우 패턴이다. 부분 문자열 내 문자 빈도를 해석하기 위해 해시 맵으로 카운트를 관리한다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(n) |
| Space | O(1) |
피드백: 윈도우를 좌우로 확장하며 최대 문자 빈도를 갱신하고, 윈도우 크기에서 최대 빈도수를 뺀 값이 k를 넘으면 왼쪽 포인터를 이동한다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
reverse-bits/essaysir.java
class Solution {
public int reverseBits(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 32; i++) {
sb.append((n >>> i) & 1); // i번째 비트를 꺼내서 뒤에 붙임
}
return Integer.parseUnsignedInt(sb.toString(), 2); // 2진 문자열 → int
}
}- 패턴: Bit Manipulation, Greedy, Divide and Conquer
- 설명: 주어진 코드는 비트를 하나씩 추출해 문자열로 뒤집어 2진수로 해석하는 방식으로 비트를 반전한다. 비트 단위 조작이 핵심이며, 문자열로 처리하는 비트 조합의 아이디어가 Bit Manipulation에 해당한다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(32) |
| Space | O(1) |
피드백: 고정 길이 반복으로 비트를 뒤집고, 이진 문자열을 정수로 변환한다.
개선 제안: 현재 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
There was a problem hiding this comment.
Stringbuilder 대신에
0으로부터 시작해서 << 로 한번 쉬프팅하고
(n >>> i) & 1의 값으로 or 연산해 보시는건 어떨까요?
There was a problem hiding this comment.
한 번 다른 방법으로도 풀어보도록 하겠습니다!!
There was a problem hiding this comment.
깔끔하게 잘 리팩터랭 하셨네요! 근데 진짜 문자열 연산 필요 없어요 제가 자바로 풀어봤거든요!
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
palindromic-substrings/essaysir.java
class Solution {
public int countSubstrings(String s) {
// 해당 substring 을 했을 때, 몇 개의 palidrome 이 존재하는 가 ?
int answer = 0;
for ( int lt = 0; lt < s.length(); lt++){
for ( int rt = lt+1; rt <= s.length(); rt++){
String curStr = s.substring(lt,rt);
if (validatePalindrome(curStr)){
answer++;
}
}
}
return answer;
}
private boolean validatePalindrome(String s){
int len = s.length();
for ( int i = 0; i < len/2; i++){
if ( s.charAt(i) != s.charAt(len -i -1)){
return false;
}
}
return true;
}
}- 패턴: Brute Force, Dynamic Programming, Two Pointers, Hash Map / Hash Set, Hash Map / Hash Set
- 설명: 모든 부분 문자열을 순차 탐색하고 각 문자열이 팰린드롬인지 검사하는 브루트 포스식 풀이이며, 부분 문자열 생성과 팰린드롬 검사 로직으로 구성되어 있습니다. DP나 슬라이딩 윈도우 등의 최적화 패턴은 사용되지 않습니다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(n^3) |
| Space | O(1) |
피드백: 부분 문자열 생성과 팔린드롬 검사 때문에 범위가 큰 입력에서 비효율적입니다.
개선 제안: 고려해볼 만한 대안: 확장 중심(center expansion) 기법으로 O(n^2) 시간, O(1) 공간 구현이 가능합니다. 또는 다이나믹 프로그래밍으로도 개선할 수 있습니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
There was a problem hiding this comment.
🏷️ 알고리즘 패턴 분석
longest-common-subsequence/essaysir.java
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int n = text1.length(), m = text2.length();
int[][] dp = new int[n + 1][m + 1]; // 0행/0열은 자동으로 0
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[n][m];
}
}- 패턴: Dynamic Programming
- 설명: 두 문자열의 부분수열 길이를 DP 배열로 상태를 누적해 계산하는 전형적인 DP 문제로, 이전 상태의 결과를 이용해 현재 값을 도출하는 패턴입니다.
📊 시간/공간 복잡도 분석
| 복잡도 | |
|---|---|
| Time | O(n * m) |
| Space | O(n * m) |
피드백: 이중 루프와 2차원 DP 배열로 모든 부분문제 값을 저장해 두었다. 문자열 길이에 비례한 시간과 공간이 필요하다.
개선 제안: 현 구현이 적절해 보입니다.
💡 풀이에 시간/공간 복잡도를 주석으로 남겨보세요!
|
|
||
| for ( int lt = 0; lt < s.length(); lt++){ | ||
| for ( int rt = lt+1; rt <= s.length(); rt++){ | ||
| String curStr = s.substring(lt,rt); |
There was a problem hiding this comment.
@essaysir 여기 로직 보면, 매번 모든 substring을 자르고 팰린드롬을 검사해서 O(N^3) 시간이 소요되는데, 각 인덱스를 중심점으로 잡고 양옆으로 확장해 나가는 방식을 사용하면 O(N^2) 시간 복잡도와 O(1) 공간 복잡도로 훨씬 효율적으로 최적화할 수 있을 것 같습니다. 한번 참고해보셔요.
| class Solution { | ||
| public int characterReplacement(String s, int k) { | ||
| Map<Character, Integer> count = new HashMap<>(); | ||
| int left = 0; | ||
| int answer = 0; | ||
| int size = 0; | ||
|
|
||
| for (int right = 0; right < s.length(); right++) { | ||
| // 1) right 문자를 윈도우에 넣는다 | ||
| count.merge(s.charAt(right), 1, Integer::sum); | ||
| size ++; | ||
| // 2) 윈도우가 조건을 어기는 동안 left를 오른쪽으로 민다 | ||
| int maxCount = Collections.max(count.values()); | ||
| while ( size - maxCount > k ) { | ||
| count.merge(s.charAt(left), -1, Integer::sum); | ||
| size --; | ||
| left++; | ||
| } | ||
|
|
||
| // 3) 지금 윈도우는 유효하니까 답 갱신 | ||
| answer = Math.max(answer,size); | ||
| } | ||
|
|
||
| return answer; | ||
| } | ||
| } |
There was a problem hiding this comment.
@essaysir HashMap과 Collections.max()를 활용해서 슬라이딩 윈도우 조건을 깔끔하게 작성해주셨네요!
다만 이 문제에서는 알파벳 대문자 26개만 다루는 조건상, int[] count = new int[26] 크기의 배열을 사용하고, maxCount 변수를 매 루프마다 새로 구하는 대신 maxCount = Math.max(maxCount, ++count[ch]) 형태로 추적하면 HashMap 오버헤드와 max 탐색 과정을 줄여 실행 속도를 훨씬 향상시킬 수 있을 것 같습니다.
There was a problem hiding this comment.
맞아요 똑같은 O(N)이지만 이건 실행 시간 차이가 좀 나더군요
| Node cloneNode = cloned.get(curNode.val); | ||
|
|
||
| for ( Node nei : curNode.neighbors ){ | ||
| List<Node> curs = nei.neighbors; |
There was a problem hiding this comment.
@essaysir 한 가지 사소한 부분인데요, 반복문 내부에 선언된 List curs = nei.neighbors; 변수가 아래 로직에서 사용되지 않는 것 같아서 정리하는게 좋아보입니다.
There was a problem hiding this comment.
@dolphinflow86 좋은 코드 리뷰 감사합니다!! 하면서, 다음에 풀면서는 위에서 말씀하신 사항들에 대해서 더 생각해보고 풀도록 하겠습니다!! ㅎㅎ
There was a problem hiding this comment.
너무 깔끔하게 잘 해결하셨네요,
공간복잡도 최적화도 한번 해보시죠!
답안 제출 문제
작성자 체크 리스트
In Review로 설정해주세요.검토자 체크 리스트
Important
본인 답안 제출 뿐만 아니라 다른 분 PR 하나 이상을 반드시 검토를 해주셔야 합니다!