[코테] 프로세스 (스택/큐)

ekil·2026년 4월 5일

코딩테스트

목록 보기
8/15

프로세스 (스택/큐)

2026.4.1. & 2026.4.5.

https://school.programmers.co.kr/learn/courses/30/lessons/42587

핵심 개념

큐에서 "처리 못하면 뒤로 보낸다"는 동작의 구현 방법 접근법!

순회하면서 인덱스로 값을 찾지 않고, 원본 배열을 직접 수정하며 앞에서부터 꺼내고 맨뒤에 넣으며 처리하는 방식
=> 프로세스를 추적하기 위해 요소가 수정되는 배열이 필요한데, 원본 자체를 수정할 경우 index가 맞지 않게 됨. 결국 location과의 비교도 불가능해짐.

  1. shift(), push()를 사용해 index 없이 처리하기

  2. for ... of 루프와 while 루프의 차이

  • for ... of Arr: Arr가 변경되어도 순회 대상에 반영 X, 처음 판별한 값으로 고정됨 => push()로 뒤에 추가한 요소가 순회에 포함되지 않음

  • 코드를 읽을 때 이터레이터(다음에 어떤 요소를 줄지 기억하는 객체)를 한번 만들고 그 만든 걸 계속 참조하는 구조임. 루프 안에서 배열이 변경되어도 for ... of 부분을 재평가하지 않음.

  • while: 매 반복마다 조건을 새로 평가 => push()로 뒤에 추가한 요소가 순회에 포함됨

  • 반복할 코드를 실행하고, 반복문을 더 실행할지 말지를 알기 위해선 while 뒤의 조건을 확인해야 하는 구조니까 그런 듯!!

  • (추가) 일반 for문은 while처럼 매 반복마다 조건을 재평가함! (다만 이 문제에선 while이 더 적절함)

  • for문: 조건을 매번 배열에서 직접 읽음, 변경된 사항이 반영됨

  • for ... of: 시작할 때 순회 계획을 미리 만들어두고, 그걸 따라감, 변경된 사항이 반영되지 않음

for (let i = 0; i < arr.length; i++)
//                  ↑ 매 반복마다 arr.length를 그 시점에 읽음
for (let item of arr)
// 시작 시점에 이터레이터 생성 → "1번째, 2번째, 3번째 줄게"가 확정됨
// 이후 arr가 바뀌어도 이터레이터는 모름

=> 배열 자체를 보는 게 아니라 다음에 어떤 요소를 실행할지 미리 계획한 객체여서, 계획이 세워진 후에 배열이 바뀌어도 계획이 바뀌지 않음

  • 이 문제에서 for문보다 while이 적절한 이유: i를 증가시키지 않고 순회해야 함. shift로 요소를 제거하면 length가 줄어들면서 i는 증가하니까 모든 요소를 순회하지 못함. for문에서 shift를 사용하려면 i를 증가시키지 않고 고정해야 함 = while과 같음
    => shift처럼 배열의 길이를 변경하면서 순회할 때는 while이 가장 적합함
  • for문에서 push 사용 시에는 iarr.length가 같은 속도로 증가 => 무한루프 위험 있음
  • 이번 문제 풀이에선 while, push, shift의 조합이어서 length 변화 없이 모든 요소 순회 가능. 종료 조건이 논리적으로 보장되어야 안전한데, 이 문제에서는 우선순위 최고값이 반드시 존재하므로 종료가 보장됨!
  1. location 추적 방법: priorites 배열에서 값과 index를 함께 묶어 재가공한 배열 만들어 사용
  • [index, value]의 배열로 구성해도 되고, {priority, index} 같은 객체로 구성해도 무방

내 풀이

function solution(priorities, location) {
    const array = priorities.map((p, idx) => [idx, p]);
    let count = 0;
    
    while (array.length > 0) {
        const item = array.shift(); // 실행할 요소 꺼내기
        if (array.length === 0) { // ⚠️ 불필요한 코드임
            // 마지막 남은 요소면 체크 없이 처리 후 끝냄
            count += 1;
            return count;
        }
        
        // 대기 중인 프로세스 우선순위 체크
        const cannotProceed = array.findIndex(arr => item[1] < arr[1]) !== -1;
        if (cannotProceed) {
            array.push(item); // 맨뒤에 다시 넣기
        } else {
            // 프로세스 실행
            count += 1;
            if (item[0] === location) {
                return count;
            }
        }
    }
}

개선된 풀이

function solution(priorities, location) {
    const array = priorities.map((p, idx) => [idx, p]);
    let count = 0;
    
    while (array.length > 0) {
        const item = array.shift(); // 실행할 요소 꺼내기
        // ✅ length가 0일 경우 아래 cannotProceed가 true일 수 없음. 별도 처리 불필요!
        
        // 대기 중인 프로세스 우선순위 체크
        // ✅ 가독성을 고려해 some으로 변경
        const cannotProceed = array.some(arr => item[1] < arr[1]);
        if (cannotProceed) {
            array.push(item); // 맨뒤에 다시 넣기
        } else {
            // 프로세스 실행
            count += 1;
            if (item[0] === location) {
                return count;
            }
        }
    }
}

핵심 차이

  • array의 마지막 요소를 꺼냈을 때, array.push(item)을 실행해 무한루프에 빠질 것을 대비(?)해 얼리 리턴 코드를 넣은 거였는데, 생각해보면 그런 상황은 발생할 수 없음. 불필요한 분기를 없앰.
  • findIndexsome 둘 다 조건 충족하면 즉시 종료하므로 효율은 같은데, 체크하려는 것이 index가 아니라 조건을 만족하는 값이 존재하는가 이므로, some이 의미에 더 부합함.

막혔던 포인트

  1. 원본 배열 자체를 수정하고 순회하면서 index를 변수로 두어 따로 관리하려 했음 => 원본 배열의 길이가 줄어드는데 index는 계속 증가, priorites[index]undefined가 되면서 비교 로직이 부정확해짐

  2. 그럼 그렇게 안하면서 원래 위치를 특정할 방법이 도대체 무엇인가??? (location 추적 방식)
    => 위에서 짚은 대로 값과 원래 인덱스를 함께 저장한 데이터를 정의해두고 사용
    => shift()로 꺼내도 원래 위치가 함께 있는 구조 ([[0, 2], [1, 1], [2, 3]]: [원래인덱스, 우선순위] 형태)

  3. for ... of로 사용했을 때 원본 배열 수정이 반영되지 않는 현상
    => 반복 대상인 배열이 반복문 안에서 수정되고 그것이 반영되어야 한다면, 매 반복 실행 시마다 조건을 다시 체크하는 while을 사용

  4. index를 특정하려는 생각 버리기!
    => 큐에서는 실행할 요소를 특정할 때 배열에서 index로 값을 판단하지 않아도, shift()로 꺼낸 맨 첫번째 요소가 바로 "현재 처리할 요소"임!!

풀면서 찾은 개념

  • splice를 찾았는데, shiftpush만 이용하면 되는 거였음

다음에 비슷한 문제 만나면

  1. 주어진 데이터의 '위치'를 알아야 하고 요소가 unique하지 않다면 => 처음 주어진 값에서 위치를 같이 저장한 형태로 재가공해 사용하기
  2. 큐 문제라면 실행할 배열의 첫번째 요소부터 처리하면 되니까 shift를 사용하고, 문제 상황에 맞게 push도 사용하기. 반대로 스택 문제라면 pop을 사용해보면 되지 않을까?
  3. for...of문은 순회 시작 시 이터레이터를 한 번 생성하므로, 이후 배열이 변경되어도 반영되지 않음. 변경된 배열을 순회해야 한다면 매 반복마다 조건을 재평가하는 while을 사용할 것.

휴..어려웠다..

profile
좋아하는 일을 잘함으로써 먹고살고 싶은 프론트엔드 개발자입니다.

0개의 댓글