<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>hahyun.dev</title>
    <link>https://gkgus0201.tistory.com/</link>
    <description>https://github.com/songhahyun</description>
    <language>ko</language>
    <pubDate>Thu, 14 May 2026 11:31:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hahyun</managingEditor>
    <item>
      <title>프로그래머스 Python 스택/큐 문제 풀이 - 기능 개발, 주식가격</title>
      <link>https://gkgus0201.tistory.com/11</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 기능 개발&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 기능보다 먼저 개발될 수 있고, 이때 뒤에 있는 기능은 앞에 있는 기능이 배포될 때 함께 배포됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 배포되어야 하는 순서대로 작업의 진도가 적힌 정수 배열 progresses와 각 작업의 개발 속도가 적힌 정수 배열 speeds가 주어질 때 각 배포마다 몇 개의 기능이 배포되는지를 return 하도록 solution 함수를 완성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한 사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업의 개수(progresses, speeds배열의 길이)는 100개 이하입니다.&lt;/li&gt;
&lt;li&gt;작업 진도는 100 미만의 자연수입니다.&lt;/li&gt;
&lt;li&gt;작업 속도는 100 이하의 자연수입니다.&lt;/li&gt;
&lt;li&gt;배포는 하루에 한 번만 할 수 있으며, 하루의 끝에 이루어진다고 가정합니다. 예를 들어 진도율이 95%인 작업의 개발 속도가 하루에 4%라면 배포는 2일 뒤에 이루어집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;progresses&lt;/th&gt;
&lt;th&gt;speeds&lt;/th&gt;
&lt;th&gt;return&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[93, 30, 55]&lt;/td&gt;
&lt;td&gt;[1, 30, 5]&lt;/td&gt;
&lt;td&gt;[2, 1]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[95, 90, 99, 99, 80, 99]&lt;/td&gt;
&lt;td&gt;[1, 1, 1, 1, 1, 1]&lt;/td&gt;
&lt;td&gt;[1, 3, 2]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 설명&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 #1&lt;br /&gt;첫 번째 기능은 93% 완료되어 있고 하루에 1%씩 작업이 가능하므로 7일간 작업 후 배포가 가능합니다.&lt;br /&gt;두 번째 기능은 30%가 완료되어 있고 하루에 30%씩 작업이 가능하므로 3일간 작업 후 배포가 가능합니다. 하지만 이전 첫 번째 기능이 아직 완성된 상태가 아니기 때문에 첫 번째 기능이 배포되는 7일째 배포됩니다.&lt;br /&gt;세 번째 기능은 55%가 완료되어 있고 하루에 5%씩 작업이 가능하므로 9일간 작업 후 배포가 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 7일째에 2개의 기능, 9일째에 1개의 기능이 배포됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 #2&lt;br /&gt;모든 기능이 하루에 1%씩 작업이 가능하므로, 작업이 끝나기까지 남은 일수는 각각 5일, 10일, 1일, 1일, 20일, 1일입니다. 어떤 기능이 먼저 완성되었더라도 앞에 있는 모든 기능이 완성되지 않으면 배포가 불가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 5일째에 1개의 기능, 10일째에 3개의 기능, 20일째에 2개의 기능이 배포됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42586&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42586&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기능개발 작업의 진도 &lt;code&gt;progresses&lt;/code&gt; 와 속도 &lt;code&gt;speeds&lt;/code&gt;를 &lt;code&gt;(progress, speed)&lt;/code&gt; 의 튜플 형태로 큐에 적재&lt;/li&gt;
&lt;li&gt;while 문을 돌면서 queue 가 비어있을 때까지 아래 task 를 반복
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫번째 for 문: queue 에서 작업을 하나씩 꺼내서(popleft) 작업의 진도 &lt;code&gt;progress&lt;/code&gt; 를 속도 &lt;code&gt;speed&lt;/code&gt; 만큼 업데이트하고, 다시 큐에 적재(append)&lt;/li&gt;
&lt;li&gt;두번째 for 문: queue 에서 작업을 하나 꺼내서(popleft) 작업의 진도가 100 이상인지 체크, 진도가 100 이상이면 continue, 아니면 break 해서 for 문 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간 복잡도: &lt;b&gt;O(N^2)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;외부 while queue 루프:&lt;/b&gt; 모든 작업이 완료될 때까지 반복되므로, 만약 작업 진도가 1, 작업 속도가 1인 작업이 100까지 가려면, 최대 &lt;b&gt;100번&lt;/b&gt; 가량 반복&lt;/li&gt;
&lt;li&gt;&lt;b&gt;첫 번째 내부 for 루프:&lt;/b&gt; 큐의 모든 요소를 순회하며 진도를 업데이트. queue 에서 작업을 하나씩 꺼내고 다시 넣는 popleft, append 메소드의 시간 복잡도는 &lt;b&gt;O(1)&lt;/b&gt;, 큐의 크기가 N 이라 할 때 &lt;b&gt;O(N)&lt;/b&gt; 소요&lt;/li&gt;
&lt;li&gt;&lt;b&gt;두 번째 내부 for 루프:&lt;/b&gt; 배포 가능한 작업을 확인하기 위해 큐의 앞부분부터 하나씩 확인. 이 역시 최대 &lt;b&gt;O(N)&lt;/b&gt; 소요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결과적으로, 전체 복잡도는 &lt;b&gt;(총 소요 일수 X N)&lt;/b&gt; 에 비례, 문제의 제한 사항에서 N &amp;le; 100 으로 작기 때문에 통과에는 지장이 없으나, 만약 작업 개수가 10만 개 이상으로 늘어난다면 성능 저하가 발생할 수 있는 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from collections import deque

def solution(progresses, speeds):
    queue = deque()
    answer = []

    # queue에 작업을 튜플 형태로 넣기 (progress, speed)
    for p, s in zip(progresses, speeds):
        queue.append((p,s))

    while queue:
        # queue에 들어있는 작업 progress 업데이트
        for i in range(len(queue)): 
            p, s = queue.popleft()
            p += s
            queue.append((p, s))

        cnt = 0

        # queue에 들어있는 작업을 꺼내서 progress가 100 이상인 경우 count
        for i in range(len(queue)):
            curr_p, curr_s = queue.popleft()

            if curr_p &amp;gt;= 100:
                cnt += 1
                continue

            else:
                queue.appendleft((curr_p, curr_s))
                break

        # count가 1 이상인 경우만 answer 리스트에 append
        if cnt &amp;gt; 0:
            answer.append(cnt)

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 리팩토링&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 작업의 &lt;b&gt;진도가 100%가 되기까지 며칠이 걸리는지&lt;/b&gt;를 먼저 계산
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 작업별 남은 일수를 한 번의 루프(O(N))로 계산&lt;/li&gt;
&lt;li&gt;계산된 일수 리스트를 한 번 순회(O(N))하면서 앞의 작업보다 빨리 끝나는 것들을 묶어서 배포&lt;/li&gt;
&lt;li&gt;이렇게 하면 전체 시간 복잡도는 &lt;b&gt;O(N)&lt;/b&gt;이 되어, 작업의 개수가 대폭 늘어나도 매우 빠르게 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import math

def solution(progresses, speeds):
    answer = []
    # 각 작업이 완료될 때까지 필요한 '남은 일수'를 미리 계산
    # 예: [7, 3, 9]
    days = [math.ceil((100 - p) / s) for p, s in zip(progresses, speeds)]

    if not days:
        return []

    front = 0  # 기준이 되는 작업의 인덱스
    for i in range(len(days)):
        # 현재 작업(i)이 기준 작업(front)보다 더 오래 걸린다면
        if days[i] &amp;gt; days[front]:
            # 지금까지 쌓인 기능들을 배포
            answer.append(i - front)
            front = i  # 기준 작업을 현재 작업으로 변경

    # 마지막으로 남은 기능들 배포
    answer.append(len(days) - front)

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 주식가격&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초 단위로 기록된 주식가격이 담긴 배열 prices가 매개변수로 주어질 때, 가격이 떨어지지 않은 기간은 몇 초인지를 return 하도록 solution 함수를 완성하세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제한사항&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prices의 각 가격은 1 이상 10,000 이하인 자연수입니다.&lt;/li&gt;
&lt;li&gt;prices의 길이는 2 이상 100,000 이하입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;prices&lt;/th&gt;
&lt;th&gt;return&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;[1, 2, 3, 2, 3]&lt;/td&gt;
&lt;td&gt;[4, 3, 1, 1, 0]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입출력 예 설명&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1초 시점의 ₩1은 끝까지 가격이 떨어지지 않았습니다.&lt;/li&gt;
&lt;li&gt;2초 시점의 ₩2은 끝까지 가격이 떨어지지 않았습니다.&lt;/li&gt;
&lt;li&gt;3초 시점의 ₩3은 1초뒤에 가격이 떨어집니다. 따라서 1초간 가격이 떨어지지 않은 것으로 봅니다.&lt;/li&gt;
&lt;li&gt;4초 시점의 ₩2은 1초간 가격이 떨어지지 않았습니다.&lt;/li&gt;
&lt;li&gt;5초 시점의 ₩3은 0초간 가격이 떨어지지 않았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/42584&quot;&gt;https://school.programmers.co.kr/learn/courses/30/lessons/42584&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;풀이&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정렬 + 스택&lt;/li&gt;
&lt;li&gt;prices 리스트를 이중 for 문으로 순회
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 for 문은 0 부터 &lt;code&gt;len(prices)&lt;/code&gt; 까지 순회, 내부 for 문은 &lt;code&gt;(i+1)&lt;/code&gt; 부터 &lt;code&gt;len(prices)&lt;/code&gt; 까지 순회&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prices[i]&lt;/code&gt; 기준으로 다음 원소의 가격 &lt;code&gt;prices[j]&lt;/code&gt; 가 낮아지기 전까지 count + 1&lt;/li&gt;
&lt;li&gt;다음 원소의 가격 &lt;code&gt;prices[j]&lt;/code&gt; 가 &lt;code&gt;prices[i]&lt;/code&gt; 보다 낮아지면 for 문 break&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;처음 시도할 때 놓쳤던 부분은 &lt;code&gt;count += 1&lt;/code&gt; 을 if 조건절 안에 넣는게 아니라, &lt;code&gt;j&lt;/code&gt; 가 +1 증가하면 &lt;code&gt;count&lt;/code&gt; 도 +1 증가시키도록 로직을 짜야 한다는 것 (경계값 구현 실수)&lt;/li&gt;
&lt;li&gt;시간 복잡도: &lt;b&gt;O(N^2)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;외부 루프&lt;/b&gt; (for i in range(...)): prices의 길이인 n 만큼 반복&lt;/li&gt;
&lt;li&gt;&lt;b&gt;내부 루프&lt;/b&gt; (for j in range(i + 1, ...) ): 각 i에 대해 남은 요소(n-i)만큼 탐색&lt;/li&gt;
&lt;li&gt;문제의 제한사항에서 prices 의 길이는 최대 10만개이므로, 최악의 경우 &lt;b&gt;O(N^2) 코드는 최대 100억번의 연산 필요&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def solution(prices):
    answer = []

    for i in range(0, len(prices)):
        count = 0

        for j in range(i+1, len(prices)):
            count += 1

            if prices[i] &amp;gt; prices[j] :
                break

        answer.append(count)

    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;코드 리팩토링&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핵심 아이디어: 아직 가격이 떨어지지 않은 인덱스들을 스택에 보관하다가, 가격이 떨어지는 순간 스택에서 꺼내 기간을 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def solution(prices):
    n = len(prices)
    answer = [0] * n
    stack = []  # 인덱스를 저장할 스택

    for i in range(n):
        # 스택이 비어있지 않고, 현재 가격이 스택 최상단(마지막) 가격보다 떨어졌다면
        while stack and prices[stack[-1]] &amp;gt; prices[i]:
            past_index = stack.pop()
            answer[past_index] = i - past_index  # 떨어진 시점과 기록 시점의 차이 계산

        stack.append(i)

    # 반복문이 끝났음에도 스택에 남은 인덱스들 (끝까지 가격이 안 떨어진 경우)
    while stack:
        past_index = stack.pop()
        answer[past_index] = n - 1 - past_index

    return answer&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <category>스택</category>
      <category>자료구조</category>
      <category>큐</category>
      <category>프로그래머스</category>
      <author>hahyun</author>
      <guid isPermaLink="true">https://gkgus0201.tistory.com/11</guid>
      <comments>https://gkgus0201.tistory.com/11#entry11comment</comments>
      <pubDate>Tue, 5 May 2026 15:14:22 +0900</pubDate>
    </item>
  </channel>
</rss>