문제
https://school.programmers.co.kr/learn/courses/30/lessons/81303
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
알고리즘
스택, 구현
접근
추가와 삭제가 빈번 -> linked List 이용
결과 : 정확성 통과, 효용성 all 시간초과
why?
요소의 개수는 최대 1000,000(백만 개)이고 cmd 연산은 최대 200,000(이십 만)개 임.
Linked List로 add 하는 경우
- 내부적으로 포인터 사용하니깐, 해당 인덱스에 있는 요소의 포인터를 새로 추가한 요소에 잇고, 새로 추가한 요소 포인터도 다음을 가리키도 하면 끝이지 않나? 그럼 O(1)
remove 하는 경우
- 마찬가지로 내부 포인터 연결 끊어주고 이어주면 끝? 그럼 O(1)
여기를 잘못 알고 있었음!!
삭제는 O(1)이 맞다. 하지만 삭제할 요소를 찾는 것이 O(n)이다. Linked List를 한 줄로 이어진 자료구조로 생각해서 바로 배열처럼 메모리 공간을 찾는 줄 알았는데, 실제로 메모리를 일렬로 저장되어 있지 않기 때문에 연결될 노드들을 항상 따라가야한다.
stack을 이용하지만 Push, Pop 연산만 함. 그럼 얘도 O(1)
객체 생성 비용과 이용 비용이 있지만 시간초과가 날 정도인가? 그럼 이런 문제는 더 효용성을 따져야 하는건데 기준을 모르겠네.
=> 아님. 내가 푼 알고리즘은 최종적으로 O(n^2)이었음.
🪀 실제 배열에 연산을 하는 대신, 인덱스만으로 연산하기
옳은 풀이법을 찾아봤다.
linked List의 핵심 데이터인 next, prev 만 따로 관리하는 방법이다. 문제에서 반환해야하는 것은 각 행이 삭제 되었는지 여부이다. 그래서 실제 배열을 선언하고 삽입과 삭제 연산을 하는 것이 아니라, 인덱스만으로 연산을 한다.
import java.util.*;
class Solution {
public String solution(int n, int k, String[] cmd) {
// 리스트에 요소 넣기
int [] up = new int[n+2];
int [] down = new int[n+2];
for(int i=0; i<n+2; i++){
up[i] = i+1;
down[i] = i-1;
}
k++;
// 삭제 원소 관리할 스택
Stack<Integer> stack = new Stack();
for(String c : cmd){
String [] str = c.split(" ");
char ch = str[0].charAt(0);
if(ch == 'D'){
int x = Integer.parseInt(str[1]);
for(int i=0; i<x; i++){
k = up[k];
}
}
else if(ch == 'U'){
int x = Integer.parseInt(str[1]);
for(int i=0; i<x; i++){
k = down[k];
}
}
// 삭제
else if(ch == 'C'){
stack.add(k);
// stack에 정보 넣음
up[down[k]] = up[k];
down[up[k]] = down[k];
if(n < up[k]){
k = down[k];
} else {
k = up[k];
}
}
// 복구
else if(ch == 'Z'){
int cur = stack.pop();
up[down[cur]] = cur;
down[up[cur]] = cur;
}
}
char [] answer = new char[n];
Arrays.fill(answer, 'O');
for(int i: stack) {
answer[i-1] = 'X';
}
return new String(answer);
}
}
U\D 연산
- 단순히 인덱스를 증가시키면 안되고, 타고 타고 들어가면서 접근해야한다.
삭제 연산
- 복구할 스택에 k(현재 선택된 행)를 추가하여, 복구 작업 때 이용하도록 한다.
- k가 제거 되니, k의 down, up 이 서로를 가리키도록 업데이트 한다.
- 이때, k가 가장 마지막 노드일 때는 k는 down 노드를 가리킨다(문제 그대로)
복구 연산
- stack에서 가장 최근에 삭제한 행을 뽑는다.
- k를 다시 이어줘야 하니, k의 up, down 이 k를 가리키도록 업데이트 한다.
출력
- 스택에 남아있으면, 제거 된 행이니 'x' 표시한다.
새롭게 배운 점
linked List를 실제 데이터 삽입, 삭제 없이 up, down 배열을 통해서만 관리할 수 있구나를 알게됐다. 이 문제 처럼 무슨 데이터가 들어있는지 보여줄 필요가 없을 때는 이렇게 인덱스만 가지고 연산하면 훨씬 빠르구나.
'알고리즘 > 프로그래머스' 카테고리의 다른 글
| [프로그래머스] level2 - 짝지어 제거하기 (0) | 2025.03.17 |
|---|---|
| [프로그래머스] 택배 배달과 수거하기 level2 (Java) - 그리디 (0) | 2025.01.31 |
| [프로그래머스] 석유 시추 - level2(Java) (1) | 2025.01.30 |
| [프로그래머스] 퍼즐 게임 챌린지 level2- JAVA (0) | 2025.01.27 |
| [프로그래머스] SQL Level 3 모음(~ing) (0) | 2024.03.01 |