<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>삼시세코</title>
    <link>https://onedaythreecoding.tistory.com/</link>
    <description>삼시세끼 잔잔하게 개발하는 백엔드 지망생 수련기 : Spring boot, Algorithm</description>
    <language>ko</language>
    <pubDate>Tue, 26 May 2026 19:52:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>돌래씨</managingEditor>
    <image>
      <title>삼시세코</title>
      <url>https://tistory1.daumcdn.net/tistory/4224843/attach/50d0a512bf084d45bcdd279a6d2b5622</url>
      <link>https://onedaythreecoding.tistory.com</link>
    </image>
    <item>
      <title>[ 컴퓨터 밑바닥 책] 2장 : 프로그램 실행 (프로세스, 스레드, 코루틴)</title>
      <link>https://onedaythreecoding.tistory.com/entry/%F0%9F%A7%AD%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B0%91%EB%B0%94%EB%8B%A5-%EC%B1%85-2%EC%9E%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%BD%94%EB%A3%A8%ED%8B%B4</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;CPU 와 멀티태스킹&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU, PC 레지스터
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU는 단순한 연산장치&lt;/li&gt;
&lt;li&gt;다음 실행할 명령어 주소는 레지스터 중 PC 레지스터에서 찾음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;하드웨어 연결&lt;/li&gt;
&lt;li&gt;라이브러리 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;프로세스 : 재시작을 위해 저장해놓은 프로그램 실행 상황 데이터&lt;/li&gt;
&lt;li&gt;운영체제 : 프로세스 스케줄링 관리도구 (다른 역할도 많지만)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;프로세스 생성 오버헤드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;가벼움&lt;/li&gt;
&lt;li&gt;스레드 간 공유 리소스 문제 처리 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드는 운영체제 계층에 구현. CPU 코어 수와 무관&lt;/li&gt;
&lt;li&gt;CPU 는 스레드를 알지 못함&lt;/li&gt;
&lt;li&gt;생성 시 진입함수를 지정. CPU 가 진입함수 주소로 실행 시작&lt;/li&gt;
&lt;li&gt;프로세스 주소공간에서 스택 영역을 나누어씀. 나머지 (코드, static데이터, 힙)는 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;대기열(큐)을 사이에 둔 생성자-소비자 패턴으로 스레드풀의 스레드에 작업을 할당&lt;/li&gt;
&lt;li&gt;적절한 스레드 수
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 집약적인 작업의 경우 : 일반적으로 CPU 코어 수와 동일하면 적절&lt;/li&gt;
&lt;li&gt;입출력 집약적인 작업의 경우 : 적절값을 계산하는 이론은 있지만 절대적이지 않으므로 실제 테스트를 통해 찾는 것을 권장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드영역 : 읽기 전용. thread safe&lt;/li&gt;
&lt;li&gt;데이터(static) 영역 : 스레드 공유 리소스&lt;/li&gt;
&lt;li&gt;힙 영역 : 스레드 공유 리소스. 포인터에 의해 여러 스레드에서 접근 가능&lt;/li&gt;
&lt;li&gt;스택 영역 : 스레드 공유 리소스. 스레드별 스택영역이 구분되어있긴 하지만 접근을 제한하지는 않아서 접근 가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스레드 전용 저장소 : 스레드 전용 변수로 선언 시 인스턴스가 각각의 스레드에 복사되어 사용됨 (c 예시 : __thread in a = 1;)&lt;/li&gt;
&lt;li&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;공유 문제가 발생할 경우, 락이나 세마포어 등으로 보호해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;읽기 전용&lt;/li&gt;
&lt;li&gt;원자성 연산(atomic)으로 처리&lt;/li&gt;
&lt;li&gt;동기화 시 상호배제 (mutex, spin lock, semaphore 등 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;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;코루틴 (사용자 상태 스레드) : 사용자가 코드상으로 관리. 스레드보다 가벼움. 반환 및 재개 지점을 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;반환 및 재개지점을 만나면 저장해두고, 해당 코루틴이 재호출 되었을 때 그 지점부터 실행 재개&lt;/li&gt;
&lt;li&gt;e.g. 파이썬 : yield 키워드로 반환 및 재개지점을 지정&lt;/li&gt;
&lt;li&gt;코루틴의 상태정보는 힙 영역에 저장됨&lt;/li&gt;
&lt;li&gt;동기 방식으로 비동기 프로그래밍을 가능하게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;일반함수, 스레드와 비교
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 함수와 형식상 차이가 없음. 일반함수는 재개지점이 0개인 코루틴과 동일(코루틴의 일종)&lt;/li&gt;
&lt;li&gt;가벼운 스레드같다고 함 : 실행 중 정지 및 재개가 가능하다는 점이 운영체제가 스레드를 스케줄링하는 것과 유사. 스케줄링 제어권이 사용자에게 있는 스레드&lt;/li&gt;
&lt;li&gt;코루틴은 스레드가 없던 시절에 사용되던 기술로, 스레드 등장 후 사용되지 않다가 높은 동시성 및 성능을 처리하기 위해 조명받음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/251</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%F0%9F%A7%AD%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B0%91%EB%B0%94%EB%8B%A5-%EC%B1%85-2%EC%9E%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%BD%94%EB%A3%A8%ED%8B%B4#entry251comment</comments>
      <pubDate>Wed, 11 Jun 2025 19:02:50 +0900</pubDate>
    </item>
    <item>
      <title>[ 컴퓨터 밑바닥책] 1장 : 프로그래밍 언어부터 프로그램 실행까지 (컴파일러, 링커, 추상화)</title>
      <link>https://onedaythreecoding.tistory.com/entry/%F0%9F%A7%AD%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B0%91%EB%B0%94%EB%8B%A5%EC%B1%85-1%EC%9E%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%96%B8%EC%96%B4%EB%B6%80%ED%84%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89%EA%B9%8C%EC%A7%80-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC-%EB%A7%81%EC%BB%A4-%EC%B6%94%EC%83%81%ED%99%94</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** &lt;a href=&quot;https://github.com/jeeheaG/computer-deep-dive-study/blob/ba55e135bb24c501cbccf68da3e217a996bedaca/week2/jeehea/1%EC%9E%A5_%EC%A7%80%ED%98%9C.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;깃허브 md파일&lt;/a&gt;에서 가독성이 좀더 좋습니다!&lt;/p&gt;
&lt;figure id=&quot;og_1742316146463&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;computer-deep-dive-study/week2/jeehea/1장_지혜.md at ba55e135bb24c501cbccf68da3e217a996bedaca &amp;middot; jeeheaG/computer-deep-dive-s&quot; data-og-description=&quot;⛵ 클클 7기 컴퓨터 탐험대. Contribute to jeeheaG/computer-deep-dive-study development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jeeheaG/computer-deep-dive-study/blob/ba55e135bb24c501cbccf68da3e217a996bedaca/week2/jeehea/1%EC%9E%A5_%EC%A7%80%ED%98%9C.md&quot; data-og-url=&quot;https://github.com/jeeheaG/computer-deep-dive-study/blob/ba55e135bb24c501cbccf68da3e217a996bedaca/week2/jeehea/1%EC%9E%A5_%EC%A7%80%ED%98%9C.md&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cVrVMI/hyYqP0HzAj/AEZ0jIJuZRbBf5J6aK3upk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bA9UVH/hyYvpFLZAz/QUMTJTR9TjTAgoy6hYQc3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/jeeheaG/computer-deep-dive-study/blob/ba55e135bb24c501cbccf68da3e217a996bedaca/week2/jeehea/1%EC%9E%A5_%EC%A7%80%ED%98%9C.md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jeeheaG/computer-deep-dive-study/blob/ba55e135bb24c501cbccf68da3e217a996bedaca/week2/jeehea/1%EC%9E%A5_%EC%A7%80%ED%98%9C.md&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cVrVMI/hyYqP0HzAj/AEZ0jIJuZRbBf5J6aK3upk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bA9UVH/hyYvpFLZAz/QUMTJTR9TjTAgoy6hYQc3K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;computer-deep-dive-study/week2/jeehea/1장_지혜.md at ba55e135bb24c501cbccf68da3e217a996bedaca &amp;middot; jeeheaG/computer-deep-dive-s&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;⛵ 클클 7기 컴퓨터 탐험대. Contribute to jeeheaG/computer-deep-dive-study development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;궁금했던 점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바는 패키지나 라이브러리를 언제 어떻게 불러올까 60p
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM 의 클래스 로드 과정&lt;br /&gt;&amp;nbsp;: 클래스 로딩 &amp;rarr; 링크 &amp;rarr; 초기화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;찾는 클래스가 없으면&amp;nbsp;ClassNotFoundException&amp;nbsp;발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JVM의 클래스 로딩 방식
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;import&amp;nbsp;문을 사용한 정적 로딩
&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;/li&gt;
&lt;li&gt;Reflection 또는 ClassLoader를 활용한 동적 로딩
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Reflection 사용 시 성능 저하 가능성 (JIT최적화 적용 불가, 보안 검증 추가)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정적 로딩 :&amp;nbsp;import&amp;nbsp;문 (컴파일 시)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바 컴파일러(javac)가&amp;nbsp;import된 클래스를 클래스 경로(CLASS_PATH)에서 찾아 .class 파일을 참조함&lt;/li&gt;
&lt;li&gt;실행 시점에는 이미 클래스를 JVM이 로드해둔 상태&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동적 로딩 : Reflection 또는 ClassLoader를 이용 (런타임)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Class.forName() 및 ClassLoader 를 활용. JVM 이 로드&lt;/li&gt;
&lt;li&gt;설치된 라이브러리 여부에 따라 동작 변경 가능&lt;/li&gt;
&lt;li&gt;실행 시점에 &quot;java.util.ArrayList&quot; 클래스가 존재하면 동적으로 로드하는 코드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742315972886&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class DynamicLoadingExample {
    public static void main(String[] args) {
        try {
            Class&amp;lt;?&amp;gt; clazz = Class.forName(&quot;java.util.ArrayList&quot;); // 런타임에 로딩
            Object obj = clazz.getDeclaredConstructor().newInstance();
            System.out.println(&quot;클래스 로딩 성공: &quot; + obj.getClass().getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CLASSPATH 에 여러 경로 지정 시 우선순위는?
&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;옵션이 환경변수보다 우선순위가 높음&lt;/li&gt;
&lt;li&gt;구분자로 구분된 값들 중 앞쪽 값이 우선순위가 높음&lt;/li&gt;
&lt;li&gt;지정 방법 2가지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바 실행 시 classpath 지정 옵션(-cp, -classpath)을 사용&lt;/li&gt;
&lt;li&gt;환경변수 CLASSPATH 로 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742316068268&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//// 자바 실행 시 classpath 지정 옵션(-cp, -classpath)을 사용
// 윈도우
set CLASSPATH=C:\myProject\classes;C:\myLib\myJar.jar

//맥
export CLASSPATH=/home/user/myProject/classes:/home/user/myLib/myJar.jar


//// 환경변수 CLASSPATH 로 지정
java -cp &quot;C:\path1;C:\path2;C:\path3&quot; MyApp&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;중간코드에 추가적인 최적화가 진행되기도 한다.&amp;rarr; ? 그런 경우가 언제지 46p&lt;/li&gt;
&lt;li&gt;순환 구문 내 순환 상태와 관계없이 계산 가능한 값이 있다면 순환 구문 외부에서 먼저 진행하는 등의 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정리&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저수준의 세부사항을 제어할 수 있는 고수준의 추상적인 표현
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU 트랜지스터 &amp;rarr; 기계 명령어 &amp;rarr; (어셈블리어) &amp;rarr; 고급 언어&lt;/li&gt;
&lt;li&gt;구문 syntax
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업 수행 명령 &amp;rarr; 문(statement) 으로 정의&lt;/li&gt;
&lt;li&gt;상황에 따른 실행 &amp;rarr; 조건문&lt;/li&gt;
&lt;li&gt;재귀와 반복 &amp;rarr; 반복문&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;구문 트리 : 모든 코드는 수열 표현식 또는 트리구조로 표현 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가상머신(인터프리터) : CPU 별 기계어로 변환&lt;/li&gt;
&lt;li&gt;컴파일러
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상적인 &amp;lsquo;구문&amp;rsquo;을 기계어(또는 바이트코드)로 번역하는 일 = 컴파일&lt;/li&gt;
&lt;li&gt;하나의 복잡한 프로그램일 뿐&lt;/li&gt;
&lt;li&gt;소스파일, 코드는 텍스트일 뿐&lt;/li&gt;
&lt;li&gt;소스파일 &amp;rarr; (컴파일) &amp;rarr; 실행파일(기계어 오브젝트파일 or 바이트코드)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자세한 과정
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;구문분석 : 코드 &amp;rarr; (어휘분석) &amp;rarr; 토큰 &amp;rarr; (해석parsing) &amp;rarr; 구문트리&lt;/li&gt;
&lt;li&gt;의미분석(semantic analysis) : 구문트리 검사. 컴파일 오류 검사&lt;/li&gt;
&lt;li&gt;중간코드(IR Code) 생성, 어셈블리어 코드로 변환&lt;/li&gt;
&lt;li&gt;어셈블리어를 기계어로 변환&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;심벌 : 모든 변수 이름. 링커의 관심사는 전역변수와 함수 이름.&lt;/li&gt;
&lt;li&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;재배치 : 컴파일 시 표시해둔 임시 주소를 찾아, 실제 메모리상 주소 삽입&lt;/li&gt;
&lt;li&gt;대상object파일에 포함되어있는 데이터 (컴파일러가 기록)
&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;데이터영역 : 소스파일의 전역변수 (지역변수는 대상파일에 포함x)&lt;/li&gt;
&lt;li&gt;심벌 테이블 : 외부 심벌 정보. 공급 / 수요로 나누어 기록&lt;/li&gt;
&lt;li&gt;.relo.text와 .relo.data : 컴파일 시점에 메모리 주소를 확정할 수 없는 변수 명령어의 위치와 관련 데이터 저장
&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;라이브러리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 : 소스파일을 개별적으로 미리 컴파일해두고 해더파일 제공. 정적 링크
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. .lib, .a&lt;/li&gt;
&lt;li&gt;컴파일 시 속도가 빠름&lt;/li&gt;
&lt;li&gt;라이브러리 내용을 직접 복사함. 공간 효율 떨어짐&lt;/li&gt;
&lt;li&gt;라이브러리 내용이 변경될 때마다 실행파일도 다시 컴파일해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;프로그램 실행 시점에 동적링크&lt;/li&gt;
&lt;li&gt;동적링크 방식 2가지
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로그램이 메모리에 적재(load)될 때 loader에 의해서&lt;/li&gt;
&lt;li&gt;런타임에 코드가 직접 동적 링크 실행&lt;/li&gt;
&lt;li&gt;: 이 경우 실행파일에 라이브러리 정보 저장x&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&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;라이브러리가 수정되면 라이브러리만 다시 컴파일하면 됨. 수정 용이&lt;/li&gt;
&lt;li&gt;플러그인 등으로 런타임에도 쉬운 기능 확장&lt;/li&gt;
&lt;li&gt;라이브러리를 별도로 컴파일하므로 여러 언어를 혼합하여 사용 가능&lt;/li&gt;
&lt;li&gt;코드 재사용 효율성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;라이브러리를 임의의 메모리 절대주소로 참조할 수 없음 &amp;rarr; ??&lt;/li&gt;
&lt;li&gt;실행파일 만으로 실행 불가. 종속된 동적 라이브러리를 제공해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가상메모리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상 메모리 - 물리메모리 간 위치 맵핑 : 사상(mapping)관계. 이를 기록한 것이 페이지 테이블. 페이지 테이블은 프로세스마다 가짐&lt;/li&gt;
&lt;li&gt;메모리구조(영역 순서)는 고정&lt;/li&gt;
&lt;li&gt;링커가 프로그램이 실제로 물리메모리 어디에 존재하는지 몰라도 되도록 해줌&lt;/li&gt;
&lt;li&gt;링커가 프로그램 실행파일만으로도 심벌의 실제 메모리 주소를 예측할 수 있게 해줌&lt;/li&gt;
&lt;li&gt;논리상으로만 존재함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;추상화는 저수준의 세부계층을 몰라도 프로그래밍을 가능하게 해주지만, 제대로 다룰 줄 알려면 저수준까지의 이해를 갖추어야 함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>CS</category>
      <category>CS</category>
      <category>가상메모리</category>
      <category>링커</category>
      <category>추상화</category>
      <category>컴파일러</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/250</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%F0%9F%A7%AD%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%B0%91%EB%B0%94%EB%8B%A5%EC%B1%85-1%EC%9E%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%96%B8%EC%96%B4%EB%B6%80%ED%84%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EC%8B%A4%ED%96%89%EA%B9%8C%EC%A7%80-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%AC-%EB%A7%81%EC%BB%A4-%EC%B6%94%EC%83%81%ED%99%94#entry250comment</comments>
      <pubDate>Wed, 19 Mar 2025 01:43:40 +0900</pubDate>
    </item>
    <item>
      <title>[ 객체 개구리책] 4,5장 자바가 확장한 객체지향 &amp;amp; SOLID</title>
      <link>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-45%EC%9E%A5-%EC%9E%90%EB%B0%94%EA%B0%80-%ED%99%95%EC%9E%A5%ED%95%9C-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-SOLID</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;정리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4장 자바가 확장한 객체지향&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;abstract
&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;추상 메서드 : 상위 클래스가 구체적인 기능을 구현할 수 없으나 그 기능을 구현(오버라이딩) 및 접근할 수 있도록 강제하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;생성자 = 객체 생성자 메서드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;초기화 블록
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static 블록 : 해당 클래스가 사용되는 시점에 클래스가 static 영역에 로드되면서 실행됨
&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;/li&gt;
&lt;li&gt;인스턴스 블록 : 인스턴스 생성 시 실행. 거의 안쓰임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;final
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;final 클래스 : 상속 불가&lt;/li&gt;
&lt;li&gt;final 변수 : 상수. 초기화 이후 재할당 불가&lt;/li&gt;
&lt;li&gt;final 메서드 : 오버라이딩 불가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;instanceof (사용 권장하지 않음)
&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;LSP 를 어기는 코드에서 주로 사용되므로 유의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이유 : 상위 타입이 하위 타입에 의존하는 코드라는 의미&lt;br /&gt;&amp;nbsp;&amp;rarr; 상속 구조가 잘못되었거나, 역할 분리가 필요할 가능성 있음
&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;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742315192457&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;class Bird {
    void fly() {
        System.out.println(&quot;새가 날아갑니다.&quot;);
    }
}

class Penguin extends Bird {
    // 펭귄은 날 수 없지만, 상속을 강제 당함
}

class BirdHandler {
    void makeBirdFly(Bird bird) {
        if (bird instanceof Penguin) {  // ❌ LSP 위반: 특정 하위 클래스를 체크
            System.out.println(&quot;펭귄은 날 수 없습니다.&quot;);
        } else {
            bird.fly();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        BirdHandler handler = new BirdHandler();
        handler.makeBirdFly(new Bird());  // &quot;새가 날아갑니다.&quot;
        handler.makeBirdFly(new Penguin());  // &quot;펭귄은 날 수 없습니다.&quot; (LSP 위반)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;package : 네임스페이스 역할&lt;/li&gt;
&lt;li&gt;interface
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멤버에 public, abstract, static final 를 자동으로 붙여주지만 명시적으로 붙여 작성하는 것을 권장&lt;/li&gt;
&lt;li&gt;자바8부터 디폴트 메서드, 정적 추상 메서드 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;람다 : 변수에 할당할 수 있는 함수(로직)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;함수형 언어의 특성을 수용한 것 (자바8부터)&lt;/li&gt;
&lt;li&gt;로직을 변수에 저장하고, 인자로 전달하고, 반환값으로 사용할 수 있게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;this : 지역변수가 아닌 객체멤버에 접근&lt;/li&gt;
&lt;li&gt;super : 바로 위 상위클래스에 접근&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5장 SOLID -객체지향 설계원칙&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;SRP 단일 책임 : 역할 분리. 클래스, 메서드를 변경해야 하는 이유는 단 하나일 것
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bad case
&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;if 문에 따라 다른 로직 실행&lt;/li&gt;
&lt;li&gt;** 메서드에 if 문이 등장하면 한 메서드에서 여러 기능(책임)을 구현중인게 아닌지 의심해보자&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OCP 개방 폐쇄 : 자신의 확장에는 열려있고, 주변(외부)의 변화에는 닫혀있을 것
&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;예시 모음
&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;자바 어플리케이션 - JDBC(JDBC 드라이버) - DB종류 : DB는 그 종류 확장에 열려있고, 자바 어플리케이션은 DB종류 변경이 닫혀있음&lt;/li&gt;
&lt;li&gt;자바 코드 - JVM - OS : OS는 운영체제 종류 확장에 열려있고, 자바 코드는 운영체제 변경에 닫혀있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;LSP 리스코프 치환 : 서브타입은 항상 기반타입 인스턴스 역할을 대신할 수 있어야 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상속이 분류도가 아닌 계층도로 구현되었을 때 LSP 를 위반&lt;/li&gt;
&lt;li&gt;e.g. 아버지 - 딸 : 아버지 춘향이 = new 딸();&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ISP 인터페이스 분리 : 역할 분리. 역할을 인터페이스로 분리하고, 각 역할에 충실한 최소한의 기능만 강제할 것
&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;/li&gt;
&lt;li&gt;DIP 의존관계 역전 : 자신보다 변하기 쉬운 것에 의존하지 말 것
&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;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** SoC 관심사의 분리 : 하나의 대상에는 하나의 관심사만.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;** SRP와 ISP 는 같은 문제(책임 중복)에 대한 두 가지 해결방안으로 볼 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 상황에서는 SRP 를 권장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜 책임 중복에 대해 인터페이스보다 클래스 분리를 권장할까?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 분리 : 책임에 대한 기능을 클래스 단위로 직접 분리하고, 구현하여 캡슐화함 &amp;rarr; 객체 지향적&lt;/li&gt;
&lt;li&gt;인터페이스 분리 : 기능을 분리할 수 있지만 결국은 책임을 지울 구현 클래스가 필요함. 인터페이스가 너무 많아지는 인터페이스 지옥에 빠질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&quot;추상클래스는 물려줄 속성이 많을수록, 인터페이스는 구현할 추상메서드가 적을수록 좋다&quot; 에 대한 답
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;추상클래스 : 하위 클래스들이 공통적으로 가지게 될 속성을 잘 뽑아내어 상위 클래스에 배치함&lt;br /&gt;&amp;rarr; 메모리 효율 높아짐. 객체의 특성을 잘 표현했다는 의미&lt;/li&gt;
&lt;li&gt;인터페이스 : 인터페이스가 맡은 역할에 충실한 최소한의 기능만 강제하는 것이 좋음(SRP)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부가적인 내용&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기화 블록
&lt;div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static 블록 : 해당 클래스가 사용되는 시점에 클래스가 static 영역에 로드되면서 실행됨
&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;Q. import 만 하면 어떨까? 블록 실행 안될까? 메모리 로드는?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;import 문은 클래스를 찾을 수 있도록 도와주는 역할만 함. 실행과 관련이 없음&lt;br /&gt;&amp;nbsp;&amp;rarr; 로드되지 않음!
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실행 시 로드된 클래스 확인하는 옵션 : java -verbose:class ClassLoaderTest&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JUnit 의 @BeforeAll 과 유사한 성격&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;인스턴스 블록 : 인스턴스 생성 시 실행. 거의 안쓰임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인스턴스 블록과 스프링 빈 라이프사이클 콜백 메서드 중 하나인 @PostConstruct 비교해보기&amp;nbsp;&lt;br /&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;인스턴스 초기화 블록 {}&lt;/td&gt;
&lt;td style=&quot;background-color: #9b9b9b; color: #ffffff;&quot;&gt;초기화 콜백 메서드 @PostConstruct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;실행 시점&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;객체 생성 시&lt;/td&gt;
&lt;td&gt;의존성 주입 후&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;스프링 의존성 주입 반영&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;불가능&lt;/td&gt;
&lt;td style=&quot;background-color: #f9f9f9;&quot;&gt;가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;background-color: #efefef;&quot;&gt;&lt;b&gt;가독성 및 유지보수&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;코드 흐름에서 벗어나 위치&lt;/td&gt;
&lt;td&gt;메서드로 분리하여 명확한 역할 분리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;instanceof 는 양방향이 가능하다. 실제로 담긴 객체를 기준으로 판별함&lt;br /&gt;코드 실행 예제 (코드의 조건문 모두 true임)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1742315064725&quot; class=&quot;scala&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.util.*;
import java.lang.*;
import java.io.*;

// The main method must be in a class named &quot;Main&quot;.
class A {
    
}

class B extends A {
    
}

class Main {
    public static void main(String[] args) {

        A a = new B();
        B b = new B();
        
        if(a instanceof B){
            System.out.println(&quot;a is instanceof B&quot;); // true
        }
        if(b instanceof A){
            System.out.println(&quot;b is instanceof A&quot;); //true
        }
        System.out.println(&quot;end&quot;);

    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;interface 는 public 추상메서드 및 정적속성만 가질 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그래서 public, abstract, static final 은 붙이지 않아도 자동으로 붙지만,&lt;/li&gt;
&lt;li&gt;명시적으로 적어주는 것을 권장&lt;/li&gt;
&lt;/ul&gt;
&amp;rarr; 왜 public ? 165p
&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;구현 클래스에서 접근제한자를 동일하거나 더 좁은 범위로 지정하여 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;왜 책임 중복에 대해 ISP 보다 SRP 를 권장할까
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 분리 : 책임에 대한 기능을 클래스 단위로 직접 분리하고, 구현하여 캡슐화함 &amp;rarr; 더 객체 지향적&lt;/li&gt;
&lt;li&gt;인터페이스 분리 : 기능을 분리할 수 있지만 결국은 책임을 지울 구현 클래스가 필요함. 인터페이스가 너무 많아지는 인터페이스 지옥에 빠질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;추상클래스에 생성자가 왜 필요할까?&lt;br /&gt;&amp;rarr; 하위클래스에서 공통적으로 쓸 수 있게 하기 위함!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LSP 심화 내용을 더 살펴보자 (스터디원분의 공유내용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;선행조건: 메서드가 실행되기 전에 반드시 참이어야 하는 조건&lt;/li&gt;
&lt;li&gt;예시: 상위 타입 Animal의 eat(Food food) 메서드는 모든 종류의 Food 객체를 인자로 받을 수 있을 때, 하위 타입 Carnivore의 eat(Food food) 메서드가 인자로 Meat 타입만 허용한다면, 이는 선행조건을 강화한 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;후행조건: 메서드가 성공적으로 실행된 후에 반드시 참이어야 하는 조건&lt;/li&gt;
&lt;li&gt;예시: 상위 타입 List의 get(int index) 메서드는 주어진 인덱스에 항상 존재하는 요소를 반환한다고 가정할 때, 하위 타입 ImmutableList의 get(int index) 메서드가 어떤 경우에도 null을 반환하거나 예외를 던진다면, 이는 후행조건을 약화시킨 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;불변조건: 객체의 생명 주기 동안 항상 참이어야 하는 조건, 예시로는 Rectangle 클래스에서 &quot;너비와 높이는 항상 양수여야 한다&quot;&lt;/li&gt;
&lt;li&gt;예시: 상위 타입 Rectangle은 &quot;너비와 높이는 항상 0보다 크거나 같아야 한다&quot;는 불변 조건을 가지고 있을 때, 하위 타입 Square에서 너비와 높이를 음수로 설정할 수 있도록 허용한다면, 이는 상위 타입의 불변 조건을 위반&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그렇다면 LSP는 오바라이딩과 오버로딩을 지향하지 않는 것인가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI 답변: 아닙니다. LSP(리스코프 치환 원칙)는 오버라이딩과 오버로딩을 &lt;b&gt;하지 않는 것이 좋다&lt;/b&gt;는 의미가 아니라, &lt;b&gt;오버라이딩을 할 때 어떻게 해야 하위 타입이 상위 타입으로 안전하게 치환될 수 있는지&lt;/b&gt;에 대한 원칙&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&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;오버라이딩 or 추상메서드 구현 시 접근제한자를 변경할 수 있지만, &lt;br /&gt;더 넓은 범위로 변경할 수는 없음! 좁은 범위로만 가능함&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>OOP</category>
      <category>solid</category>
      <category>객체</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/249</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-45%EC%9E%A5-%EC%9E%90%EB%B0%94%EA%B0%80-%ED%99%95%EC%9E%A5%ED%95%9C-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-SOLID#entry249comment</comments>
      <pubDate>Wed, 19 Mar 2025 01:29:27 +0900</pubDate>
    </item>
    <item>
      <title>[ 객체 개구리책] 3장 자바와 객체지향</title>
      <link>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-3%EC%9E%A5-%EC%9E%90%EB%B0%94%EC%99%80-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;알게된 내용&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Mutabe과 Immutable
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Immutable : 생성 후 그 상태가 변하지 않는 객체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. String, Integer, Float, Long&lt;/li&gt;
&lt;li&gt;값이 수정되면 아예 새로운 값이 생성되고 참조값이 새 주소로 바뀜&lt;/li&gt;
&lt;li&gt;값 수정 후 identityHashCode()로 해시코드를 출력해보면 다름 &amp;rarr; 다른 객체임&lt;/li&gt;
&lt;li&gt;멀티스레드 환경에서도 안전. 해시키로 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Mutable : 생성 후에도 상태를 변경할 수 있는 객체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;e.g. ArrayList, HashMap, StringBuilder, Date&lt;/li&gt;
&lt;li&gt;변경이 많을 시 메모리 성능 상의 이점이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변수명 앞에 a나 the 를 붙이는 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 이름의 클래스가 존재하고, 클래스의 인스턴스를 만들고 싶을 때,e.g. Bird 의 인스턴스 aBird&lt;/li&gt;
&lt;li&gt;a 나 the 를 붙여서 분류가 아닌 객체임을 강조해준다.&lt;/li&gt;
&lt;li&gt;물론 클래스명을 그대로 쓰는 것보단 좀더 유일무이한 객체임을 의미하는 변수명을 권장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;궁금한 점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;상위클래스는 상속해줄 특성이 많을수록 좋다?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LSP : 하위 타입은 상위 타입을 항상 대체할 수 있어야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;오버로딩을 지원하지 않는 언어 : C, JS, Dart 등
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;거기서는 오버로딩에 어떻게 대처하나? &lt;br /&gt;- 메서드명에 타입을 명시함&lt;br /&gt;- 파이썬은 인자 기본값을 활용해 오버로딩을 유사하게 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;정리한 것&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 class
&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;객체는 분류에 속하는 유일무이한 실체다
&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;/li&gt;
&lt;li&gt;클래스 : 객체 = 분류 : 실체
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;s&gt;붕어빵&lt;/s&gt;보다는&amp;hellip;. 펭귄 : 핑구&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;객체 지향 4대 특성
&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;추상화 : 모델링&lt;/li&gt;
&lt;li&gt;상속 : 재사용&lt;/li&gt;
&lt;li&gt;다형성 : 사용 펀의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;자바에서는 class 를 통해 추상화를 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상속 : 재사용&amp;amp;확장 extends
&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;상위클래스의 특성을 하위클래스에 물려줌
&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;확장 : 하위 클래스에서 필요한 특성을 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하위 클래스는 상위 클래스다 : LSP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;is-a 가 아니라 is a kind of 관계이다.&lt;/li&gt;
&lt;li&gt;클래스는 &amp;lsquo;분류&amp;rsquo; 이기 때문에 &amp;lsquo;~이다&amp;rsquo; 의 논리가 적용되는 것은 어색하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;상속한 클래스에 동일한 이름의 상위메서드가 여러개 존재하면 어떤 것을 사용할 지 난감해짐&lt;/li&gt;
&lt;li&gt;인터페이스 : 다중상속의 장점 활용 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;~able : 구현은 (인터페이스)할 수 있다.&lt;/li&gt;
&lt;li&gt;e.g. Serializable, Cloneable, Comparable, Runnable &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;상속은 특성을 상속해줌 / 인터페이스는 기능 구현을 강제함인터페이스는 구현할 메서드가 적을수록 좋음 : ISP&lt;/li&gt;
&lt;li&gt;&amp;rarr; 상위클래스는 상속해줄 특성이 많을수록 좋음 : LSP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다형성 : 사용편의성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오버라이딩, 오버로딩, 제네릭 &amp;rarr; 사용편의성 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 내용&lt;/h2&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;더이상 참조되지 않는 객체는 힙 영역에서 GC 에 의해 수거됨&lt;/li&gt;
&lt;li&gt;**GC가 언제 오는지는 신만이 아신다는 말이 있다&lt;/li&gt;
&lt;li&gt;메모리 이름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;static 영역이 static 인 이유 - 프로그램 실행 후 메모리 static영역에 한번 올라가면 위치가 변할 일이 없는 영역이기 때문&lt;/li&gt;
&lt;li&gt;스택 영역이 스택인 이유 - FILO 라서&lt;/li&gt;
&lt;li&gt;힙 영역이 힙인 이유 - 대용량 자료를 저장할 수 있도록 메모리를 사용하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변수 초기화
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지역(local)변수는 직접 초기화해주지 않으면 쓰레기값을 가짐&lt;/li&gt;
&lt;li&gt;멤버변수(local 외)는 기본값으로 초기화해줌(초기화의 주체를 규정하기가 애매하므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;상위 클래스 타입 변수에 하위 클래스 인스턴스를 할당할 경우, (형변환)변수는 해당 상위클래스 인스턴스의 힙영역 위치를 참조함&lt;/li&gt;
&lt;li&gt;하위 및 상위 클래스 인스턴스 모두 생성되고&lt;/li&gt;
&lt;li&gt;오버라이딩된 메서드는 상위 메서드까지 덮어씌움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;정적 멤버는 일관된 사용과 물리적 접근 효율성을 위해 Class.staticVar 형식으로 접근하자&lt;/li&gt;
&lt;li&gt;참조변수를 할당하는 경우에는 참조(주소)값 자체를 복사함 (Call By Value)
&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;/li&gt;
&lt;li&gt;객체지향적으로 쓰인 코드는논리적으로 이해하기 쉬워야 한다&lt;/li&gt;
&lt;li&gt;인간의 언어로 번역하면서 읽을 때&lt;/li&gt;
&lt;li&gt;getter &amp;amp; setter 는 한국말로 접근자 &amp;amp; 설정자&lt;/li&gt;
&lt;li&gt;접근제어자 범위
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;private / default / protected / public&lt;/li&gt;
&lt;li&gt;클래스 / 패키지 / +상속 / all&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;변수명 앞에 a나 the 를 붙이는 경우
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일한 이름의 클래스가 존재하고, 클래스의 인스턴스를 만들고 싶을 때,e.g. Bird 의 인스턴스 aBird&lt;/li&gt;
&lt;li&gt;a 나 the 를 붙여서 분류가 아닌 객체임을 강조해준다.&lt;/li&gt;
&lt;li&gt;물론 클래스명을 그대로 쓰는 것보단 좀더 유일무이한 객체임을 의미하는 변수명을 권장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;옆으로 나열해서 적재하면 모두 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알아보기&lt;/h3&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;논리적설계, 물리적 설계 92p&lt;/li&gt;
&lt;li&gt;이뮤터블과 메모리 할당 97p&lt;/li&gt;
&lt;li&gt;힙이 힙인 이유?&lt;/li&gt;
&lt;li&gt;지역변수는 기본자료형도 초기화가 안돼??&lt;/li&gt;
&lt;li&gt;다중상속의 다이아몬드 문제 117p&lt;/li&gt;
&lt;li&gt;상위클래스는 상속해줄 특성이 많을수록 좋음 : LSP ? 119p&lt;/li&gt;
&lt;li&gt;인텔리제이에서 클래스 다이어그램 보는 법.. 124p&lt;/li&gt;
&lt;li&gt;명시적 형변환(Casting) / 암묵적 형변환(Promotion) 127p&lt;/li&gt;
&lt;li&gt;오버로딩을 지원하지 않는 언어가 뭐가 있나? 거기서는 오버로딩에 어떻게 대처하나? 133p&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&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;LSP : 계약을 지켜
&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;맥락적인 계약&lt;/li&gt;
&lt;/ul&gt;
기존 메서드의 의도와 다르게 오버라이딩된 건 LSP를 잘 지켰다고 할 수 있는가?메서드 계약이 결정되는 순간은 컴파일에서 끝나지 않는다고 생각&lt;/li&gt;
&lt;li&gt;&amp;rarr; 잘못된 코드인 것 같고, 객체지향적이지도 않은 것 같다고 생각했다.&lt;/li&gt;
&lt;li&gt;파이썬도 언어 차원에서는 오버로딩을 지원하지 않아서 인자의 기본값을 세팅하는 방향으로 오버로딩을 유사하게 만들어쓴다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>OOP</category>
      <category>객체</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/248</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-3%EC%9E%A5-%EC%9E%90%EB%B0%94%EC%99%80-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5#entry248comment</comments>
      <pubDate>Tue, 18 Mar 2025 22:02:49 +0900</pubDate>
    </item>
    <item>
      <title>[ 객체 개구리책] 1,2장 자바 등장 배경 &amp;amp; 메모리 동작 방식</title>
      <link>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-12%EC%9E%A5-%EC%9E%90%EB%B0%94-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D</link>
      <description>&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 style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;call by ~ 들 정리해보기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바는 참조값을 복사 전달하므로 배열이든 List든(원시타입이든 객체든) 원본이 변경됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바는 포인터를 사용하지 않아서 메모리 안전성이 높음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 자바도 포인터로 메모리를 조작할 수 있었다면, GC 처리된 자원에 대해 바뀌거나 사라진 메모리 주소를 바라볼 수도 있음. 그래서 포인터를 사용하지 않을 수도.&lt;/li&gt;
&lt;li&gt;포인터에 대한 압박 &amp;rarr; 개발 생산성 저하 &amp;rarr; Java 가 기업단에서 우세하게 된 배경일 수도.&lt;/li&gt;
&lt;li&gt;C의 댕글링 포인터 : 이미 해제된(free&amp;nbsp;또는 delete된) 메모리를 계속 가리키고&amp;nbsp;있는 포인터&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;atomic은 어떻게 atomic 을 보장하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;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;제어문은 메서드 안에만 존재할 수 있음&lt;/li&gt;
&lt;li&gt;메서드 간 소통은 오직 인자와 반환값을 통해서만 일어남 (메서드의 블랙박스화)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바는 항상 Call By Value 이다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체를 전달할 때 : 객체의 참조값(주소)를 Call By Value 로 전달함처음 전달받은 원본 객체는 영향을 받지 않음&lt;br /&gt;&amp;rarr; 그래서 전달받은 객체 변수에 다른 객체를 할당하면 참조값이 변경됨&lt;/li&gt;
&lt;li&gt;원시타입을 전달할 때 : 항상 Call By Value!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;변수 체는 스택에 저장되어 객체의 참조값(주소값)을 저장. 객체는 힙에 저장.&lt;/li&gt;
&lt;li&gt;더이상 참조되지 않는 객체는 GC가 힙에서 회수 &lt;br /&gt;&amp;rarr; 추후 GC 에 대해 더 자세히..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;자바 .java &amp;rarr; 기계어 컴파일 및 변환 과정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단계 : 컴파일러에 의해 컴파일 &amp;rarr; JVM에 의해 기계어로 변환 후 JRE 환경에서 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 자바 소스코드 (.java)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바 컴파일러(javac)에 의해 자바 목적파일로 컴파일&lt;/li&gt;
&lt;li&gt;.java&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1741143119093&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class HelloWorld {
    public static void main(String[] args) {
        System.out.println(&quot;Hello, World!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 자바 byte code (목적파일과 유사한 역할, .class)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM 이 실행할 수 있는 형태&lt;/li&gt;
&lt;li&gt;JVM명령어(opcode)로 구성됨&lt;/li&gt;
&lt;li&gt;JVM 에서 기계어로 변환 후 실행&lt;/li&gt;
&lt;li&gt;16비트 바이너리 코드 .class 의 어셈블리어 형태 (javap -c HelloWorld.class 출력 결과)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1741143157473&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Compiled from &quot;HelloWorld.java&quot;
public class HelloWorld {
public HelloWorld();
Code:
0: aload_0
1: invokespecial #1                  // Method java/lang/Object.&quot;&amp;lt;init&amp;gt;&quot;:()V
4: return

public static void main(java.lang.String[]);
Code:
0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc           #3                  // String Hello, World!
5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 기계어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바 목적파일을 JVM 에서 기계어 변환 후 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인터프리팅 or JIT(Just-In-Time) 컴파일러로 기계어 변환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실제로 CPU 에서 실행되는 형태&lt;/li&gt;
&lt;li&gt;기계어(바이너리 코드)를 x86-64 아키텍쳐 어셈블리어로 표현한 것 (일부)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1741143184425&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mov rdi, OFFSET FLAT:.LC0   ; &quot;Hello, World!&quot; 문자열 로드
call printf                 ; printf 함수 호출 (C 라이브러리 사용)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;static 변수(상수x)는 왜 존재할까? 언제 필요할까?
&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;프로그램 전체에 공유해야할 값..? 그런데 이제 여러 스레드에서 접근해서 값을 변경해도 괜찮은.. 그런 게 존재하나&lt;/li&gt;
&lt;li&gt;변경되지 않는 값이면 static 상수를 쓰면 되니 해당 안됨&lt;/li&gt;
&lt;/ul&gt;
&amp;rarr; 관련 의견
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DB 커넥션 풀을 관리에 쓰일 수 있지 않을까? &amp;rarr; 싱글톤이 필요한 것에 더 가까울 듯?&lt;/li&gt;
&lt;li&gt;게임 서버에서 캐릭터의 HP같은 속성 기본값을 static 으로 쓰더라&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;JVM 은 바이트코드 실행 방법(인터프리팅/JIT컴파일)을 언제 어떻게 결정할까?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM 내부 실행엔진이 둘 중 어떤 방식으로 실행할 지 결정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 실행 횟수 : 처음엔 인터프리팅 모드로 실행. 실행횟수에 따라 내부 카운터로 핫스팟 감지 (보통 10,000회 실행 시 JIT 컴파일 대상)&lt;/li&gt;
&lt;li&gt;그 외 CPU 성능 및 메모리, 코드 실행 시간, JVM 옵션에 따라 등에 의해 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;실행 방법을 강제해줘야하는 상황은 어떤 게 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;읽으며 정리 기록&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1장&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;기계어를 벗어나 인간의 언어를 모방&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;C 언어 : 싱글소스. 이식성 &amp;uarr;. One Source (Multi Object) Use Anywhere&lt;/li&gt;
&lt;li&gt;C++ : 객체지향 지원&lt;/li&gt;
&lt;li&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;포인터없는 프로그래밍&lt;/li&gt;
&lt;li&gt;가상머신(JVM). 이식성 &amp;uarr;. Write Once Use Anywhere&lt;/li&gt;
&lt;li&gt;JDK + JRE [ JVM ]&lt;/li&gt;
&lt;li&gt;스프링은 OOP 프레임워크이다.&lt;/li&gt;
&lt;li&gt;스프링은 &lt;b&gt;PSA&lt;/b&gt; 기법을 통해 중구난방으로 구현된 다양한 기술을 &lt;b&gt;표준화된 방식&lt;/b&gt;으로 사용할 수 있게 지원해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2장&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JRE
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM 을 위한 실행 환경&lt;/li&gt;
&lt;li&gt;호스트 OS와 JVM 간의 추상화 계층 역할. JVM이 OS 기능을 간접적으로 활용할 수 있게 해줌
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OS 의 기능을 사용할 수 있는 Java 라이브러리 제공&lt;/li&gt;
&lt;li&gt;네이티브 코드와 상호작용할 수 있는 인터페이스 제공&lt;/li&gt;
&lt;li&gt;JVM 의 메모리 관리, GC 기능, 스레드 관리 등을 처리해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바의 메모리 사용방식 (T구조)
&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;데이터 저장 영역
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스태틱 static : 클래스(패키지)가 올라가는 곳&lt;/li&gt;
&lt;li&gt;스택 stack : 실행할 메서드 &amp;lsquo;&amp;rsquo;&lt;/li&gt;
&lt;li&gt;힙 heap : 객체 &amp;lsquo;&amp;rsquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메서드를 여는 중괄호를 만날 때마다 스택 영역에 메서드를 실행할 스택 프레임이 생김. 아래부터 메모리를 사용함. 닫는 중괄호를 만나면 스택 프레임이 소멸함&lt;/li&gt;
&lt;li&gt;메서드 안에서 또다른 중괄호(if문 등)를 만나면 해당 메서드의 스택 프레임 내부에 또다른 스택 프레임이 생성되고, 사용된 후 닫는 중괄호를 만나면 소멸한다.&lt;/li&gt;
&lt;li&gt;내부 블록(스택프레임)에서 외부 블록의 데이터에 접근할 수는 있지만,&lt;/li&gt;
&lt;li&gt;역은 불가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;힙 영역의 객체 멤버 변수들은 GC에 의해 회수됨&lt;/li&gt;
&lt;li&gt;메서드의 블랙박스화 : 메서드들은 인자와 반환값으로만 소통함&lt;/li&gt;
&lt;li&gt;멀티 스레드 / 멀티 프로세스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;멀티 스레드 : 스택영역 구분, 스태틱과 힙 영역 공유&lt;br /&gt;&amp;rarr; 특히 멀티 스레드 환경에서는 전역변수 사용하지 말 것. 스레드 안정성 깨짐&lt;/li&gt;
&lt;li&gt;멀티 프로세스 : 모든 영역 별도사용 (각자의 T메모리 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Java</category>
      <category>Java</category>
      <category>OOP</category>
      <category>개구리</category>
      <category>객체</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/247</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%F0%9F%90%B8%EA%B0%9D%EC%B2%B4-%EA%B0%9C%EA%B5%AC%EB%A6%AC%EC%B1%85-12%EC%9E%A5-%EC%9E%90%EB%B0%94-%EB%93%B1%EC%9E%A5-%EB%B0%B0%EA%B2%BD-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D#entry247comment</comments>
      <pubDate>Wed, 5 Mar 2025 11:57:23 +0900</pubDate>
    </item>
    <item>
      <title>[설계 문서] RUSH Logistic - AI 기반 배송 소요시간 예측 물류 시스템 (MSA)</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EC%84%A4%EA%B3%84-%EB%AC%B8%EC%84%9C-RUSH-Logistic-AI-%EA%B8%B0%EB%B0%98-%EB%B0%B0%EC%86%A1-%EC%86%8C%EC%9A%94%EC%8B%9C%EA%B0%84-%EC%98%88%EC%B8%A1-%EB%AC%BC%EB%A5%98-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 기반 배송 소요시간 예측 물류 시스템을 설계했습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트명 : RUSH Logistic&lt;br /&gt;AI를 기반으로 예측한 배송 소요시간을 활용하는 물류 시스템입니다.&lt;/li&gt;
&lt;li&gt;아키텍쳐 : MSA (MicroService Architecture)&lt;/li&gt;
&lt;li&gt;기술 스택 : Spring cloud, Eureka, PostgreSql, Redis, Docker&lt;br /&gt;+ Zipkin, Gemini api, NaverMap api&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;API 명세서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션으로 작성하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 엔드포인트들을 설계하였고, 자세한 내용은 링크의 노션 페이지를 참고 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;a href=&quot;https://www.notion.so/teamsparta/API-9aef80a72b8b4e56bc579e3231c4dc40&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;API 명세서 노션&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFKBJT/btsLba98UR4/jiWvqgn8PDZw3Ty4hW59t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFKBJT/btsLba98UR4/jiWvqgn8PDZw3Ty4hW59t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFKBJT/btsLba98UR4/jiWvqgn8PDZw3Ty4hW59t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFKBJT%2FbtsLba98UR4%2FjiWvqgn8PDZw3Ty4hW59t1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;702&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH518T/btsLaHtNe8n/mZQyQgd3SsjKuS6ZJTjdkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH518T/btsLaHtNe8n/mZQyQgd3SsjKuS6ZJTjdkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH518T/btsLaHtNe8n/mZQyQgd3SsjKuS6ZJTjdkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH518T%2FbtsLaHtNe8n%2FmZQyQgd3SsjKuS6ZJTjdkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1001&quot; height=&quot;768&quot; data-origin-width=&quot;1001&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;317&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVGFvI/btsLcGuWlfo/zRKcXlHvzKssdxlIi7kNK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVGFvI/btsLcGuWlfo/zRKcXlHvzKssdxlIi7kNK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVGFvI/btsLcGuWlfo/zRKcXlHvzKssdxlIi7kNK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVGFvI%2FbtsLcGuWlfo%2FzRKcXlHvzKssdxlIi7kNK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1151&quot; height=&quot;317&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;317&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;507&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDBS8/btsK9x6MgSj/auT9eXlbcaDk8r1kZ6wF8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDBS8/btsK9x6MgSj/auT9eXlbcaDk8r1kZ6wF8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDBS8/btsK9x6MgSj/auT9eXlbcaDk8r1kZ6wF8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDBS8%2FbtsK9x6MgSj%2FauT9eXlbcaDk8r1kZ6wF8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1000&quot; height=&quot;507&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;507&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;577&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JEyIy/btsLaBAyUBt/xpK5l2YVDkO6u9K49v5QA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JEyIy/btsLaBAyUBt/xpK5l2YVDkO6u9K49v5QA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JEyIy/btsLaBAyUBt/xpK5l2YVDkO6u9K49v5QA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJEyIy%2FbtsLaBAyUBt%2FxpK5l2YVDkO6u9K49v5QA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1007&quot; height=&quot;577&quot; data-origin-width=&quot;1007&quot; data-origin-height=&quot;577&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ERD 및 테이블 명세서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ERD 는 ERD cloud 를 활용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 바탕으로 테이블 명세서를 작성하였고, 링크 참고 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1xiXvHmo2wijXeWZmYdi3OQq0XHNwmVog8oWbCUE3zuE/edit?gid=2112576932#gid=2112576932&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;테이블 명세서&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2311&quot; data-origin-height=&quot;1362&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQrIuf/btsLddFSS59/vmxE1hrlxnh7GYHvuZUOk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQrIuf/btsLddFSS59/vmxE1hrlxnh7GYHvuZUOk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQrIuf/btsLddFSS59/vmxE1hrlxnh7GYHvuZUOk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQrIuf%2FbtsLddFSS59%2FvmxE1hrlxnh7GYHvuZUOk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2311&quot; height=&quot;1362&quot; data-origin-width=&quot;2311&quot; data-origin-height=&quot;1362&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인프라 설계서&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 아키텍처 구조를 시각화하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스별 개별 배포가 가능하고, 특정 서비스 확장이 유연하다는 장점에 MSA 아키텍처를 선택하였습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 도커를 활용해 운영 편리성을 높였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;884&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SdIH0/btsLa1r5Bhr/Zveo22NdWOpyJQ6Iq6F3zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SdIH0/btsLa1r5Bhr/Zveo22NdWOpyJQ6Iq6F3zK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SdIH0/btsLa1r5Bhr/Zveo22NdWOpyJQ6Iq6F3zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSdIH0%2FbtsLa1r5Bhr%2FZveo22NdWOpyJQ6Iq6F3zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1268&quot; height=&quot;884&quot; data-origin-width=&quot;1268&quot; data-origin-height=&quot;884&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Dev Convention&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 개발 시 필요한 사항들을 컨벤션을 정의했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;깃 커밋, 브랜치 사용, 개발 관련 컨벤션들을 정의하였고 자세한 사항은 노션 페이지를 참고 부탁드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; &lt;a href=&quot;https://www.notion.so/teamsparta/Convention-Docs-7a05f4cbbd324fab834773548b10dda7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;컨벤션 노션 페이지&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 gitmoji 도 사용하여 가독성을 높이기로 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p61sa/btsLbXwpgvu/Qp7Yezh9hXPJGPMNKj0pbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p61sa/btsLbXwpgvu/Qp7Yezh9hXPJGPMNKj0pbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p61sa/btsLbXwpgvu/Qp7Yezh9hXPJGPMNKj0pbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp61sa%2FbtsLbXwpgvu%2FQp7Yezh9hXPJGPMNKj0pbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;322&quot; height=&quot;347&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>TIL</category>
      <category>MSA</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/246</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EC%84%A4%EA%B3%84-%EB%AC%B8%EC%84%9C-RUSH-Logistic-AI-%EA%B8%B0%EB%B0%98-%EB%B0%B0%EC%86%A1-%EC%86%8C%EC%9A%94%EC%8B%9C%EA%B0%84-%EC%98%88%EC%B8%A1-%EB%AC%BC%EB%A5%98-%EC%8B%9C%EC%8A%A4%ED%85%9C-MSA#entry246comment</comments>
      <pubDate>Fri, 6 Dec 2024 17:59:54 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 분산 추적 (Spring Cloud Sleuth) &amp;amp; 로깅 (Zipkin) &amp;amp; 이벤트 드리븐 아키텍처 및 스트림 처리</title>
      <link>https://onedaythreecoding.tistory.com/entry/TIL-%EB%B6%84%EC%82%B0-%EC%B6%94%EC%A0%81-Spring-Cloud-Sleuth-%EB%A1%9C%EA%B9%85-Zipkin-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%93%9C%EB%A6%AC%EB%B8%90-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%B0%8F-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%B2%98%EB%A6%AC</link>
      <description>&lt;h1&gt;9. 분산 추적 (Spring Cloud Sleuth) 및 로깅 (Zipkin)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.1 분산 추적&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.1.1 분산 추적이란?&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;각 서비스의 호출 관계와 성능을 시각화하여 문제를 진단하고 해결할 수 있도록 도움&lt;/li&gt;
&lt;li&gt;주요 개념: 트레이스(Trace), 스팬(Span), 컨텍스트(Context)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;트레이스(Trace) : 트레이스는 하나의 요청이 시작부터 끝까지 각 서비스를 거치는 전체 흐름&lt;/li&gt;
&lt;li&gt;스팬(Span) : 스팬은 분산 추적에서 가장 작은 단위로, 특정 서비스 내에서의 개별 작업 또는 요청&lt;/li&gt;
&lt;li&gt;컨텍스트(Context) : 컨텍스트는 요청이 서비스 간에 전달될 때 함께 전파되어, 각 서비스가 요청의 전체 흐름에 대한 정보를 가질 수 있게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.1.2 왜 분산 추적이 필요한가?&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;서비스 간의 복잡한 호출 관계로 인해 문제 발생 시 원인을 파악하기 어려울 수 있음&lt;/li&gt;
&lt;li&gt;분산 추적을 통해 각 서비스의 호출 흐름을 명확히 파악하고, 성능 병목이나 오류를 빠르게 진단&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.2 Micrometer&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.2.1 Micrometer란?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring 기반 애플리케이션에서 메트릭을 수집하고 모니터링하기 위한 라이브러리&lt;/li&gt;
&lt;li&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;: 애플리케이션의 다양한 성능 지표 수집&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 연동&lt;/b&gt;: Prometheus, Grafana 등 다양한 모니터링 툴과 연동&lt;/li&gt;
&lt;li&gt;&lt;b&gt;추적 기능&lt;/b&gt;: 분산 추적을 위한 기능 제공. 서비스 간의 호출 흐름을 추적하여 성능 병목 진단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.3 Zipkin&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.3.1 Zipkin이란?&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;특징
&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;: 각 서비스에서 전송된 트레이스와 스팬데이터를 수집하고 저장&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시각화&lt;/b&gt;: 트레이스 데이터를 시각화하여 서비스 간의 호출 관계를 명확히 파악&lt;/li&gt;
&lt;li&gt;&lt;b&gt;검색 및 필터링&lt;/b&gt;: 특정 트레이스나 스팬을 검색하고 필터링하여 문제를 진단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.4 Zipkin 서버 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.4.1 Zipkin 서버 실행&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Docker로 Zipkin 서버 실행 : docker run -d -p 9411:9411 openzipkin/zipkin&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9.4.2 Zipkin 대시보드 사용&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Zipkin 대시보드에 접속하여 트레이스 데이터를 시각화합니다:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;URL: http://localhost:9411&lt;/li&gt;
&lt;li&gt;트레이스 검색 및 분석&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9.5 분산 추적 예제&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스 호출 흐름 추적
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예제 서비스 간의 호출 흐름을 추적하고, Zipkin 대시보드에서 시각화&lt;/li&gt;
&lt;li&gt;각 서비스 호출 시 트레이스와 스팬이 생성되고, Zipkin 서버로 전송&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;성능 병목 진단
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Zipkin 대시보드에서 성능 병목이 발생하는 부분을 식별&lt;/li&gt;
&lt;li&gt;각 스팬의 소요 시간과 호출 관계를 분석하여 성능 문제를 진단하고 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;10. 이벤트 드리븐 아키텍처와 스트림 처리 (Spring Cloud Stream)&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.1 이벤트 드리븐 아키텍처&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1.1 이벤트 드리븐 아키텍처란?&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;이벤트는 비동기적으로 처리되며, 서비스 간의 느슨한 결합을 통해 독립적으로 동작할 수 있게 함&lt;/li&gt;
&lt;li&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;: 시스템 내에서 발생하는 상태 변화나 행동을 나타내는 메시지&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 소스&lt;/b&gt;: 이벤트를 생성하여 이벤트 버스에 전달하는 역할&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 버스&lt;/b&gt;: 이벤트 소스와 이벤트 핸들러 간의 메시지 전달을 중개 (메세지 큐)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 핸들러&lt;/b&gt;: 이벤트를 수신하여 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1.3 이벤트 드리븐 아키텍처의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;이벤트 기반 통신 &amp;rarr; 서비스 간의 결합도 낮춤&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;확장성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수평 확장이 용이 &amp;rarr; 대규모 시스템에서 유용&lt;/li&gt;
&lt;li&gt;이벤트 프로듀서와 컨슈머를 독립적으로 확장 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;요청과 응답을 비동기적으로 처리하여 성능 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1.4 이벤트 드리븐 아키텍처의 단점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;이벤트 흐름과 상태 관리를 체계적으로 설계 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;이벤트 재처리 및 장애 복구 메커니즘 구현 필요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.1.5 예시: 온라인 쇼핑몰&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;이벤트 소스&lt;/b&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;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 버스&lt;/b&gt;: Kafka나 RabbitMQ와 같은 메시지 브로커가 '주문 생성' 이벤트를 전달&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이벤트 핸들러&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;: '주문 생성' 이벤트를 수신하여 재고를 확인, 업데이트&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배송 서비스&lt;/b&gt;: '주문 생성' 이벤트를 수신하여 배송 준비 시작&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결제 서비스&lt;/b&gt;: '주문 생성' 이벤트를 수신하여 결제 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10.2 Spring Cloud Stream&lt;/h2&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;Kafka, RabbitMQ 등의 메시지 브로커와 통합하여 이벤트 스트리밍을 처리&lt;/li&gt;
&lt;li&gt;프로듀서와 컨슈머 간의 통신을 추상화하여 간편하게 이벤트 기반 애플리케이션을 개발&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;10.2.2 주요 특징&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;바인더 추상화&lt;/b&gt;: 메시지 브로커와의 통합을 위한 추상화 레이어를 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;프로듀서/컨슈머 모델&lt;/b&gt;: 이벤트를 생성하고 처리하는 프로듀서/컨슈머 모델 지원&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유연한 설정&lt;/b&gt;: 다양한 설정 옵션으로 쉽게 커스터마이징&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL</category>
      <category>event</category>
      <category>MQ</category>
      <category>MSA</category>
      <category>spring cloud sleuth</category>
      <category>spring cloud stream</category>
      <category>zipkin</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/245</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/TIL-%EB%B6%84%EC%82%B0-%EC%B6%94%EC%A0%81-Spring-Cloud-Sleuth-%EB%A1%9C%EA%B9%85-Zipkin-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%93%9C%EB%A6%AC%EB%B8%90-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%B0%8F-%EC%8A%A4%ED%8A%B8%EB%A6%BC-%EC%B2%98%EB%A6%AC#entry245comment</comments>
      <pubDate>Mon, 25 Nov 2024 17:23:09 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] MSA - 서킷 브레이커 (Resilience4j)</title>
      <link>https://onedaythreecoding.tistory.com/entry/TIL-MSA-%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-Resilience4j</link>
      <description>&lt;h1&gt;5. 서킷 브레이커 (Resilience4j)&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1.1 서킷 브레이커란?&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;외부 서비스 호출 실패 시 빠른 실패를 통해 장애를 격리하고, 시스템의 다른 부분에 영향을 주지 않도록 합니다.&lt;/li&gt;
&lt;li&gt;상태 변화: 클로즈드 -&amp;gt; 오픈 -&amp;gt; 하프-오픈&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2.2 Resilience4j의 주요 특징&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;서킷 브레이커 상태&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;클로즈드(Closed)&lt;/b&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;호출이 실패하면 실패 카운터가 증가, 실패율이 설정된 임계값(예: 50%)을 초과하면 오픈 상태로 전환&lt;/li&gt;
&lt;li&gt;예시: 최근 5번의 호출 중 3번이 실패하여 실패율이 60%에 도달하면 오픈 상태로 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;오픈(Open)&lt;/b&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;일정 대기 시간이 지난 후, 하프-오픈 상태로 전환&lt;/li&gt;
&lt;li&gt;예시: 서킷 브레이커가 오픈 상태로 전환되고 20초 동안 모든 요청이 차단&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;하프-오픈(Half-Open)&lt;/b&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;하프-오픈 상태에서는 제한된 수의 요청을 허용하여 시스템이 정상 상태로 복구되었는지 확인. 성공하면 서킷 브레이커는 클로즈드 상태로 전환&lt;/li&gt;
&lt;li&gt;요청이 다시 실패하면 서킷 브레이커는 다시 오픈 상태로 전환&lt;/li&gt;
&lt;li&gt;예시: 하프-오픈 상태에서 3개의 요청을 허용하고, 모두 성공하면 클로즈드 상태로 전환. 만약 하나라도 실패하면 다시 오픈 상태로 전환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fallback&lt;/b&gt;: 호출 실패 시 대체 로직을 제공하여 시스템 안정성 확보&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모니터링&lt;/b&gt;: 서킷 브레이커 상태를 모니터링하고 관리할 수 있는 다양한 도구 제공&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.3 Resilience4j 설정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3.1 기본 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;resilience4j 의존성은 spring starter(start.spring.io)에서 추가하여 사용하지 않을 것.&lt;br /&gt;&amp;rarr; 그건 추상화된 계층에 대한 의존성임! 구현이 안되어있음&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://resilience4j.readme.io/docs/getting-started-3&quot;&gt;resilience4j 공식 문서&lt;/a&gt; 따라서&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;rdquo;io.github.resilience4j:resilience4j-spring-boot3:2.2.0&amp;rdquo;&lt;/code&gt; 와 aop 의존성을 직접 추가하여 사용 (&amp;rdquo;boot3&amp;rdquo; 임을 주의)&lt;br /&gt;&amp;rarr; 이렇게 해야 구현체가 의존성에 추가되어서 우리가 원하는 대로 작동이 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3.2 Resilience4j 설정 파일&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;application.yml&lt;/code&gt; 파일에서 설정이 중 필요한 것만 그때그때 사용. 공식문서 참고&lt;br /&gt;&amp;rarr; 설정값 다 외울 필요는 없음 기본적으로 default 값으로 반영됨.&lt;code class=&quot;language-yaml&quot;&gt;
&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.4 Fallback 메커니즘&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.4.1 Fallback 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Fallback 메서드 : 외부 서비스 호출이 실패했을 때 실행할 대체 로직&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@CircuitBreaker(name = &quot;myService&quot;, fallbackMethod = &quot;fallbackMethod&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;어노테이션에 이런식으로 메서드명 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.4.2 Fallback의 장점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시스템의 안정성을 높이고, 장애가 발생해도 사용자에게 일정한 응답을 제공&lt;br /&gt;&amp;rarr; 사용자에게 시스템이 안정적이라고 느끼게 할 수 있음. 실제로도 시스템 안정성에 도움이 됨&lt;/li&gt;
&lt;li&gt;장애가 다른 서비스에 전파되는 것을 방지&lt;br /&gt;&amp;rarr; 에러의 진원지 찾기도 쉬워짐&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.5.1 Resilience4j Dashboard 설정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Resilience4j Dashboard : 서킷 브레이커의 상태 모니터링&lt;/li&gt;
&lt;li&gt;Prometheus와 Grafana를 사용하여 Resilience4j 서킷 브레이커의 상태를 실시간으로 모니터링&lt;/li&gt;
&lt;li&gt;Prometheus를 통해 수집된 메트릭을 Grafana 대시보드에서 시각화할 수 있습니다.&lt;/li&gt;
&lt;li&gt;구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;build.gradle&lt;/code&gt; 의존성 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application.yml&lt;/code&gt; 파일 설정&lt;code class=&quot;language-java&quot;&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://${hostname}:${port}/actuator/prometheus&lt;/code&gt; 에서 서킷브레이커 항목 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5.6 Resilience4j와 Spring Cloud 연동&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다른 Spring Cloud 구성 요소와 쉽게 통합 : Resilience4j는 Spring Cloud Netflix 패키지의 일부로, Eureka와 Ribbon 등 다른 Spring Cloud 구성 요소와 쉽게 통합 가능&lt;/li&gt;
&lt;li&gt;Spring Cloud의 서비스 디스커버리와 로드 밸런싱을 활용하여 더욱 안정적인 마이크로서비스 아키텍처를 구축할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>TIL</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/244</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/TIL-MSA-%EC%84%9C%ED%82%B7-%EB%B8%8C%EB%A0%88%EC%9D%B4%EC%BB%A4-Resilience4j#entry244comment</comments>
      <pubDate>Fri, 22 Nov 2024 18:45:08 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 24/11/15 QueryDSL 은 누구냐</title>
      <link>https://onedaythreecoding.tistory.com/entry/TIL-241115-QueryDSL-%EC%9D%80-%EB%88%84%EA%B5%AC%EB%83%90</link>
      <description>&lt;h1&gt;개요&lt;/h1&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;br /&gt;&amp;rarr; 쿼리문의 where 조건 변동성이 크고 복잡하여 쿼리문을 객체로 다루어 동적으로 생성하고자 할 때 사용&lt;/li&gt;
&lt;li&gt;ORM JPA 로 웬만한 건 다 할 수 있지만, JPA 어노테이션으로 네이티브 쿼리로만 관리하기에는 &lt;br /&gt;쿼리가 복잡하거나 가독성이 떨어지거나 유지보수가 어려울 경우에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;sql 실행하지 않아도 컴파일 시 오류 발견 가능&lt;/li&gt;
&lt;li&gt;메서드로 쿼리 구성 가능&lt;/li&gt;
&lt;li&gt;유지보수성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알고 가야 할 것, 선택의 이유&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;queryDSL 도 JPA 처럼 내부적으로 EntityManager 기반으로 동작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDBC 템플릿 기준으로 쿼리 생성하는 건 똑같음&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;복잡한 쿼리문에 대헤 조금만 쿼리가 길어져도 유지보수가 어려워졌기 때문&lt;/li&gt;
&lt;li&gt;JPA 를 사용하다가 한계가 와서 쓴건지? 어떤 부분이 힘들어서 도입한 건지? 고민한 흔적 남기기&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;h1&gt;Query DSL&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Java 로 sql 쿼리를 작성할 수 있게 해주는 라이브러리&lt;/li&gt;
&lt;li&gt;컴파일 단계에서 에러 확인 가능
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JPQL 등 직접 sql 을 활용하는 방식은 sql 문 실행 전까지는 오류를 알 수 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동적으로 쿼리를 작성해 코드가 유연해짐&lt;/li&gt;
&lt;li&gt;QFile : QueryDsl 에서 쿼리를 위해 사용하는 java 파일
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;entity 생성 후 gradle 의 compile java 를 실행하면&lt;/li&gt;
&lt;li&gt;entity 마다 QFile 을 자동으로 생성함&lt;/li&gt;
&lt;li&gt;src/generated : IntelliJ 에서 QFile 저장 위치 (여기 아니면 build 아래에)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음에 이어서..&lt;/p&gt;</description>
      <category>TIL</category>
      <category>JPA</category>
      <category>QueryDSL</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/243</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/TIL-241115-QueryDSL-%EC%9D%80-%EB%88%84%EA%B5%AC%EB%83%90#entry243comment</comments>
      <pubDate>Fri, 15 Nov 2024 17:23:38 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 24/11/12 TS : 에러는 앞뒤상황을 보자</title>
      <link>https://onedaythreecoding.tistory.com/entry/TIL-241112-TS-%EC%97%90%EB%9F%AC%EB%8A%94-%EC%95%9E%EB%92%A4%EC%83%81%ED%99%A9%EC%9D%84-%EB%B3%B4%EC%9E%90</link>
      <description>&lt;h1&gt;에러 메세지&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인데 이게 중요한 게 아니었음&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;2024-11-12T16:10:25.033+09:00 ERROR 16884 --- [spartaOrderSystem] [           main] o.s.b.web.embedded.tomcat.TomcatStarter  : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name 'webSecurityConfig' defined in file [C:\\JHPro\\spring_2024\\sparta\\back\\build\\classes\\java\\main\\com\\spartaordersystem\\global\\security\\config\\WebSecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 1: Error creating bean with name 'userDetailsServiceImpl' defined in file [C:\\JHPro\\spring_2024\\sparta\\back\\build\\classes\\java\\main\\com\\spartaordersystem\\global\\security\\user\\UserDetailsServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'userRepository' defined in com.spartaordersystem.domains.user.repository.UserRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Cannot resolve reference to bean 'jpaSharedEM_entityManagerFactory' while setting bean property 'entityManager'

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 에러만 계속 구글링하고 뜯어보고 관련 클래스 검색하고 그랬다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 사실 진짜 문제는 이 에러 메세지보다 &lt;b&gt;이전에 찍힌 에러&lt;/b&gt;에 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BaseAudit 을 상속받은 엔티티 클래스에 같은 이름의 컬럼이 존재해서 Bean을 생성할 수 없다는 메세지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트를 급하게 세팅하며 임시로 만들어둔 코드가 남아있던 것이었다.&lt;/p&gt;
&lt;h1&gt;배운 것&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 메세지를 볼 때, 디버깅을 할 때에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞뒤 상황도 찾아보는 유연함을 갖자..&lt;/p&gt;</description>
      <category>TIL</category>
      <category>Til</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/242</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/TIL-241112-TS-%EC%97%90%EB%9F%AC%EB%8A%94-%EC%95%9E%EB%92%A4%EC%83%81%ED%99%A9%EC%9D%84-%EB%B3%B4%EC%9E%90#entry242comment</comments>
      <pubDate>Tue, 12 Nov 2024 23:47:20 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 24/11/11 포스트맨 보다는 통합 테스트 짤래요</title>
      <link>https://onedaythreecoding.tistory.com/entry/TIL-241111-%ED%8F%AC%EC%8A%A4%ED%8A%B8%EB%A7%A8-%EA%B7%80%EC%B0%AE%EC%95%84-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A7%A4%EB%9E%98</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;JUnit5 로 통합테스트 해보고 싶어서 공부&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포스트맨 의존도를 낮추고 싶다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;@SpringBootTest : 모든 스프링 빈을 로드(스캔&amp;amp;등록)함. application context 를 생성하여 테스트 실행
&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;옵션
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;classes : 해당 클래스만 빈으로 등록(모든 빈 등록x)&lt;/li&gt;
&lt;li&gt;properties, value : 같은 속성. @Value 에 들어가는환경변수 값 주입&lt;/li&gt;
&lt;li&gt;webEnvironment : application context 관련 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MOCK : 기본값. application context 를 생성하지만 mock 환경으로 제공하므로 내장서버(tomcat) 실행되지 않음&lt;/li&gt;
&lt;li&gt;RANDOM_PORT : 실제 서블릿환경 제공. application context 생성. 내장서버 실행. 사용되지 않는 랜덤 포트를 listen 함&lt;/li&gt;
&lt;li&gt;DEFINED_PORT : 실제 서블릿환경 제공. 웹 기반의 application context 생성. 내장서버 실행. 지정해준 포트를 listen 함&lt;/li&gt;
&lt;li&gt;NONE : 기본적인 application context 를 로드[?]. 서블릿 환경 제공 안함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SpringBootTest.WebEnvironment. ~&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;@ActiveProfiles : 테스트 시 사용할 profile 지정 가능 (yml파일도 이 설정 따라감)&lt;/li&gt;
&lt;li&gt;@Transactional : 함께 사용 시 테스트 후 롤백됨됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;테스트 방식&lt;/h1&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;@AutoConfigureMockMvc
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MockMvc 객체 사용해 테스트&lt;/li&gt;
&lt;li&gt;테스트 후 롤백 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TestRestTemplate
&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;롤백 안됨&lt;/li&gt;
&lt;li&gt;RestTemplate 의 테스트 버전 [?]&lt;/li&gt;
&lt;/ul&gt;
** TestRestTemplate 빈은 webEnvironment 옵션 설정에 따라 자동으로 생성됨&lt;/li&gt;
&lt;li&gt;둘다 사용하지 않고 그냥 service, repository 메서드 사용&lt;br /&gt;&amp;rarr; 이건 안할 듯함&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 공부 중..&lt;/p&gt;</description>
      <category>TIL</category>
      <category>JUnit</category>
      <category>Spring</category>
      <category>Til</category>
      <category>테스트</category>
      <category>통합테스트</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/241</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/TIL-241111-%ED%8F%AC%EC%8A%A4%ED%8A%B8%EB%A7%A8-%EA%B7%80%EC%B0%AE%EC%95%84-%ED%86%B5%ED%95%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A7%A4%EB%9E%98#entry241comment</comments>
      <pubDate>Mon, 11 Nov 2024 23:27:41 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1074 Z | Gol5 | 분할정복(divide and conquer) Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1074-Z-Gol5-%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5divide-and-conquer-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1074 Z | Gol5 | 분할정복(divide and conquer) Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1074&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1074&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;유형 : 분할정복&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;[문제해석] &lt;br /&gt;2^N&amp;nbsp;크기의&amp;nbsp;정사각형&amp;nbsp;배열을&amp;nbsp;Z모양으로&amp;nbsp;나눠&amp;nbsp;탐색할&amp;nbsp;때,&amp;nbsp;특정&amp;nbsp;칸을&amp;nbsp;탐색하는&amp;nbsp;순서&amp;nbsp;구하기 &lt;br /&gt;Z모양&amp;nbsp;탐색이란&amp;nbsp;:&amp;nbsp;2사분면&amp;nbsp;&amp;gt;&amp;nbsp;1사분면&amp;nbsp;&amp;gt;&amp;nbsp;3사분면&amp;nbsp;&amp;gt;&amp;nbsp;4사분면&amp;nbsp;순 &lt;br /&gt;&lt;br /&gt;[구상] &lt;br /&gt;4x4.&amp;nbsp;N=2 &lt;br /&gt;4x4/4&amp;nbsp;=&amp;nbsp;4&amp;nbsp;가&amp;nbsp;제일&amp;nbsp;큰&amp;nbsp;사각형&amp;nbsp;크기. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;현재&amp;nbsp;탐색중인&amp;nbsp;면&amp;nbsp;크기&amp;nbsp;=&amp;nbsp;S &lt;br /&gt;이&amp;nbsp;중에&amp;nbsp;누구?&amp;nbsp;r,c&amp;nbsp;값이&amp;nbsp;S&amp;nbsp;/&amp;nbsp;2&amp;nbsp;보다&amp;nbsp;큰가?작은가? &lt;br /&gt;그&amp;nbsp;안에서&amp;nbsp;다시&amp;nbsp;2^N/2&amp;nbsp;/2&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;2&amp;nbsp;&amp;nbsp;3 &lt;br /&gt;0&amp;nbsp;|&amp;nbsp;0&amp;nbsp;&amp;nbsp;1&amp;nbsp;&amp;nbsp;4&amp;nbsp;&amp;nbsp;5 &lt;br /&gt;1&amp;nbsp;|&amp;nbsp;2&amp;nbsp;&amp;nbsp;3&amp;nbsp;&amp;nbsp;6&amp;nbsp;&amp;nbsp;7 &lt;br /&gt;2&amp;nbsp;|&amp;nbsp;8&amp;nbsp;&amp;nbsp;9&amp;nbsp;&amp;nbsp;12&amp;nbsp;13 &lt;br /&gt;3&amp;nbsp;|&amp;nbsp;10&amp;nbsp;11&amp;nbsp;14&amp;nbsp;15&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;S&amp;nbsp;=&amp;nbsp;4.&amp;nbsp;S/2&amp;nbsp;=&amp;nbsp;2 &lt;br /&gt;r&amp;nbsp;=&amp;nbsp;2,&amp;nbsp;c&amp;nbsp;=&amp;nbsp;1&amp;nbsp;-&amp;gt;&amp;nbsp;r&amp;nbsp;&amp;gt;&amp;nbsp;2,&amp;nbsp;c&amp;nbsp;&amp;lt;&amp;nbsp;2&amp;nbsp;-&amp;gt;&amp;nbsp;3사분면&amp;nbsp;-&amp;gt;&amp;nbsp;+line^2 &lt;br /&gt;좌표의&amp;nbsp;사분면에&amp;nbsp;따라&amp;nbsp;값&amp;nbsp;더해줌 &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;-&amp;gt; 제일 큰 사각형부터 사분할 하면서 누적값 더함, 좌표이동, 다음 단계 작은 사각형 탐색&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;문제 속 상황도 정확하게 파악할 것 (순서 같은 거 시작값이 0인지 1인지..)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;
N, r, c = map(int, input().split())

def recur(S, r, c, add_sum) :
    if S == 1 :
        return add_sum
    
    add = 0
    line = S//2
    square = line**2
    
    if line &amp;lt;= r :
        add += square*2
        r -= line
    if line &amp;lt;= c :
        add += square
        c -= line

    return recur(line, r, c, add_sum+add)

print(recur(2**N, r, c, 0))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>분할정복</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/240</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1074-Z-Gol5-%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5divide-and-conquer-Python#entry240comment</comments>
      <pubDate>Tue, 29 Oct 2024 14:52:28 +0900</pubDate>
    </item>
    <item>
      <title>CUID 리드미 읽어보기</title>
      <link>https://onedaythreecoding.tistory.com/entry/CUID-%EB%A6%AC%EB%93%9C%EB%AF%B8-%EC%9D%BD%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;발단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pk로 auto increment 된 정수형을 쓰느냐 UUID를 쓰느냐 둘다 쓰느냐의 이야기를 하다가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CUID 라는 걸 알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 TSID 도 쓰게 되어서 유니크한 값에 사용되는 자료형들에 대해 짧게 적어보려는 글의 시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(+UUID version,GUID, TSID 와의 비교까지 해보면..좋을 듯?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CUID&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/paralleldrive/cuid2&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/paralleldrive/cuid2&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1727668644978&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - paralleldrive/cuid2: Next generation guids. Secure, collision-resistant ids optimized for horizontal scaling and perfor&quot; data-og-description=&quot;Next generation guids. Secure, collision-resistant ids optimized for horizontal scaling and performance. - paralleldrive/cuid2&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/paralleldrive/cuid2&quot; data-og-url=&quot;https://github.com/paralleldrive/cuid2&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/tEplD/hyXaz4Fpfl/tibFTRcDkSfzqnVuMOSQok/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/caL4fF/hyXaJ0xvCZ/NlEBS4tR9C9lRQCYjl4Y40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/paralleldrive/cuid2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/paralleldrive/cuid2&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/tEplD/hyXaz4Fpfl/tibFTRcDkSfzqnVuMOSQok/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/caL4fF/hyXaJ0xvCZ/NlEBS4tR9C9lRQCYjl4Y40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - paralleldrive/cuid2: Next generation guids. Secure, collision-resistant ids optimized for horizontal scaling and perfor&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Next generation guids. Secure, collision-resistant ids optimized for horizontal scaling and performance. - paralleldrive/cuid2&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리드미에 따르면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;다음 값을 예측하거나 유효한 키값을 추론하는 등 &lt;b&gt;예측이 쉽지 않음&lt;/b&gt;&lt;br /&gt;여러개의 독립적인 무작위 소스와 해시값들을 사용한다고 함. NIST 표준을 따르는 암호학적으로 보안적인 해싱 알고리즘(Sha3)을 따름
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CUID 는 다음과 같은 엔트로피 소스들을 사용함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생성한 값이 JavaScript 코드나 CSS 스타일링에서 문제 없이 사용될 수 있도록 그 규칙을 따르는 값을 사용 ( 숫자로 시작할 수 없고, 문자 또는 _,$로 시작해야 함)&lt;/li&gt;
&lt;li&gt;시스템 현재 시각&lt;/li&gt;
&lt;li&gt;의사 난수 값&lt;/li&gt;
&lt;li&gt;session counter, 사용자 지문값 등 프로그램이나 하드웨어에서 사용되는 값들&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;수평적인 여러 기기에서도 유니크 값 생성 보장&lt;br /&gt;많은 pseudo-random 알고리즘들은 시간ms 값을 랜덤시드로 사용하는데, 이는 수많은 기기에서 같은 시각에 실행될 경우 충돌 가능성이 있음 (v4 UUID에서 보고됨). CUID 는 다양한 엔트로피 소스를 사용해 이를 피함&lt;/li&gt;
&lt;li&gt;중복값 생성(충돌)될 확률이 낮음&lt;br /&gt;약 4,000,000,000,000,000,000 번 생성했을 때 중복된 id값이 생성될 확률이 50%정도이다.&lt;/li&gt;
&lt;li&gt;오프라인 환경에서 생성 가능&lt;/li&gt;
&lt;li&gt;특수문자가 없는 편리성&lt;br /&gt;Base36 으로 인코딩되어있음 (소문자와 숫자만 사용됨)&lt;/li&gt;
&lt;li&gt;빠름. 비동기연산이나 사용자가 인지할 만한 지연 없음&lt;/li&gt;
&lt;li&gt;지나치게 빠르지 않음 (보안 이슈)&lt;br /&gt;너무 빠르면 복제값을 찾거나 entropy-hiding 을 깨트리는 병렬공격을 당할 수 있음.&lt;br /&gt;unique id 세계에서는 가장 빠른 자는 보안적인 측면에서 지게 되어있다고 한다. &lt;br /&gt;(무슨말이지 해시와 관련된 문제라고 한다. 따로 알아봐야할 듯&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;순차적인 id 값이 필요할 때&lt;/li&gt;
&lt;li&gt;tight 한 loop나 render loop 등 빠르고 효율적인 처리 성능이 요구되는 상황&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;CUID1 &lt;span&gt;는 deprecated됨&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;CUID1 는 항상 'c' 로 시작했지만, CUID2 는 아님!&lt;/li&gt;
&lt;li&gt;CUID2 는 기존 CUID1 를 그대로 대체할 수는 없다. 그래서 교체 라이브러리를 마련함&lt;/li&gt;
&lt;li&gt;CUID 는 isCuid() 라는 메서드로 DB접근 없이 유효한 Cuid 형식인지 확인이 가능함&lt;br /&gt;UUID 는 따로 기능을 제공하진 않아서 보통 정규표현식으로 형식을 확인함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌든 타임값도 쓰는 것 같은데 어떻게 보안적으로 좋다는 건지 아직은 잘 모르겠다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타임값을 쓰지만 다른 시드도 복합적으로 사용해서?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UUID에서 report되던 충돌문제를 개선했다고 하니 좋아졌을 것 같긴 한데   어떻게?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UUID 나 다른 알고리즘에 대해서도 찾아보고 비교해 봐야 체감이 될 것 같다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 점에서는 좋겠고, 어떤 점에서는 약하겠는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt; isCuid() 라는 메서드로 DB접근 없이 유효한 Cuid 형식인지 확인이 가능함&lt;/i&gt; -&amp;gt; 이 점이 편리하다고 들었다는데 이마저도 아직 체감이 안된다. 그냥 형식을 확인하는 거면 UUID도 정규표현식 확인하는 메서드 하나 만들어서 쓰면 똑같아지는 건가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CUID 라는 게 있다는 걸 이렇게 알아보았습니다~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;~의문~&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정말 좋은가?&lt;/li&gt;
&lt;li&gt;오프라인 환경에서 생성할 수 없는 키값은 어떤게 있나?&lt;/li&gt;
&lt;li&gt;gzip 이 뭐지&lt;/li&gt;
&lt;li&gt;unique id 세계에서는 가장 빠른 자는 보안적인 측면에서 지게 되어있다고 한다? &lt;br /&gt;-&amp;gt; 해시에서 보안과 속도의 관계&lt;/li&gt;
&lt;li&gt;Base36&lt;/li&gt;
&lt;li&gt;NIST 표준&lt;/li&gt;
&lt;li&gt;Sha3&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>이모저모/Dev스몰톡&amp;amp;프젝</category>
      <category>CUID</category>
      <category>PK</category>
      <category>unique key</category>
      <category>uuid</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/239</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/CUID-%EB%A6%AC%EB%93%9C%EB%AF%B8-%EC%9D%BD%EC%96%B4%EB%B3%B4%EA%B8%B0#entry239comment</comments>
      <pubDate>Mon, 30 Sep 2024 14:32:54 +0900</pubDate>
    </item>
    <item>
      <title>[실험실] ws와 http 의 세션은 개별적?</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EC%8B%A4%ED%97%98%EC%8B%A4-ws%EC%99%80-http-%EC%97%90%EC%84%9C%EC%9D%98-%EC%84%B8%EC%85%98</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;배경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;websocket 에서의 사용자 인증로직을 구현하다가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 정보를 세션으로 관리하는 과정에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ws 연결되어있는 사용자가 http 요청을 보내면 세션이 어떻게 되는 거지? 라는 생각이 들었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분리되어 관리된다는 말을 봤는데 진짜인지 궁금해서 테스트해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시나리오&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;회원가입&lt;/li&gt;
&lt;li&gt;ws 연결 맺고 세션에 사용자 정보 저장하기&lt;/li&gt;
&lt;li&gt;ws 연결 맺은 클라이언트가 http 요청 보내고,&lt;/li&gt;
&lt;li&gt;http 요청 시 세션 정보(세션 id, 사용자 정보) 로그로 출력해보기
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;예상안1 : ws와 http 세션이 따로 관리되어 세션id가 다르고 사용자 정보가 확인되지 않음&lt;/li&gt;
&lt;li&gt;예상안2 : 같은 클라이언트로 인식해서 ws연결에서 세션에 저장된 사용자 정보가 출력됨&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;츄라이~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 소켓 연결 전 http 요청으로 확인한 session id&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;: 7491B3D4CEDFCE8CD4D0B2A02282B39F&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;20240927 15:37:08.764 [http-nio-8080-exec-5] [INFO] c.r.s.d.t.TestController - session id : 7491B3D4CEDFCE8CD4D0B2A02282B39F 

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 소켓 연결 CONNECT 요청 시 확인한 session id&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 9379cea3-1fe2-13f8-1b0d-8daa4c072a10&lt;/p&gt;
&lt;pre id=&quot;code_1727420871599&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;20240927 15:40:53.054 [http-nio-8080-exec-8] [INFO] c.r.s.d.r.c.StompInterceptor - session id : 9379cea3-1fe2-13f8-1b0d-8daa4c072a10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 소켓 연결 후 SUBSCRIBE, SEND 요청 시 확인한 session id&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 9379cea3-1fe2-13f8-1b0d-8daa4c072a10&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; CONNECT 시와 동일&lt;/p&gt;
&lt;pre id=&quot;code_1727420897165&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;20240927 15:44:24.125 [http-nio-8080-exec-9] [INFO] c.r.s.d.r.c.StompInterceptor - session id : 9379cea3-1fe2-13f8-1b0d-8daa4c072a10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 소켓 연결 후 http 요청으로 확인한 session id&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 7491B3D4CEDFCE8CD4D0B2A02282B39F&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 소켓 연결 전 http 요청과 동일, 소켓 연결 세션과는 다름!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 소켓 세션에 저장해둔 사용자 정보도 확인되지 않음(null)&lt;/p&gt;
&lt;pre class=&quot;basic&quot;&gt;&lt;code&gt;20240927 15:45:47.291 [http-nio-8080-exec-2] [INFO] c.r.s.d.t.TestController - session id : 7491B3D4CEDFCE8CD4D0B2A02282B39F
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&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;같은 클라이언트라도 http 와 ws 의 세션은 별개로 생성되고 관리된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;session id 값이 형식까지 완전히 다르다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;http : 7491B3D4CEDFCE8CD4D0B2A02282B39F&lt;/li&gt;
&lt;li&gt;ws : 9379cea3-1fe2-13f8-1b0d-8daa4c072a10&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;디버깅해보니 ws 요청 시 sessionAttribute 내의 session id 는 simpSessionId 라는 이름으로 저장되어있었다.&lt;/li&gt;
&lt;li&gt;[?] simp가 붙는 건 simpMessageBroker 사용과 관련이 있는걸까? session id 는 정확히 누가 생성해서 부여하는 걸까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ws 연결 중 http 를 요청해도 다른 세션을 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 왜 이런 의문이 든 적이 없었나 생각해봤는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api 서버 위주로 개발하면서 session을 사용하지 않고 stateless하게 token (JWT) 을 사용한 인증 로직만 구현해왔었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 websocket 기능을 제대로 구현하면서 세션을 사용하게 되어 요런 고민을 하게 된 거였다. 재밌다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 생긴 의문에 대해서도 다음에 알아보겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( [?] simp가 붙는 건 simpMessageBroker 사용과 관련이 있는걸까? session id 는 정확히 누가 생성해서 부여하는 걸까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Spring boot</category>
      <category>HTTP</category>
      <category>Session</category>
      <category>Spring</category>
      <category>websocket</category>
      <category>WS</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/238</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EC%8B%A4%ED%97%98%EC%8B%A4-ws%EC%99%80-http-%EC%97%90%EC%84%9C%EC%9D%98-%EC%84%B8%EC%85%98#entry238comment</comments>
      <pubDate>Fri, 27 Sep 2024 16:10:18 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 5430 AC | deque, 구현 | Gol5 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-5430-AC-deque-%EA%B5%AC%ED%98%84-Gol5-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 5430 AC | deque, 구현 | Gol5 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/5430&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/5430&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : deque, 구현&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[문제&amp;nbsp;해석] &lt;br /&gt;주어진 문자열에 주어진 연산을 수행한 결과를 출력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;양쪽에서 제거가 일어나야 하므로 데크deque 사용&lt;/li&gt;
&lt;li&gt;시간제한이 넉넉하지는 않은 것 같아서&lt;br /&gt;뒤집기 연산 시 문자열을 실제로 뒤집지 않고 현재 앞방향을 표시해두고 그에따라 처리하는 방향으로 풀어보겠음&lt;/li&gt;
&lt;li&gt;시간 제한 : 1초&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;br /&gt;아래 경우들 처리에 좀 시간이 걸렸다. 알아두자~&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앞뒤의 특정 문자 제거하고 입력받기 : string = input().strip(&amp;rdquo;\n그리고제거할문자들&amp;rdquo;)&lt;br /&gt;** input()은&amp;nbsp;마지막에&amp;nbsp;\n가&amp;nbsp;같이&amp;nbsp;들어오므로&amp;nbsp;strip에&amp;nbsp;\n도&amp;nbsp;포함시켜&amp;nbsp;제거해줘야&amp;nbsp;함&lt;/li&gt;
&lt;li&gt;구분자 없는 문자열 한 문자씩 리스트로 입력받기 : list(string.strip())&lt;br /&gt;개행문자만 제거해주고 list로 감쌈&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리스트 뒤집는 함수도 sort랑 방식이 똑같음을 기억~
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;list.reverse()&lt;/li&gt;
&lt;li&gt;reversed(list)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from collections import deque
import sys
input = sys.stdin.readline

ans = []
for _ in range(int(input())) : 
    coms = list(input().strip())
    # print(coms)

    num_cnt = int(input())

    # 빈 숫자배열이 주어지는 경우 처리
    if num_cnt == 0 : 
        nums = deque()
        _ = input()
    else :
        nums = deque(map(int, input().strip(&quot;\n[]&quot;).split(&quot;,&quot;))) # input()은 마지막에 \n가 같이 들어오므로 strip에 \n도 포함시켜줘야 함

    # print(nums)

    isReversed = False
    isError = False

    for com in coms :
        if com == 'R' :
            isReversed = not isReversed
        else : # 'D'인 경우
            if len(nums) &amp;lt;= 0 :
                isError = True
                break

            if isReversed :
                nums.pop()
            else :
                nums.popleft()
    
    # error 가 난 케이스 처리
    if isError :
        ans.append(&quot;error&quot;)
    else : 
        # 연산이 정상 완료된 경우 마지막 방향에 따라 결과 저장
        if isReversed :
            nums.reverse()
        ans.append(&quot;[&quot; + &quot;,&quot;.join(map(str, nums)) + &quot;]&quot;)

print(&quot;\n&quot;.join(ans))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>deque</category>
      <category>구현</category>
      <category>백준</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/237</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-5430-AC-deque-%EA%B5%AC%ED%98%84-Gol5-Python#entry237comment</comments>
      <pubDate>Sun, 18 Aug 2024 17:43:33 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2696 치즈 | Gol4 | bfs Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2696-%EC%B9%98%EC%A6%88-Gol5-bfs-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 2696 치즈 | BFS | Gol5 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2636&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2636&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : bfs&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[문제 해석] &lt;br /&gt;치즈의 공기에 노출된 부분이 한시간에 한칸씩 녹을때, 마지막에 녹은 치즈 칸수와 다 녹기까지 걸린 시간은?&lt;br /&gt;-&amp;gt; BFS로 공기를 상하좌우 인접한 것만 탐색해나가면 치즈 속 구멍을 공기로 인식하지 않도록 처리가 가능해짐!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구상 &lt;br /&gt;1.&amp;nbsp;전체&amp;nbsp;치즈&amp;nbsp;개수&amp;nbsp;적어둠 &lt;br /&gt;2.&amp;nbsp;BFS&amp;nbsp;돌며&amp;nbsp;공기랑&amp;nbsp;인접한(이번에&amp;nbsp;녹을)&amp;nbsp;치즈&amp;nbsp;위치&amp;nbsp;적어둠 &lt;br /&gt;3.&amp;nbsp;녹임.&amp;nbsp;녹은&amp;nbsp;치즈&amp;nbsp;개수를&amp;nbsp;전체&amp;nbsp;치즈&amp;nbsp;개수에서&amp;nbsp;빼서&amp;nbsp;남은&amp;nbsp;치즈&amp;nbsp;개수&amp;nbsp;계산해둠 &lt;br /&gt;4. 다 녹을 때까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;br /&gt;확실히 공기인 부분에서 시작하는 bfs를 쓰면 된다!&lt;br /&gt;비슷한 유형이 등장할테니 기억해둘것~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;from collections import deque
import sys
input = sys.stdin.readline

h, w = map(int, input().split())
board = [list(map(int, input().split())) for _ in range(h)]

# 전체 치즈 개수 세놓기
cheeze_cnt = 0
for line in board : 
    for v in line :
        if v == 1 :
            cheeze_cnt += 1

dir_arr = [(1,0), (0,1), (-1,0), (0,-1)]
que = deque()

time = 0
last_cheeze_cnt = 0
while cheeze_cnt :
    time += 1

    visited = [[False]*w for _ in range(h)]
    que.append((0,0)) # 공기 전부 탐색
    visited[0][0] == True
    melt = []

    while que :
        x,y = que.pop()
        
        for dir in dir_arr :
            nx = x + dir[0]
            ny = y + dir[1]

            # 0이면 공기 bfs 탐색에 넣고 1이면 녹을 치즈로 추가
            if 0 &amp;lt;= nx &amp;lt; h and 0 &amp;lt;= ny &amp;lt; w and not visited[nx][ny] :
                visited[nx][ny] = True
                if board[nx][ny] == 0 :
                    que.append((nx, ny))
                else :
                    melt.append((nx, ny))

    # 치즈 녹이기
    last_cheeze_cnt = cheeze_cnt # 직전에 녹인 치즈 수를 저장해두고 마지막에 답 출력에 사용
    cheeze_cnt -= len(melt)

    for i, j in melt :
        board[i][j] = 0

print(time)
print(last_cheeze_cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/236</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2696-%EC%B9%98%EC%A6%88-Gol5-bfs-Python#entry236comment</comments>
      <pubDate>Sun, 18 Aug 2024 15:42:43 +0900</pubDate>
    </item>
    <item>
      <title>[Conference] Google I/O Extended Incheon 송도 (24/07/28)</title>
      <link>https://onedaythreecoding.tistory.com/entry/Conference-Google-IO-Extended-Incheon-%EC%86%A1%EB%8F%84-240728</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;송도 컨벤시아에서 열린 GDG 주최 컨퍼런스에 다녀왔다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google I/O Extended Incheon 송도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://festa.io/events/5477&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://festa.io/events/5477&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722095525734&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;I/O Extended 2024 Incheon | Festa!&quot; data-og-description=&quot;Festa에서 당신이 찾는 이벤트를 만나보세요.&quot; data-og-host=&quot;festa.io&quot; data-og-source-url=&quot;https://festa.io/events/5477&quot; data-og-url=&quot;https://festa.io/events/5477&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bskKHA/hyWCBchbGw/OC3uAL5mLPmkOEAazbjWa0/img.gif?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540&quot;&gt;&lt;a href=&quot;https://festa.io/events/5477&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://festa.io/events/5477&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bskKHA/hyWCBchbGw/OC3uAL5mLPmkOEAazbjWa0/img.gif?width=960&amp;amp;height=540&amp;amp;face=0_0_960_540');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;I/O Extended 2024 Incheon | Festa!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Festa에서 당신이 찾는 이벤트를 만나보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;festa.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연이 있는 발표자 분께서 초대권을 주셔서 다녀올 수 있었다 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 취준생 수준에서 알아듣기 힘든 내용이 많을까봐 고민도 했는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로는 다녀오길 잘 한 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zqECQ/btsI60WZYWd/nfW6hQzKdSKMhh2cjrgyCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zqECQ/btsI60WZYWd/nfW6hQzKdSKMhh2cjrgyCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zqECQ/btsI60WZYWd/nfW6hQzKdSKMhh2cjrgyCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzqECQ%2FbtsI60WZYWd%2FnfW6hQzKdSKMhh2cjrgyCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;520&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;2288&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 누군가 말씀해주신 컨퍼런스 발표 추천 기준을 생각해서 오늘 들을 세션들을 골라봤는데,&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;아무것도 모르는 기술, 또는 내용을 다 알것 같은 세션이라면 듣지 말 것&lt;/li&gt;
&lt;li&gt;그냥 입문을 위한 설명 세션도 bad (내가 구글에서 공식문서를 읽는 게 나음. 굳이 현장에서?)&lt;/li&gt;
&lt;li&gt;이 발표에 나의 생각을 바꿀만한 포인트가 있다고 생각하면 valuable함. 기술설명보다는 생각하게 하는 주제 추천. 이런 세션의 경우 현장감도 효과가 있음&lt;/li&gt;
&lt;li&gt;현장감을 느끼고 싶은 주제 또는 발표자분을 직접 대면하고싶거나 질문하고 싶은 게 있을 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이대로 덮어두면 잊어버릴게 뻔하니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지극히 개인적인 관점에서 세션별 메모를 정리한다. 기억용으로!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 올리브영 전시영역의 꺾이지 않는 안정성&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;오늘 발표 내용 대부분은 올영 테크블로그에도 나와있다(올영도 테크블로그가 있는지 몰랐다,,)&lt;/li&gt;
&lt;li&gt;외주를 맡기고 사용하다가 2021년 쯤부터 개발쪽에 신경쓰기 시작하기 시작한 것 같음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;온라인몰의 전시영역 : &lt;br /&gt;많은 데이터를 담으며, (집계데이터를 비롯한..)&lt;br /&gt;빠른 속도를 요구하고, (3초의 법칙)&lt;br /&gt;비즈니스적(마케팅)으로도 중요한 역할을 함&lt;/li&gt;
&lt;li&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;온프레미스 : scale up, out이 어려움 -&amp;gt; 가용자원 모두 동원해도 세일기간 대처 어려움&lt;/li&gt;
&lt;li&gt;enterprize legacy : 외주로 맡겨 사용해오던 프로젝트라 히스토리를 아는 개발자가 없음&lt;/li&gt;
&lt;li&gt;얼마나 느렸는데?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Main LCP90 : 3.37초&lt;br /&gt;100명 중 90명이 3.37초의 대기를 경험함 -&amp;gt; 3초의 법칙.. 너무 김&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모놀리식 구조는 이런 문제들을 개선하고 테스트하기 위한 과정에 시간도 너무 오래 걸렸음&lt;br /&gt;=&amp;gt; 빠른 개선을 위해 MSA도입 결정!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MSA 장점
&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;redis캐싱 : 성능저하 대부분이 RDB접근 영역에서 발생함을 캐치하고 도입
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이에 따라오는 Cache Stampede 문제라는 것이 있다고 함. 만료된 redis 키값을 사용해 데이터를 찾게 되는 상황에 대한 것..인듯.. ttl expired time?&lt;br /&gt;-&amp;gt; 이에 대한 대처로 Mongo DB를 같이 사용 -&amp;gt; 이부분 잘 이해 못했음&amp;nbsp; .. . .&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;개선된 부분들
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영적 측면
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몽고DB 사용 -&amp;gt; json저장 용이, schemaless 로 유연&amp;nbsp;&lt;/li&gt;
&lt;li&gt;캐싱 데이터 세분화 : 같은 페이지 안에서도 자주 조회되는 데이터 위주로 캐싱&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다양한 고가용성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;캐시서버에 문제가 생겼을 때에도, 늦더라도 메인페이지 내용을 띄우는 것이 UX에 긍정적&lt;br /&gt;-&amp;gt; 서킷 브레이커..라는 걸 사용해서 redis캐시서버 - 몽고DB - RDB 순으로 응답이 없을 경우 차례로 다음으로 넘어가서 늦더라도 데이터를 받아올 수 있도록 플로우를 만듦&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;등등..&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그래서 얼마나 개선?&lt;br /&gt;평균 10초 걸리던 부분을 0.96초까지 내림 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;영상자료를 사용하고&lt;/li&gt;
&lt;li&gt;넣고 싶은 이미지가 있으면 chatGPT한테 만들어달라고 할 수도 있고&lt;/li&gt;
&lt;li&gt;현실의 리얼한 멘트가 담긴 자료(댓글, 메세지, 후기 등..)&lt;/li&gt;
&lt;li&gt;밈&amp;amp;짤 활용&lt;/li&gt;
&lt;li&gt;결과 전/후 비교영상으로 보여주기&lt;/li&gt;
&lt;li&gt;기술토크 마무리 후 소감이나 감상을 짧게 이야기하는 것도 괜찮은 듯&lt;/li&gt;
&lt;li&gt;발표는 마지막에 3줄요약하라!는 말도 있었다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Web Application Service에서의 E2E 테스트 자동화 사례 공유&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테스트코드 : 의도한 대로 작동하는지 확인하기 위한 코드
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;개발자 스스로의 신뢰도 향상&lt;/li&gt;
&lt;li&gt;CI/CD 파이프라인의 핵심 요소&lt;/li&gt;
&lt;li&gt;예외 처리 개선&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시간 x2 이상 (빠르게 서비스하는게 중요한 상황이라면 과감히 포기하는 것도..)&lt;/li&gt;
&lt;li&gt;if 테스트가 놓치고 있는 시나리오가 존재할 경우, 개발자에게 잘못된 안정감을 주는 것이 됨&lt;/li&gt;
&lt;li&gt;테스트 규모가 너무 커져서 이해가 어려워지거나 테스트가 도는 데에 시간이 너무 많이 걸릴 수 있음&lt;/li&gt;
&lt;li&gt;테스트를 로컬에서만 돌리려고 구축하진 않음. CI 구축에도 시간이 듦&lt;/li&gt;
&lt;li&gt;잘못된 방향의 테스트에 고통받을 수 있음 ex) 내부 함수 호출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&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;통합 테스트 : 단위테스트 + 인프라적인 요소&lt;/li&gt;
&lt;li&gt;기능 테스트 : 특정 기능 하나의 flow에 대한 테스트&lt;/li&gt;
&lt;li&gt;E2E 테스트 : 회원가입부터 시작해서 유저가 경험할 수 있는 모든 기능 흐름을 처음부터 끝까지 다 해봄. 유저 관점. (기능 테스트와 E2E 테스트는 이분법적으로 나눠지기보다는 상황에 따라 쓰임)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;보안 테스트 : sql injection 등... 테스트 시 보안관점에서 체크해야할 것들이 리스트업 되어있는 정부에서 제공하는 웹페이지가 있음. 찾아보자&lt;/li&gt;
&lt;li&gt;사용성 테스트 : 수동&lt;/li&gt;
&lt;li&gt;회귀 테스트 : 새로운 기능을 추가, 수정했을 때 기존 기능이 정상 작동하는지 확인하는 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작성 시 팁
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;input, output 에 집착하기 보다는 이 테스트가 어떤 비즈니스를 해결해야 하는가에 집중하자! 네이밍할 때도.&lt;/li&gt;
&lt;li&gt;Negative case, 예외 상황에 대한 테스트가 중요하다. 정상적인 흐름 외. ex) timezone 맞추기, 경계값 등..&lt;/li&gt;
&lt;li&gt;다양한 관점 : 유저는 우리가 예상하지 못한 순서로 조작할 수도 있음을 기억하고 테스트도 커버해볼 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CI 에 올려 자동화하지 않으면 반쪽짜리 테스트코드라고 할 수도 있겠죠&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 기술주의자 vs 논리주의자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물리적인 기술에 기준하는가 vs 추상적인 논리에 기준하여 대상을 해석, 평가하는가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;추상화 - 높은 수준 - 인간복잡도 높음(어려움) - 논리주의&lt;/li&gt;
&lt;li&gt;구체화 - 낮은 수준 - 인간복잡도 낮음(쉬움) - 기술주의&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**인간복잡도가 높다는 말은 이 글에서 사람이 이해하기 어렵다고 느낀다는 의미로 쓰임&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex1) 뛰어난 가수는?&lt;/p&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;가창력은 부족하지만 표현력이 뛰어나 사람들에게 감동을 주는 가수 &lt;br /&gt;-&amp;gt; 논리주의 : 음악이 존재해야 가창력이 존재함. &lt;br /&gt;음악은 인간의 감정을 풍부하게 하는 것으로, 가창력은 감정을 풍부하게 하는 기술&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex2) 아래 두 코드는 Command Pattern일까?&lt;/p&gt;
&lt;pre id=&quot;code_1722302691740&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 첫번째 코드
Class SaveCommand {
	void Execute() {
    	...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722302736581&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 두번째 코드
Class Save {
	void Run() {
    	...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Command Pattern의 정의는 개체 자체가 명령인 패턴.&lt;br /&gt;첫번째와 달리 두번째 코드는 Command Pattern 에서 많이 쓰이는 네이밍을 사용하지 않았지만,&lt;br /&gt;개체 자체가 명령이 된다는 정의를 따르고 있으므로&lt;br /&gt;논리주의 관점에서 보면 두가지 모두 Command Pattern이 맞다.&lt;/li&gt;
&lt;li&gt;하지만 두번째가 항상 옳다는 것은 아니다. 어떻게 판단하는게 자신에게 유리한지 상황에 따라 생각할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex3) API로써, HTTP 통신의 단점은?&lt;/p&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;논리주의 : 통신계약이 너무 구체적으로 표현되어있어, 도메인에 맞게 적용하려면 무리가 많다. GET, PUT, POST, DELETE ... 이 중 어디에 넣을지 고민되는 요청들이 존재함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**HTTP 통신의 정의 : 도메인을 풀어낸 원격지의 API 호출 계약&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**개발자 : 요구사항을 컴퓨터 언어로 풀어내주는 번역가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex4) 아래 두 사례 모두 연속통합(CI) 인가?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 자동화(Github Action 등으로) 사용해 통합하는 팀&lt;/li&gt;
&lt;li&gt;빌드 자동화는 사용하지 않지만 매일 빌드 및 통합을 수동으로 하는 팀&lt;br /&gt;-&amp;gt; 연속통합 : 작업 결과물을 매우 자주 통합하는 것&lt;br /&gt;첫번째 팀은 자동화 툴을 사용하지만 얼마나 자주인지 알 수 없음. 빌드 자동화는 CI를 편하게 하는 도구일 뿐&lt;br /&gt;두번째 팀은 매일 하는 것이 보장되므로 논리주의 관점에서는 두번째가 더 연속통합을 하고 있다고 볼 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex5) 로그아웃 API 에 대해 : 서버에서 아무 동작도 안하는데 호출을 해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 기술주의의 관점에서는 앞으로 발생할 비즈니스적인 요구에 방어할 수 있게 되었다고 답함(로그아웃 시 어떤 처리를 해줘야 하는 상황이 올 경우..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;논리주의자 : 높은 논리주의를 바탕으로 기술주의도 사용해 대상을 바라봄&lt;br /&gt;패러다임, 특징, 논리에 집착함. 거시적 관점. 인간복잡도 중시. 실제 가치 창출에 관심을 두고 인간적 입장에서 생각함&lt;/li&gt;
&lt;li&gt;기술주의자 : 기술주의만 사용해 대상을 바라봄. 시작은 쉬운것부터 하는 것이므로 이걸로 시작하며, 여기서 10년차 이상의 극소수가 논리주의로 넘어간다고 봄&lt;br /&gt;성능, 기술, 최적화에 집착함. 미시적 관점. 시간, 공간 복잡도 중시. 신기술. 트렌드에 관심을 두고 기계적 입장에서 생각함. 특정 기술을 굉장히 사랑함&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1951&quot; data-origin-height=&quot;837&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnmaGi/btsIPKAZJHM/GAkgiHYTr8Go64hSUs0Grk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnmaGi/btsIPKAZJHM/GAkgiHYTr8Go64hSUs0Grk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnmaGi/btsIPKAZJHM/GAkgiHYTr8Go64hSUs0Grk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdnmaGi%2FbtsIPKAZJHM%2FGAkgiHYTr8Go64hSUs0Grk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;271&quot; data-origin-width=&quot;1951&quot; data-origin-height=&quot;837&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1815&quot; data-origin-height=&quot;626&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfRf3C/btsIRWNfTZC/9UKb5csJTXjkLjyKIzo7k1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfRf3C/btsIRWNfTZC/9UKb5csJTXjkLjyKIzo7k1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfRf3C/btsIRWNfTZC/9UKb5csJTXjkLjyKIzo7k1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfRf3C%2FbtsIRWNfTZC%2F9UKb5csJTXjkLjyKIzo7k1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;221&quot; data-origin-width=&quot;1815&quot; data-origin-height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;클린 아키텍처의 저자 로버트 C. 마틴&lt;br /&gt;아키텍처에 대한 예시 코드는 하나도 제공하지 않았음 -&amp;gt; 논리주의자로서, 구체적인 건 그에게 중요치 않음을 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 왜 내가 만든 쿼리는 느릴까?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세션은 집중도 떨어져서 거의 못들었는데 ㅎㅎ.. 그래도 유의미할만한 내용만 몇줄 적자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;쿼리 읽는 순서 : from -&amp;gt; where -&amp;gt; on(join) -&amp;gt; select -&amp;gt; 그 외&lt;/li&gt;
&lt;li&gt;클러스터 인덱스 : 인덱스 키 값을 기반으로 테이블데이터 행 전체를 정렬해서 저장&lt;/li&gt;
&lt;li&gt;sql의 select절에서 컬럼 순서는 성능과 상관 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 데이터독을 활용하여 애플리케이션에서 발생하는 모든것을 모니터링하기&lt;/h3&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;Monitoring : 현재 상태에 대한 수치 확인. 내부 사정까진 x&lt;/li&gt;
&lt;li&gt;Observability : 시스템 내부 동작 이해에 중점. 문제 근본 원인을 파악하는 목적. 아래와 같은 효과가 있다!
&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;전체 system 이해도 증가&lt;/li&gt;
&lt;li&gt;대규모 시스템 관리 가능&lt;/li&gt;
&lt;li&gt;문제 예방, 최적화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;APM : 서비스 전반에 걸친 문제 원인을 파악할 수 있음&lt;br /&gt;이게 없었으면 로그 하나하나 순서 생각해서 찾아가 뜯어봐야 하는데 너무 좋은 기능~&lt;/li&gt;
&lt;li&gt;RUM (Real User Monitoring) : 데이터독이 짱잘해줌. 사용자가 어떤 흐름으로 머물다 갔는지 화면을 마우스커서까지 그대로 보여줌&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리는 여기까지다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 컨퍼런스에서 어떤 걸 얻어가야하는지 조금 느껴졌던 날이었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기억에 남는 발표는 기술vs논리주의자 였다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예시들이 참 와닿았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아직 모르는 게 정말 많고.. 공부할수록 새로운 게 계속 나오다보니&lt;br /&gt;나도 기술에 집착하고 새로운 기술 신기한 기술을 자꾸 찍먹해볼까싶고 기웃거리는 느낌이 있었는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으론 좀더 넓은 관점에서 공부할 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트에 대한 세션도 아직 테스트 왕초보인 나에게는 어떤 부분이 집중해야 하는지 더 이해하게 된 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올리브영 세션같은 경험기는 항상 재밌다 히히&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대학생 막차로 데이터독 써봐야지~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즐건 컨퍼런스 끝!&lt;/p&gt;</description>
      <category>이모저모/Dev스몰톡&amp;amp;프젝</category>
      <category>2024</category>
      <category>Google IO</category>
      <category>컨퍼런스</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/235</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/Conference-Google-IO-Extended-Incheon-%EC%86%A1%EB%8F%84-240728#entry235comment</comments>
      <pubDate>Tue, 30 Jul 2024 11:33:15 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2869 달팽이는 올라가고싶다 | 수식 | Bro1 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2869-%EB%8B%AC%ED%8C%BD%EC%9D%B4%EB%8A%94-%EC%98%AC%EB%9D%BC%EA%B0%80%EA%B3%A0%EC%8B%B6%EB%8B%A4-%EC%88%98%EC%8B%9D-Bro1-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 2869 달팽이는 올라가고싶다 | 수식 | Bro1 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2869&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2869&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;시간제한 0.25초&lt;/li&gt;
&lt;li&gt;시간제한때문에&amp;nbsp;반복문&amp;nbsp;없이&amp;nbsp;수식으로&amp;nbsp;해결해야&amp;nbsp;하는데&amp;nbsp;수식정리&amp;nbsp;머리가&amp;nbsp;안굴러가서&amp;nbsp;애먹은&amp;nbsp;문제..^.^&lt;br /&gt;브론즈라고 만만하게 보지 마라 하늘아래 알고리즘문제는 공평하다&lt;/li&gt;
&lt;li&gt;math.ceil() 대신 그냥 /나누기연산 후 int()함수 적용한 결과와 같은지 비교해서 소수점유무를 알아내 처리해도 올림과 같은 효과를 낼 수 있다.&lt;/li&gt;
&lt;li&gt;풀이
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;달팽이는 무조건 낮에 도착하므로, 마지막날 갈 수 있는 거리는 밤에 미끄러지는 영향을 받지 않는다.&lt;br /&gt;따라서 한번의 낮 이동값을 미리 전체거리에서 빼두고, &lt;br /&gt;남은 거리 이상 이동하려면 낮밤(하루)를 최소 몇번 지나야 하는지 계산하면 된다. &lt;br /&gt;나눗셈과 올림 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import math
A, B, V = map(int, input().split())

day = A-B
print(math.ceil((V-A)/day)+1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>수식</category>
      <category>수학</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/234</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2869-%EB%8B%AC%ED%8C%BD%EC%9D%B4%EB%8A%94-%EC%98%AC%EB%9D%BC%EA%B0%80%EA%B3%A0%EC%8B%B6%EB%8B%A4-%EC%88%98%EC%8B%9D-Bro1-Python#entry234comment</comments>
      <pubDate>Fri, 5 Jul 2024 02:00:42 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 12904 A와 B | greedy? | Gol5 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-12904-A%EC%99%80-B-greedy-Gol5-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 12904 A와 B | greedy? | Gol5 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/12904&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/12904&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : greedy&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제&amp;nbsp;해석] &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열을&amp;nbsp;두&amp;nbsp;종류의&amp;nbsp;연산만으로&amp;nbsp;특정&amp;nbsp;문자열로&amp;nbsp;바꿀&amp;nbsp;수&amp;nbsp;있는지&amp;nbsp;확인하는&amp;nbsp;문제 &lt;/li&gt;
&lt;li&gt;문자열 S, T주어짐. S를 T로 바꿀 것 &lt;/li&gt;
&lt;li&gt;가능한 연산 두가지&amp;nbsp; &amp;nbsp;&amp;nbsp;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마지막에 A추가 &lt;/li&gt;
&lt;li&gt;전체를 뒤집고 B추가 &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;바꿀 수 있으면 1, 없으면 0 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;그냥 bfs로 연산 경우의 수 다 확인?? -&amp;gt; 너무 오래 걸림&lt;/li&gt;
&lt;li&gt;거꾸로&amp;nbsp;연산?&amp;nbsp;-&amp;gt;&amp;nbsp;이게&amp;nbsp;연산&amp;nbsp;수가&amp;nbsp;적다! &lt;/li&gt;
&lt;li&gt;T -&amp;gt; S 뒤집는 연산 - 현재 마지막 글자에 따라 연산이 결정된다. &lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;마지막 글자가 A이면 : 마지막에 위치한 A제거 &lt;/li&gt;
&lt;li&gt;마지막 글자가 B이면 : 마지막에 위치한 B제거 후 전체 뒤집기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

S = input().strip()
T = input().strip()

while len(S) &amp;lt; len(T) :
    last = T[-1] #마지막 글자 없애기 전에 킵해두기
    T = T[:-1]

    if last == 'B' :
        T = T[::-1] #뒤집기

print(1 if S==T else 0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Greedy</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/233</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-12904-A%EC%99%80-B-greedy-Gol5-Python#entry233comment</comments>
      <pubDate>Wed, 3 Jul 2024 19:33:50 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2812 크게 만들기 | stack, greedy? | Gol3 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2812-%ED%81%AC%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0-stack-greedy-Gol3-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 2812 크게 만들기 | stack, greedy? | Gol3 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2812&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2812&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : stack, greedy?&lt;br /&gt;이런것도 그리디에 속하는 구나..? 흠&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진 숫자에서 숫자 K개를 지웠을 때 얻을 수 있는 가장 큰 수 구하기&lt;/li&gt;
&lt;li&gt;고려해야할&amp;nbsp;조건 &lt;br /&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;그 숫자 자체 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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;앞에서부터&amp;nbsp;그&amp;nbsp;다음자리가&amp;nbsp;현재자리보다&amp;nbsp;크면&amp;nbsp;현재자리&amp;nbsp;삭제 &lt;br /&gt;-&amp;gt; 아이디어는 맞음! 구현에 스택을 떠올릴 것..~&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

stack = []
N, K = map(int, input().split())

nums = tuple(map(int, input().strip()))
cnt = 0
for num in nums :
    #앞에서부터 숫자를 돌면서 바로 앞 숫자가 현재숫자보다 커질때까지 앞 숫자를 지운다. K개까지만 지운다
    while stack and stack[-1] &amp;lt; num and cnt &amp;lt; K:
        stack.pop()
        cnt += 1
    stack.append(num) #앞 숫자가 더 커졌거나 K를 다 지웠으면 append

#K개를 다 못지웠다면 이미 앞으로 갈수록 더 큰 숫자라는 뜻이므로, 뒤쪽을 잘라냄
print(''.join(map(str, stack[:N-K])))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Greedy</category>
      <category>stack</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/232</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2812-%ED%81%AC%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0-stack-greedy-Gol3-Python#entry232comment</comments>
      <pubDate>Wed, 3 Jul 2024 18:46:10 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1946 신입사원 | greedy | Sil1 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1946-%EC%8B%A0%EC%9E%85%EC%82%AC%EC%9B%90-greedy-Sil1-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1946 신입사원 | greedy | Sil1 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1946&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1946&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : greedy &lt;br /&gt;아이디어 생각하는 거 하나 더 배웠다..~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;두가지&amp;nbsp;순위&amp;nbsp;중&amp;nbsp;적어도&amp;nbsp;하나는&amp;nbsp;선발인원&amp;nbsp;내&amp;nbsp;최저순위가&amp;nbsp;아닌&amp;nbsp;사람&amp;nbsp;최대인원수 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아&amp;nbsp;설명부터&amp;nbsp;어렵다&lt;/li&gt;
&lt;li&gt;일단 각 순위 1등을 뽑아놓고 이 1등의 다른 순위를 이기는 사람을 고름? x&lt;/li&gt;
&lt;li&gt;순위 합 작은순? x&lt;/li&gt;
&lt;li&gt;-&amp;gt; 첫번재 순위대로 정렬해두고 차례로 보면서 b순위 기준점을 넘는지 판단하면 된다..&lt;br /&gt;a는&amp;nbsp;갈수록&amp;nbsp;낮아지기만&amp;nbsp;하므로&amp;nbsp;b만&amp;nbsp;보면&amp;nbsp;되는&amp;nbsp;것..!!..! &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백준 예제로 예시를 들어보면 아래처럼 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;v는 선발한 사람, x는 선발하지 않은 사람이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예1) &lt;br /&gt;1&amp;nbsp;4&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;4 &lt;br /&gt;2&amp;nbsp;3&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;3 &lt;br /&gt;3&amp;nbsp;2&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;2 &lt;br /&gt;4&amp;nbsp;1&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;1 &lt;br /&gt;5&amp;nbsp;5&amp;nbsp;x &lt;br /&gt;=&amp;gt;&amp;nbsp;4명 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;예2) &lt;br /&gt;1&amp;nbsp;4&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;4 &lt;br /&gt;2&amp;nbsp;5&amp;nbsp;x &lt;br /&gt;3&amp;nbsp;6&amp;nbsp;x &lt;br /&gt;4&amp;nbsp;2&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;2 &lt;br /&gt;5&amp;nbsp;7&amp;nbsp;x &lt;br /&gt;6&amp;nbsp;1&amp;nbsp;v&amp;nbsp;-&amp;gt;&amp;nbsp;min_second&amp;nbsp;=&amp;nbsp;1 &lt;br /&gt;7&amp;nbsp;3&amp;nbsp;x &lt;br /&gt;=&amp;gt; 3명&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;idea~&lt;br /&gt;고려해야 할 조건이 두개일 때, 한쪽 조건을 정렬해둠으로써 신경안써도 특정 조건에 해당되도록 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

T = int(input())

ans = []
for _ in range(T) :
    N = int(input())
    ranks = [tuple(map(int, input().split())) for _ in range(N)]
    
    ranks.sort() #앞쪽 등수 순으로 정렬

    cnt = 0
    min_second = N+1 #두번째 등수 최소값 저장
    for rank in ranks :
        # 첫번째 등수가 낮아지는 쪽으로 돌면서, 두번째 등수가 현재까지 선발한 사람들의 최저등수보다 높으면 선발 후 최저등수 갱신
        if rank[1] &amp;lt; min_second :
            cnt += 1
            min_second = rank[1]

    ans.append(cnt)

print('\n'.join(map(str, ans)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Greedy</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/231</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1946-%EC%8B%A0%EC%9E%85%EC%82%AC%EC%9B%90-greedy-Sil1-Python#entry231comment</comments>
      <pubDate>Wed, 3 Jul 2024 17:46:41 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1715 카드 정렬하기 | greedy, heap | Gol4 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1715-%EC%B9%B4%EB%93%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0-greedy-heap-Gol4-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1715 카드 정렬하기 | greedy, heap | Gol4 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1715&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1715&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : greedy, heap(우선순위큐)&lt;br /&gt;전에 풀었던 14698 슬라임문제랑 비슷해서 금방 해결~&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&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;모든&amp;nbsp;카드를&amp;nbsp;합치는&amp;nbsp;비교&amp;nbsp;횟수&amp;nbsp;최솟값 &lt;/li&gt;
&lt;li&gt;정렬된&amp;nbsp;카드묶음들이&amp;nbsp;주어짐 &lt;/li&gt;
&lt;li&gt;카드&amp;nbsp;묶음은&amp;nbsp;두개씩&amp;nbsp;합칠&amp;nbsp;수&amp;nbsp;있고,&amp;nbsp;합칠때에는&amp;nbsp;두&amp;nbsp;묶음의&amp;nbsp;카드수&amp;nbsp;합만큼&amp;nbsp;비교가&amp;nbsp;필요함 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&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;전에&amp;nbsp;풀었던&amp;nbsp;슬라임문제랑&amp;nbsp;비슷하네? &lt;/li&gt;
&lt;li&gt;작은묶음부터&amp;nbsp;합치면&amp;nbsp;될&amp;nbsp;듯 &lt;/li&gt;
&lt;li&gt;새로&amp;nbsp;생긴&amp;nbsp;묶음을&amp;nbsp;포함해&amp;nbsp;다시&amp;nbsp;정렬해야&amp;nbsp;하므로&amp;nbsp;정렬&amp;nbsp;상태를&amp;nbsp;유지하면서&amp;nbsp;삽입삭제가&amp;nbsp;필요함&amp;nbsp;&lt;br /&gt;-&amp;gt;&amp;nbsp;힙(우선순위큐) &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys, heapq
input = sys.stdin.readline

N = int(input())

cards = [int(input()) for _ in range(N)]

heapq.heapify(cards)

add_sum = 0
while 1 &amp;lt; len(cards) :
    c1 = heapq.heappop(cards)
    c2 = heapq.heappop(cards)

    add = c1+c2
    add_sum += add
    heapq.heappush(cards, add)

print(add_sum)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Greedy</category>
      <category>heap</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>우선순위큐</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/230</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1715-%EC%B9%B4%EB%93%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0-greedy-heap-Gol4-Python#entry230comment</comments>
      <pubDate>Wed, 3 Jul 2024 15:38:11 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1931 회의실 배정 | greedy | Sil1 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1931-%ED%9A%8C%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95-greedy-Sil1-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1931 회의실 배정 | greedy | Sil1 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1931&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1931&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : greedy &lt;br /&gt;어떤 걸 기준으로 할 지 아이디어만 떠올리면 쉬웠던 문제. 근데 난 못 떠올려서 솔루션봤다 ㅎ.ㅎ&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제&amp;nbsp;해석] &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배치&amp;nbsp;가능한&amp;nbsp;회의의&amp;nbsp;최대&amp;nbsp;수&amp;nbsp;구하기 &lt;/li&gt;
&lt;li&gt;회의 시작시간과 끝나는 시간이 주어지며, 이는 같을 수도 있음 &lt;/li&gt;
&lt;li&gt;회의가 끝나자마자 시작 가능 &lt;/li&gt;
&lt;li&gt;시간은 24시간으로 분단위 없이 주어져서 편하게 쓰면 될듯~ &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&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;제일&amp;nbsp;많은&amp;nbsp;회의를&amp;nbsp;배치할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;경우는&amp;nbsp;? &lt;br /&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;빨리 시작? ㄴㄴ &lt;/li&gt;
&lt;li&gt;겹치는 수 세두고 적은 거 &amp;gt; 짧은 거? ㄴ &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;-&amp;gt; 대박 끝나는 시간이래... 맞네&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

N = int(input())

meets = []
for _ in range(N) :
    start, end = map(int, input().split())
    meets.append((end, start))

meets.sort()

cnt = 0
last_end = 0
for meet in meets :
    if last_end &amp;lt;= meet[1] :
        cnt += 1
        last_end = meet[0]

print(cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Greedy</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/229</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1931-%ED%9A%8C%EC%9D%98%EC%8B%A4-%EB%B0%B0%EC%A0%95-greedy-Sil1-Python#entry229comment</comments>
      <pubDate>Wed, 3 Jul 2024 00:32:23 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 14621 나만 안되는 연애 | MST (Kruscal) | Gol3 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14621-%EB%82%98%EB%A7%8C-%EC%95%88%EB%90%98%EB%8A%94-%EC%97%B0%EC%95%A0-MST-Kruscal-Gol3-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 14621 나만 안되는 연애 | MST (Kruscal) | Gol3 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14621&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14621&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : MST - kruscal &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;모든&amp;nbsp;대학교를&amp;nbsp;연결하는&amp;nbsp;최단경로&amp;nbsp;=&amp;nbsp;MST &lt;/li&gt;
&lt;li&gt;남초대학교M 와 여초대학교W 사이의 간선만 경로에 포함할 수 있음 &lt;/li&gt;
&lt;li&gt;모든 학교를 연결하는 경로가 존재하지 않을 경우 -1 출력 &lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 학교 수V &amp;lt;= 1000 &lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 간선 수E &amp;lt;= 10000 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크루스칼~로 해보자 간선수 노드수에 비해 그렇게 많은 것같진 않고 프림보다 빠른 편이니까&lt;br /&gt;특이사항은 간선 선택할 때, 연결된 노드의 학교종류가 같으면 선택하지 않도록 짜면 될 듯&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;간선 (가중치, 노드1, 노드2) 형식으로 한 리스트에 입력받음 &lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 때 노드1과 노드2의 학교타입이 같으면 리스트에 넣지 않음 (그래프에 사용할 수 없는 간선이므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가중치&amp;nbsp;오름차순&amp;nbsp;정렬 &lt;/li&gt;
&lt;li&gt;kruscal 로 경로 생성 : union find로 사이클이 없는지 확인하며 간선을 가중치 작은 것부터 greedy하게 선택 &lt;/li&gt;
&lt;li&gt;간선 개수가 V-1개일 때까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

V, E = map(int, input().split())
school_type = (&quot;0&quot;, ) + tuple(input().split()) # 0번째 인덱스는 사용하지 않음

edges = []
for _ in range(E) :
    v1, v2, w = map(int, input().split())
    
    #같은 타입의 학교이면 해당 간선은 사용하지 않음
    if school_type[v1] == school_type[v2] :
        continue
    edges.append((w, v1, v2))

edges.sort()

edge_cnt = 0
weight_sum = 0
root = [i for i in range(V+1)]

# union find
def union(v1, v2) :
    #find(v1) == find(v2) 인 경우는 호출 전에 걸러졌음
    root[find(v1)] = root[find(v2)]

def find(v) :
    if root[v] != v :
        root[v] = find(root[v])
    return root[v]

# kruscal
for w, v1, v2 in edges :
    if V-1 &amp;lt;= edge_cnt :
        break

    if find(v1) == find(v2) :
        continue

    union(v1, v2)
    edge_cnt += 1
    weight_sum += w

print(weight_sum if edge_cnt == V-1 else -1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>kruscal</category>
      <category>MST</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/228</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14621-%EB%82%98%EB%A7%8C-%EC%95%88%EB%90%98%EB%8A%94-%EC%97%B0%EC%95%A0-MST-Kruscal-Gol3-Python#entry228comment</comments>
      <pubDate>Wed, 26 Jun 2024 00:44:42 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1922 네트워크 연결 | MST (Prim, Kruscal) | Gol4 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1922-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%97%B0%EA%B2%B0-MST-Prim-Kruscal-Gol4-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1922 네트워크 연결 | MST (Prim, Kruscal) | Gol4 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1922&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1922&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 1 : MST - prim (396ms)&lt;/li&gt;
&lt;li&gt;유형 2 : MST - kruscal&amp;nbsp;(240ms)&lt;br /&gt;간선 수가 노드수보다 100배 많은데 prim보다 kruscal 이 더 빠르네 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;최소의 비용으로 모든 컴퓨터를 연결하는 경로 구하기&lt;br /&gt;=&amp;nbsp;MST&amp;nbsp;최소&amp;nbsp;스패닝&amp;nbsp;트리&amp;nbsp;구하기&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 컴퓨터 수N &amp;lt;= 1000&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 간선 수M &amp;lt;= 100000&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 가중치 &amp;lt;= 10000&lt;/li&gt;
&lt;li&gt;간선이 주어질 때 두 노드번호가 같을 수도 있다 &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MST 프림으로 풀어보자~ 노드선택&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 - Prim&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선 입력받아서 연결된 첫번째 노드별로 (가중치, 노드2) 형태로 리스트에 저장&lt;/li&gt;
&lt;li&gt;최소힙 사용(정렬유지하며 잦은 삽입 필요)&lt;/li&gt;
&lt;li&gt;현재 그래프에 포함되어있는 노드 표시하는 배열 사용&lt;/li&gt;
&lt;li&gt;현재 그래프와 인접한 간선들을 최소힙에 넣음&lt;/li&gt;
&lt;li&gt;최소힙에서 가장 가중치 적은 노드 pop해서 추가될 노드가 현재 그래프에 포함되어있는 노드인지 확인 &lt;br /&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;사이클 생기면 넘어감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;간선 수 V-1개까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔&amp;nbsp;크루스칼로&amp;nbsp;풀어보즈아~ &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 - Kruscal&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선 입력받아서 (가중치, 노드1, 노드2) 형태로 한 리스트에 모두 저장&lt;/li&gt;
&lt;li&gt;간선 가중치 오름차순으로 정렬&lt;/li&gt;
&lt;li&gt;간선 리스트 돌면서 가장 가중치 작은 간선부터 사이클 생기는지 확인(union find) &lt;br /&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;사이클 생기면 넘어감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;간선 수 V-1개까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - Prim&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import heapq
import sys
input = sys.stdin.readline

V = int(input())
E = int(input())

edges = [[] for _ in range(V+1)] # i번 인덱스에 i번 노드와 연결된 간선을 (가중치, 노드2) 형태로 저장

for _ in range(E) :
    v1, v2, w = map(int, input().split())
    if v1 == v2 : 
        continue # 같은 노드를 잇는 간선은 넘어감

    edges[v1].append((w, v2))
    edges[v2].append((w, v1)) # TS

heap = []
heap.append((0,1))
in_graph = [False]*(V+1)
edge_cnt = -1
weight_sum = 0

while edge_cnt &amp;lt; V-1 :
    w, v2 = heapq.heappop(heap)

    if in_graph[v2] :
        continue

    in_graph[v2] = True
    edge_cnt += 1
    weight_sum += w
    for edge in edges[v2] :
        heapq.heappush(heap, edge)

print(weight_sum)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - Prim&lt;/h3&gt;
&lt;pre id=&quot;code_1719329922017&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

V = int(input())
E = int(input())

edges = []
for _ in range(E) :
    v1, v2, w = map(int, input().split())
    edges.append((w, v1, v2))

edges.sort()
edge_cnt = 0
weight_sum = 0

root = [i for i in range(V+1)]

def union(v1, v2) :
    # find(v1) == find(v2) 인 경우는 이미 union호출 전에 걸러졌음
    root[find(v2)] = root[find(v1)]

def find(v) :
    if root[v] != v :
        root[v] = find(root[v])
    return root[v]

for w, v1, v2 in edges :
    if edge_cnt == V-1 :
        break

    if v1 == v2 or find(v1) == find(v2) : 
        continue
    
    union(v1, v2)
    edge_cnt += 1
    weight_sum += w

print(weight_sum)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DSA/Algorithm</category>
      <category>kruscal</category>
      <category>MST</category>
      <category>prim</category>
      <category>Union Find</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/227</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1922-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%97%B0%EA%B2%B0-MST-Prim-Kruscal-Gol4-Python#entry227comment</comments>
      <pubDate>Wed, 26 Jun 2024 00:39:40 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1197 최소 스패닝 트리 | MST (Kruscal, Union Find) | Gol4 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC-MST-Kruscal-Gol4-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1197 최소 스패닝 트리 | MST (Kruscal,&amp;nbsp;Union&amp;nbsp;Find) | Gol4 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1197&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1197&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : MST - 크루스칼 알고리즘&lt;br /&gt;크루스칼 알고리즘은 간선 추가 시마다 사이클이 생기는 지 확인하기 위해 주로 Union Find를 사용한다.&amp;nbsp;&lt;br /&gt;그래서 그 내용도 나옴!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[문제 해석]&lt;br /&gt;주어진 그래프의 최소 스패닝 트리MST를 구하는 문제&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= 정점V &amp;lt; 10000&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 간선E &amp;lt; 100000&lt;/li&gt;
&lt;li&gt;음수 가중치 간선 존재, 양방향&lt;/li&gt;
&lt;li&gt;가중치 절댓값 &amp;lt; 1000000&lt;/li&gt;
&lt;li&gt;시간 제한 1초&lt;/li&gt;
&lt;li&gt;메모리 제한 128MB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;MST 구하는 법
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;크루스칼&amp;nbsp;:&amp;nbsp;간선을&amp;nbsp;greedy로&amp;nbsp;선택,&amp;nbsp;사이클없이(union&amp;nbsp;find)&lt;/li&gt;
&lt;li&gt;프림 : 노드를 선택. 노드 중 간선 가중치가 가장 적은 것 &lt;br /&gt;&lt;br /&gt;직전 글에 프림으로 풀어보았고, 이번에는 크루스칼로 풀어본다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;간선을 모두 입력받고 가중치 오름차순으로 정렬해둠 &lt;br /&gt;(최소힙 써도 되긴 한데 잦은 추가삭제가 일어나는 게 아니라서.. 시간 공간 복잡도만 올라감)&lt;/li&gt;
&lt;li&gt;union find에 쓸 루트노드테이블 만듦&lt;/li&gt;
&lt;li&gt;가장 가중치가 작은 간선을 확인함. 간선에 연결된 노드 추가 시 사이클이 생기지 않는지 union find로 두 노드의 루트노드 확인 &lt;br /&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;로트노드 같으면 사이클 생기므로 추가하지 않고 넘어감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;간선이 V-1개가 될 때까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS 1 : RecursionError - sys.setrecursionlimit(10**9) 로 일단 막음&lt;br /&gt;(TS 2 의 코드 상 논리적인 오류때문에 재귀호출이 많이 발생한 거였음)&lt;/li&gt;
&lt;li&gt;TS 2 : 메모리 초과&lt;br /&gt;-&amp;gt; union find 에서 find함수 작성 실수&lt;br /&gt;find 재귀호출 시 find(root[v]) 이렇게 현재 노드의 루트값을 호출해줘야 경로압축이 된다!!기억해&lt;br /&gt;그냥&amp;nbsp;find(v)하면&amp;nbsp;메모리&amp;nbsp;초과가&amp;nbsp;발생!&amp;nbsp;현재&amp;nbsp;함수랑&amp;nbsp;동일한&amp;nbsp;호출이라&amp;nbsp;그런듯함&amp;nbsp;(정확히&amp;nbsp;이해는&amp;nbsp;못했지만)&amp;nbsp;&lt;br /&gt;-&amp;gt; TS 1도 이 부분 때문에 난 게 아닌가 생각이 들어서 find함수 수정 후에&amp;nbsp; sys.setrecursionlimit(10**9) 지워봤는데 잘 통과되었다. 이것때문 맞았음;;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# import heapq
import sys
input = sys.stdin.readline

V, E = map(int, input().split())
root = [i for i in range(V+1)]

edge_cnt = 0
weight_sum = 0
heap = []

for _ in range(E) :
    v1, v2, w = map(int, input().split())
    # heapq.heappush(heap, (w, v1, v2))
    heap.append((w, v1, v2))

heap.sort()

def union(v1, v2) :
    if v1 == v2 :
        return
    root[find(v1)] = find(v2)

def find(v) :
    if root[v] != v :
        root[v] = find(root[v]) # TS 2
    return root[v]

# 크루스칼
for w, v1, v2 in heap :
    if edge_cnt == V-1 :
        break

    #루트가 같으면 넘어감
    if find(v1) == find(v2) :
        continue

    #루트가 다르면 그래프에 추가
    union(v1, v2)
    edge_cnt += 1
    weight_sum += w
    
# 크루스칼 - 최소힙 쓰는 경우의 코드
# while edge_cnt &amp;lt; V-1 :
#     w, v1, v2 = heapq.heappop(heap)

#     #루트가 같으면 넘어감
#     if find(v1) == find(v2) :
#         continue

#     #루트가 다르면 그래프에 추가
#     union(v1, v2)
#     edge_cnt += 1
#     weight_sum += w

print(weight_sum)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>kruscal</category>
      <category>MST</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/226</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC-MST-Kruscal-Gol4-Python#entry226comment</comments>
      <pubDate>Tue, 25 Jun 2024 23:59:01 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1197 최소 스패닝 트리 | MST(Prim) | Gol4 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC-MSTPrim-Gol4-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1197 최소 스패닝 트리 | MST(Prim) | Gol4 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1197&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1197&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : MST - 프림 알고리즘&lt;br /&gt;프림이 노드를 추가하는 방식이지만 결국은 간선의 가중치를 확인해야 하는데&lt;br /&gt;논리를 너무 노드에만 집중해서 생각했다. 생각을 열어두자~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진&amp;nbsp;그래프의&amp;nbsp;최소&amp;nbsp;스패닝&amp;nbsp;트리MST를&amp;nbsp;구하는&amp;nbsp;문제 &lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 정점V &amp;lt; 10000 &lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 간선E &amp;lt; 100000 &lt;/li&gt;
&lt;li&gt;음수 가중치 간선 존재, 양방향 &lt;/li&gt;
&lt;li&gt;가중치 절댓값 &amp;lt; 1000000 &lt;/li&gt;
&lt;li&gt;시간 제한 1초 &lt;/li&gt;
&lt;li&gt;메모리 제한 128MB &lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[개념]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MST&amp;nbsp;구하는&amp;nbsp;법 &lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;크루스칼 : 간선을 greedy로 선택, 사이클없이(union find)&lt;/li&gt;
&lt;li&gt;프림 : 노드를 선택. 노드 중 간선 가중치가 가장 적은 것&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정점보다&amp;nbsp;간선&amp;nbsp;개수&amp;nbsp;범위가&amp;nbsp;더&amp;nbsp;커서&amp;nbsp;프림으로&amp;nbsp;짜보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 1 &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선 입력받아 리스트 i번째 인덱스에 i번째 노드의 간선들을 (가중치, 연결노드) 형태로 저장 &lt;/li&gt;
&lt;li&gt;사용 후보 간선 목록으로 최소 heap사용 &lt;/li&gt;
&lt;li&gt;heap에 (0, 1)을 넣고 시작해 1번노드를 시작점으로 함 &lt;/li&gt;
&lt;li&gt;heap에서 최소 간선을 pop해서 이미 추가된 노드인지 확인 &lt;br /&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;아직 없으면 그래프에 추가하고, 그 새로운 노드의 간선들을 모두 heap에 추가함 &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;추가된 노드 개수가 전체 노드 수만큼 될 때까지 반복&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;min&amp;nbsp;heap&amp;nbsp;을&amp;nbsp;쓰니까&amp;nbsp;로직이&amp;nbsp;훨씬&amp;nbsp;깔끔해졌다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;구상&amp;nbsp;2&amp;nbsp;(메모리&amp;nbsp;초과) &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간선 입력받아 2차원 리스트에 그래프 저장 &lt;/li&gt;
&lt;li&gt;1번 정점을 출발점으로, 그래프에 포함시킨다 &lt;/li&gt;
&lt;li&gt;그래프에 인접한 노드들 중 아직 그래프에 포함되어있지 않고, 연결된 간선 가중치가 가장 적은 노드를 그래프에 포함시킨다 &lt;/li&gt;
&lt;li&gt;모든 노드가 그래프에 포함되면 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;메모리초과 : 노드 수가 10000정도면 직접 2차원 배열로 관리하는 건 메모리 상 힘들다고 함&lt;br /&gt;-&amp;gt; 음.. 그래프 입력받는 걸 dict로 해야하나 - 해봤는데 시간초과&lt;br /&gt;-&amp;gt; 통과된 다른 프림 코드 찾아봤는데 두가지를 나랑 다르게 함
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;list로 하되 v*v만큼 안쓰고 list를 dict처럼 썼음 i번째 인덱스에 i번노드의 간선들을 [노드번호, 노드와의 가중치] 이런 형태로 저장함&lt;/li&gt;
&lt;li&gt;그리고 간선 최솟값 구할 때 heap씀 : heap쓰면 그래프에 포함된 노드들에 대한 간선목록은 새로운 노드가 추가될때마다 다 힙에 때려넣어두면 됨!&lt;br /&gt;=&amp;gt; 2가지 다 적용해서 통과~~ heap쓰니까 로직도 훨 깔끔하고 아주 맘에 든다^_^ &lt;br /&gt;프림이 노드를 추가하는 방식이지만 결국은 간선의 가중치를 확인해야 하는데&lt;br /&gt;논리를 너무 노드에만 집중해서 생각했다. 생각을 열어두자~&lt;br /&gt;근데 크루스칼로 더 많이 푸시는 것 같다&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import heapq
import sys
input = sys.stdin.readline

V, E = map(int, input().split())
graph = [[] for _ in range(V+1)] 
in_graph = [False]*(V+1)

for _ in range(E) :
    v1, v2, weight = map(int, input().split())
    graph[v1].append((weight, v2))
    graph[v2].append((weight, v1))

node_cnt = 0 #그래프에 포함된 노드 개수
weight_sum = 0

#그래프에 포함된 노드들에 인접한 간선 모음
edge_heap = [(0,1)] #가중치, 새로 추가될 노드번호
while node_cnt &amp;lt; V :
    w, node = heapq.heappop(edge_heap)

    #이미 그래프에 포함된 노드면 패스
    if in_graph[node] :
        continue

    #새로운 노드 추가
    in_graph[node] = True
    node_cnt += 1
    weight_sum += w

    #새로 추가된 노드의 간선들을 힙에 추가
    for edge in graph[node] :
        heapq.heappush(edge_heap, edge)

print(weight_sum)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>MST</category>
      <category>prim</category>
      <category>Tree</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>트리</category>
      <category>프림</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/225</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1197-%EC%B5%9C%EC%86%8C-%EC%8A%A4%ED%8C%A8%EB%8B%9D-%ED%8A%B8%EB%A6%AC-MSTPrim-Gol4-Python#entry225comment</comments>
      <pubDate>Sat, 22 Jun 2024 21:37:35 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 4195 친구 네트워크 | Union Find, 경로압축 | Gol2 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-4195-%EC%B9%9C%EA%B5%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-Union-Find-%EA%B2%BD%EB%A1%9C%EC%95%95%EC%B6%95-Gol2-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 4195 친구 네트워크 | Union Find, 경로압축 | Gol2 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/4195&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/4195&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : union find (합집합)&lt;br /&gt;union find 인데 노드 이름이 문자열인&lt;br /&gt;union find 짤 때 모든 연산은 루트노드 기준으로 할 것 기억하기..~!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;sns&amp;nbsp;친구추가(합집합&amp;nbsp;연산)&amp;nbsp;시마다&amp;nbsp;그&amp;nbsp;새로운&amp;nbsp;친구&amp;nbsp;네트워크에&amp;nbsp;포함된&amp;nbsp;전체&amp;nbsp;친구&amp;nbsp;수&amp;nbsp;구하기&lt;/li&gt;
&lt;li&gt;친구 네트워크란 친구 관계만으로 이동할 수 있는 사이를 말함&lt;/li&gt;
&lt;li&gt;친구 = 노드, 친구관계 = 간선&lt;/li&gt;
&lt;li&gt;시간제한 3초&lt;/li&gt;
&lt;li&gt;친구관계 수 &amp;lt;= 100000&lt;/li&gt;
&lt;li&gt;앗..&amp;nbsp;사이클이&amp;nbsp;생길수도&amp;nbsp;있겠네..?&amp;nbsp;같은&amp;nbsp;네트워크면&amp;nbsp;친구추가&amp;nbsp;무시해야겠다&lt;/li&gt;
&lt;li&gt;노드가&amp;nbsp;친구이름으로&amp;nbsp;주어지니까&amp;nbsp;딕셔너리를&amp;nbsp;쓰자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상&amp;nbsp;:&amp;nbsp;노드이름&amp;nbsp;문자열에&amp;nbsp;해당하는&amp;nbsp;번호를&amp;nbsp;따로&amp;nbsp;적어두고&amp;nbsp;사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드이름에 해당하는 번호를 dict에 적어두고 사용함. 번호가 아직 없는 새 이름이면 번호를 매겨 사용&lt;/li&gt;
&lt;li&gt;union find 에서 부모노드를 적어놓는 배열 list사용&lt;/li&gt;
&lt;li&gt;각 트리의 원소개수를 적어두는 배열을 사용함. 트리의 루트노드번호를 인덱스로 갖고 원소개수를 값으로 가짐&lt;/li&gt;
&lt;li&gt;x, y 친구 추가 시 &lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;x의 루트노드!의 자식노드로 y의 루트노드!를 넣음&lt;/li&gt;
&lt;li&gt;x의 루트노드의 원소 개수에 y의 루트노드의 원소 개수를 더해서 새로운 원소 개수를 업데이트 해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거꾸로 번호에서 이름을 찾아야 하는 경우가 없어서 괜찮을 것 같음. 츄라이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 2 : 노드이름 문자열 그대로 사용 (이건 구현 안해봤음)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드이름이 문자열이므로, union find 에서 부모노드를 적어놓는 배열로 list대신 dictionary를 사용함&lt;/li&gt;
&lt;li&gt;x, y 친구 추가 시 x의 루트노드!의 자식노드로 y의 루트노드!를 넣음&lt;/li&gt;
&lt;li&gt;해당 친구 네트워크의 루트노드 이름을 해당 그래프의 이름으로 사용함&lt;/li&gt;
&lt;li&gt;그래프 이름을 키, 네트워크의 노드 수를 값으로 갖는 딕셔너리에 노드 수를 카운트함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딕셔너리를 자주 쓰는 것보다 처음에 번호를 매겨두는 게 더 효율적일 것 같아서 두번째 말고 첫번째 방법으로 풀었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS : 출력초과???&lt;br /&gt;답보다 글자수 자체가 많은 경우에도 오답이 아닌 출력초과가 뜰 수 있다고 함. 틀렸습니다 랑 동일하다는 의미 &lt;br /&gt;ex) 답은 1인데 내 출력은 100일 경우 한글자보다 많은 세글자 출력임&lt;br /&gt;&lt;br /&gt;-&amp;gt;&amp;nbsp;로직&amp;nbsp;상&amp;nbsp;실수가&amp;nbsp;있었음&amp;nbsp;해결했음ㅎ&lt;br /&gt;union find 짤 때 모든 연산은 루트노드 기준으로 할 것 기억하기..~!&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;TIP : 출력은 한번에&lt;br /&gt;반복문 안에서 print 바로바로 하는 것보다 함수에서 전달전달 받느라 번거롭더라도 list에 append해뒀다가 한번에 출력하는 게 속도가 빠르다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline


def union(x, y) :
    root_x, root_y = find(x), find(y)
    
    #이미 같은 친구네트워크라면 그냥 넘어감
    if root_x == root_y :
        return cnt_arr[root_x]

    #다른 트리라면 한쪽 트리의 루트에 추가
    parent[root_y] = root_x # TS : 루트를 옮겨야 그 네트워크 전체가 가서 합집합연산이 성립함..~~!~!
    cnt_arr[root_x] += cnt_arr[root_y] #새로 추가된 친구가 포함된 트리의 원소개수를 새 루트 원소개수에 합쳐줌


    #해당 루트의 트리 원소 개수
    return cnt_arr[root_x]
    

#루트 찾아주는 메서드 +경로압축
def find(x) :
    if parent[x] != x :
        parent[x] = find(parent[x]) #경로압축
    return parent[x] #루트가 아니라도 경로압축해서 이미 루트로 업데이트 되어있음


def sol () :
    res = []
    for _ in range(int(input())) :
        friend_add = input().split()

        #친구번호 가져오기
        idxs = [-1,-1]
        for i in range(2) :
            friend = friend_add[i]

            #새로 등장한 이름
            if not friend in friend_nums :
                num = len(friend_nums)
                friend_nums[friend] = num
                parent.append(num) #트리 부모노드 번호표에 추가
                cnt_arr.append(1) #루트노드의 그래프 원소 개수세기표에도 추가
            
            idxs[i] = friend_nums[friend]

        #친구번호로 트리에 넣고 해당 친구네트워크 원소 수 구해 출력
        # print(union(idxs[0], idxs[1]))
        res.append(union(idxs[0], idxs[1]))
    return res

ans = []
for _ in range(int(input())) :
        
    friend_nums = {} #문자열 이름별 숫자 인덱스 적어두는 곳
    cnt_arr = [] #해당 인덱스가 루트노드인 트리(친구네트워크)의 원소개수 적어두는 곳
    parent = [] #트리 부모노드 적어두는 곳

    ans += sol() # TIP : 출력 모아뒀다 한번에 하는 게 빠름
    # sol()

print('\n'.join(map(str, ans)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Union Find</category>
      <category>경로압축</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/224</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-4195-%EC%B9%9C%EA%B5%AC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-Union-Find-%EA%B2%BD%EB%A1%9C%EC%95%95%EC%B6%95-Gol2-Python#entry224comment</comments>
      <pubDate>Sat, 22 Jun 2024 17:05:51 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1717 집합의 표현 | Union Find, 경로압축 | Gol5 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1717-%EC%A7%91%ED%95%A9%EC%9D%98-%ED%91%9C%ED%98%84-Union-Find-%EA%B2%BD%EB%A1%9C%EC%95%95%EC%B6%95-Gol5-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1717 집합의 표현 | Union Find, 경로압축 | Gol5 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1717&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1717&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : union find +경로압축&lt;br /&gt;둘 다 새롭게 구현해본 개념이라 공부가 많이 됐다!&lt;br /&gt;union find만으로는 시간초과가 나서 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;경로압축 등 &lt;/span&gt;find함수를 최적화하여 해결했다.&lt;br /&gt;새로운 재미가 있던 문제~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;0~n&amp;nbsp;각&amp;nbsp;하나씩만&amp;nbsp;원소로&amp;nbsp;갖는&amp;nbsp;집합&amp;nbsp;n+1개가&amp;nbsp;주어지고,&amp;nbsp;이들&amp;nbsp;간의&amp;nbsp;집합&amp;nbsp;연산&amp;nbsp;결과&amp;nbsp;출력하기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;0 a b : a와 b가 포함되어있는 집합의 합집합 연산&lt;/li&gt;
&lt;li&gt;1 a b : a와 b가 같은 집합의 원소인지 확인하는 연산&lt;/li&gt;
&lt;li&gt;a = b 일 수도 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= n &amp;lt;= 1000000&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= 연산개수m &amp;lt;= 100000&lt;/li&gt;
&lt;li&gt;시간제한 : 2초&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS 1 : 합집합 연산 시 x,y가 포함된 집합 전체를 연결시켜야하므로 루트를 찾아서 연결해야 함!&lt;/li&gt;
&lt;li&gt;TS 2 : 시간초과(pypy도)
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;합집합 시 항상 한쪽 루트노드의 자식으로 연결함&lt;/li&gt;
&lt;li&gt;find연산 재귀로 변경&lt;/li&gt;
&lt;li&gt;find연산 시 최적화 - 경로압축 적용!&lt;br /&gt;경로압축까지 적용해서 시간초과 해결할 수 있었다..~ 파이썬은 최적화가 생명이군아&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**6)

N, M = map(int, input().split())

# i번 인덱스에 원소 i의 부모노드를 기록해둔 배열. 원소가 루트노드일 경우 자기자신을 기록
parent_arr = [i for i in range(N+1)] #0~n

# x, y가 포함된 집합의 합집합 연산 메서드
def union(x, y) :
        if x==y :
             return
        # parent_arr[find(y)] = x # TS 1 : y의 루트노드를 x의 자식으로 연결함
        parent_arr[find(y)] = find(x) # TS 2 : y의 루트노드를 x의 루트노드의 자식으로 연결함


# x의 루트노드 찾는 메서드 - 버전3 재귀+경로압축 # TS 2
def find(x) :
    if parent_arr[x] != x :
        parent_arr[x] = find(parent_arr[x]) #경로 압축!!
    return parent_arr[x]

for _ in range(M) :
    oper, x, y = map(int, input().split())
    if oper == 0 :
        union(x,y)
    else :
        print(&quot;YES&quot; if find(x) == find(y) else &quot;NO&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>Union Find</category>
      <category>경로압축</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/223</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1717-%EC%A7%91%ED%95%A9%EC%9D%98-%ED%91%9C%ED%98%84-Union-Find-%EA%B2%BD%EB%A1%9C%EC%95%95%EC%B6%95-Gol5-Python#entry223comment</comments>
      <pubDate>Fri, 21 Jun 2024 18:10:59 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 16946 벽 부수고 이동하기 4 | BFS | Gol2 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-16946-%EB%B2%BD-%EB%B6%80%EC%88%98%EA%B3%A0-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-4-BFS-Gol2-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 16946 벽 부수고 이동하기 4 | BFS | Gol2 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/16946&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/16946&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;채점 되게 천천히 됨 쫄깃하네여..ㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;골드 쯤 되니까 시간제한이 문제가 되기 시작한다 ㅎ허허&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : bfs&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;벽이&amp;nbsp;존재하는&amp;nbsp;맵이&amp;nbsp;주어짐.&amp;nbsp;각&amp;nbsp;벽마다&amp;nbsp;벽이&amp;nbsp;없어졌을&amp;nbsp;때,&amp;nbsp;해당&amp;nbsp;벽&amp;nbsp;위치에&amp;nbsp;인접한&amp;nbsp;공간&amp;nbsp;크기를&amp;nbsp;구하는&amp;nbsp;문제&lt;/li&gt;
&lt;li&gt;빈 공간은 0, 벽은 1&lt;/li&gt;
&lt;li&gt;벽 위치에 해당하는 공간 크기를 10으로 나눈 나머지를 출력&lt;/li&gt;
&lt;li&gt;시간제한 : 2초&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;구상 1 (timeout)&lt;/p&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;벽(1)이 나오면 해당 벽에 인접한 공간을 bfs 돌며 공간 크기를 계산한다&lt;/li&gt;
&lt;li&gt;벽 위치에 공간 크기를 10으로 나눈 나머지를 기록한다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 2 (성공)&lt;/p&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;방문 전인 빈 공간(0)이 나오면 bfs &lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인접한 0들 그룹의 크기를 세어 저장해둔다.&lt;/li&gt;
&lt;li&gt;0 자리에는 해당되는 그룹번호를 적어둔다.&lt;/li&gt;
&lt;li&gt;그룹번호는 2부터 시작한다(0,1은 이미 사용중이므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;0으로 초기화한 정답용 맵을 만들어둔&lt;/li&gt;
&lt;li&gt;다시 한 번 전체를 돈다&lt;/li&gt;
&lt;li&gt;벽(1)이 나오면 해당 벽 상하좌우로 인접한 그룹번호들을 set에 넣어 중복을 제거하고, 그 그룹들의 크기의 합을 구한다&lt;/li&gt;
&lt;li&gt;정답용 맵에 합을 10으로 나눈 나머지를 벽 위치에 기록한다. &lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS 1 : 0%도 안뜨고 시간초과 뭘까??&lt;br /&gt;-&amp;gt; 벽마다 bfs 돌면 시간초과 난다고 함. 로직 바꿔야 됨&lt;br /&gt;대신&amp;nbsp;미리&amp;nbsp;인접한&amp;nbsp;0들을&amp;nbsp;그룹으로&amp;nbsp;묶어&amp;nbsp;크기를&amp;nbsp;세두고,&amp;nbsp;벽마다&amp;nbsp;해당&amp;nbsp;벽과&amp;nbsp;인접한&amp;nbsp;0그룹들의&amp;nbsp;크기의&amp;nbsp;합으로&amp;nbsp;계산하기&lt;/li&gt;
&lt;li&gt;TS 2 : 인접한 그룹번호가 중복인 경우 놓쳤다 set으로 처리해줬음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
from collections import deque
input = sys.stdin.readline

N, M = map(int, input().split())

board = [list(map(int, list(input().strip()))) for _ in range(N)]
dir_arr = [(0,1), (1,0), (-1,0), (0,-1)]

grp_size_arr = [0,0] #그룹번호 2부터 시작

# 1. 0그룹 크기 세기 # TS 1
for i in range(N) :
    for j in range(M) :
        #벽이거나 이미 방문했으면 패스
        if board[i][j] != 0 :
            continue

        que = deque()
        que.append((i,j))
        
        grp_size_arr.append(0)
        grp = len(grp_size_arr) - 1
        board[i][j] = grp

        #bfs - 인접 0 그룹 크기 셈
        while que :
            x, y = que.pop()

            grp_size_arr[grp] += 1

            for dir in dir_arr :
                nx, ny = x+dir[0], y+dir[1]

                if 0 &amp;lt;= nx &amp;lt; N and 0 &amp;lt;= ny &amp;lt; M and board[nx][ny] == 0 :
                    board[nx][ny] = grp
                    que.append((nx, ny))

# 2. 벽마다 인접 0그룹 크기 합 구하기
answer = [[0]*M for _ in range(N)]

for i in range(N) :
    for j in range(M) :
        if board[i][j] != 1 :
            continue
        
        grp_set = set()
        
        for dir in dir_arr :
            ni, nj = i+dir[0], j+dir[1]

            #인접 그룹번호 중복제거
            if 0 &amp;lt;= ni &amp;lt; N and 0 &amp;lt;= nj &amp;lt; M and board[ni][nj] != 1 :
                grp_set.add(board[ni][nj]) # TS 2

        #그룹 크기 합 구해서 기록
        size_sum = 1
        for grp in grp_set :
            size_sum += grp_size_arr[grp]

        answer[i][j] = size_sum%10

#출력
for line in answer :
    print(''.join(map(str, line)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/222</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-16946-%EB%B2%BD-%EB%B6%80%EC%88%98%EA%B3%A0-%EC%9D%B4%EB%8F%99%ED%95%98%EA%B8%B0-4-BFS-Gol2-Python#entry222comment</comments>
      <pubDate>Thu, 20 Jun 2024 20:53:10 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2146 다리만들기 | BFS | Gol3 Python(pypy)</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2146-%EB%8B%A4%EB%A6%AC%EB%A7%8C%EB%93%A4%EA%B8%B0-BFS-Gol3-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 2146 다리만들기 | BFS | Gol3 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2146&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2146&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pypy로만 성공한 풀이입니다.(python은 시간초과)&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : bfs&lt;br /&gt;비슷한 문제들을 푸는 중이라 접근은 어렵지 않게 했으나&lt;br /&gt;조건문 하나 잘못적어서 시간 진짜 많이 썼다 도륵&lt;br /&gt;시간초과만 나서 시간을 줄이는 방법만 고민했는데 코드에 잘못된 부분이 있었다&lt;br /&gt;코드에 논리적인 오류가 있어도 오답이 아닌 시간초과만 날 수도 있다는 것을 깨달은 시간,,,&lt;br /&gt;&lt;br /&gt;pypy로만 통과했지만 핵심적인 문제는 찾았고 더 이상 시간복잡도 개선에 시간을 쓰는 건 무리라고 판단. 넘어가자!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;땅과 땅을 연결하는 가장 짧은 다리 길이 찾기&lt;/li&gt;
&lt;li&gt;다리와 인접해있으면 연결된 것임&lt;/li&gt;
&lt;li&gt;시간제한 2초&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;전체를 2번 돌아야 할 듯?
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&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;땅(1)이 나오면 bfs돌며 땅번호를 붙이면서 visited 표시한다&lt;/li&gt;
&lt;li&gt;땅 번호는 2부터 시작(0,1이 이미 사용중이므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;땅(1)이 나오면 주변에 바다(0)가 있는지 확인
&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;바다가 있으면 바다를 거쳐 가장 가까운 다른 땅을 찾아야 함&lt;/li&gt;
&lt;li&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;다음 노드로 가기 위해 거쳐온 바다 수가 현재까지 그 노드로 갔던 최소바다수(다리길이)보다 크거나 같으면 패스함&lt;/li&gt;
&lt;li&gt;바다가 나오면 큐에 넣고 해당 노드까지 거쳐온 최소바다수 갱신&lt;/li&gt;
&lt;li&gt;땅이&amp;nbsp;나오면&amp;nbsp;같은&amp;nbsp;땅인지&amp;nbsp;확인하고&amp;nbsp;다른&amp;nbsp;땅일&amp;nbsp;경우에만&amp;nbsp;최소다리길이&amp;nbsp;갱신&lt;/li&gt;
&lt;li&gt;거쳐온 바다 수가 현재까지의 최소바다수(다리길이)보다 크거나 같으면 패스함&lt;/li&gt;
&lt;li&gt;모든 노드의 최소바다수 다시 0으로 초기화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋아 일케 풀어보자!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS 1 : 시간초과(pypy도) - 더 걸러낼 조건이 있나..?
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;min_cnt_arr 위치 bfs밖으로 변경&lt;/li&gt;
&lt;li&gt;(x)&amp;nbsp;사이드&amp;nbsp;기록해놓고&amp;nbsp;그것만&amp;nbsp;돌기?&amp;nbsp;이건&amp;nbsp;사이드인지&amp;nbsp;확인하는&amp;nbsp;위치만&amp;nbsp;달라져서&amp;nbsp;어차피&amp;nbsp;비슷할&amp;nbsp;거&amp;nbsp;같음&lt;/li&gt;
&lt;li&gt;(x) 사이드 기록해놓고 아예 각 사이드 사이의 거리를 계산(bfs x)&lt;/li&gt;
&lt;li&gt;(x) 다른 분들 코드 보니까 두번째 bfs에서 bfs의 while문이 이중for문 밖에 나와있음. 어?&lt;br /&gt;-&amp;gt; 똑같은데서 시간초과&lt;/li&gt;
&lt;li&gt;TS&amp;nbsp;2&amp;nbsp;가&amp;nbsp;문제였음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;TS 2 : 코드 오류 수정으로 시간복잡도 개선(pypy통과, python 57%)&lt;br /&gt;&amp;nbsp;-&amp;gt; 두번째 bfs의 while문 안에서 주변노드 탐색할 때 최소이동횟수 비교하는 조건문을 잘못 적었음..,,,.. cnt+1을 그냥 cnt로 적어놔서 수정함&lt;br /&gt;코드에 논리적인 오류가 있어도 오답이 아닌 시간초과만 날 수도 있다는 것을 깨달은 시간,,,&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
from collections import deque
input = sys.stdin.readline
INF = int(2e9)

N = int(input())
board = [list(map(int, input().split())) for _ in range(N)]

dir_arr = [(0,1), (1,0), (-1,0), (0,-1)]


# 1. 섬 번호 붙이기
visited = [[False]*N for _ in range(N)]

grp_idx = 1 #2부터 시작
for i in range(N) :
    for j in range(N) :
        #1이 아닐 때 (=방문 전인 땅이 아닐 때)
        if board[i][j] != 1 :
            continue

        que = deque()
        grp_idx += 1
        que.append((i,j))

        #새로운 섬 bfs
        while que :
            x, y = que.pop()

            #섬 번호 표시
            board[x][y] = grp_idx

            for dir in dir_arr :
                nx, ny = x+dir[0], y+dir[1]

                #범위를 벗어나지 않고, 방문 전이며, 1일 때
                if 0 &amp;lt;= nx &amp;lt; N and 0 &amp;lt;= ny &amp;lt;N and not visited[nx][ny] and board[nx][ny]==1 :
                    visited[nx][ny] = True
                    que.append((nx,ny))

# print(&quot;\n&quot;)
# for line in board :
#     print(*line)


# 2. 가까운 땅 찾기
min_cnt = INF
min_cnt_arr = [[INF]*N for _ in range(N)] #칸 별로 최소이동횟수 기록 # TS 1 : bfs밖으로 옮김

for i in range(N) :
    for j in range(N) :
        grp = board[i][j]
        #물은 패스함
        if grp == 0 :
            continue
        
        que = deque()
        que.append((i, j)) #x, y
        min_cnt_arr[i][j] = 0

        while que :
            x, y = que.pop()
            cnt = min_cnt_arr[x][y]

            #이미 최적이 아닌 경우 패스
            if min_cnt &amp;lt;= cnt :
                continue

            for dir in dir_arr :
                nx, ny = x+dir[0], y+dir[1]

                #좌표 내이고, 최소이동횟수보다 적게 도달했다면 값을 확인
                if 0 &amp;lt;= nx &amp;lt; N and 0 &amp;lt;= ny &amp;lt; N and cnt+1 &amp;lt; min_cnt_arr[nx][ny] : # TS 2
                    nxt = board[nx][ny]

                    #같은 땅
                    if nxt == grp :
                        continue

                    #물
                    elif nxt == 0 :
                        min_cnt_arr[nx][ny] = cnt+1
                        que.append((nx, ny))
                        continue

                    #다른 땅 도착
                    else :
                        # print(&quot;arrive : (%d, %d)-(%d, %d) %d&quot;%(i, j, nx, ny, cnt))
                        min_cnt = min(min_cnt, cnt) #다리 길이 최소값 갱신
                        continue
print(min_cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/221</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2146-%EB%8B%A4%EB%A6%AC%EB%A7%8C%EB%93%A4%EA%B8%B0-BFS-Gol3-Python#entry221comment</comments>
      <pubDate>Thu, 20 Jun 2024 17:08:13 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 13549 숨바꼭질 3 | BFS | Gol5 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13549-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88-3-BFS-Gol5-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 13549 숨바꼭질 3 | BFS | Gol5 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13549&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/13549&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : bfs&lt;br /&gt;직전에 푼 1697과 유사&lt;br /&gt;그래서 좀더 깔끔하게 풀 수 있었다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;1697 숨바꼭질 문제와 동일하나, 2배 이동을 하는 경우 드는 시간이 0이라는 점만 다르다.&lt;/li&gt;
&lt;li&gt;N에서 K로 가는 가장 빠른 횟수 찾기&lt;/li&gt;
&lt;li&gt;한 횟수에 할 수 있는 이동의 종류는 3가지
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;+1&lt;/li&gt;
&lt;li&gt;-1&lt;/li&gt;
&lt;li&gt;2배&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;0 &amp;le; N, K &amp;le; 100,000&lt;/li&gt;
&lt;li&gt;시간제한 2초&lt;/li&gt;
&lt;li&gt;메모리제한 512 MB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;매번 세가지 경우 모두 bfs 돈다&lt;/li&gt;
&lt;li&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;숫자가 이동 가능 범위를 벗어날 때 (0 &amp;le; N &amp;le; 100,000)&lt;/li&gt;
&lt;li&gt;이동 횟수가 현재까지의 최소이동횟수보다 클 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2배 이동을 하는 경우 이동횟수+1을 하지 않는다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
from collections import deque
input = sys.stdin.readline
INF = int(2e9)

N, K = map(int, input().split())

que = deque()

que.append((N, 0)) #현재위치, 이동횟수
min_cnt_arr = [INF]*100001 #모든 숫자의 최소이동횟수를 기록해둠

while que : 
    cur, cnt = que.popleft()

    #도착점까지의 최소이동횟수 또는 현재지점까지의 최소이동횟수보다 이동횟수가 큼
    if min_cnt_arr[K] &amp;lt;= cnt or min_cnt_arr[cur] &amp;lt;= cnt :
        continue

    min_cnt_arr[cur] = cnt #cur == K 인 경우의 정답 업데이트가 포함됨

    for nxt in [cur+1, cur-1, cur*2] :
        if 0 &amp;lt;= nxt &amp;lt;= 100000 :
            new_cnt = cnt if nxt == cur*2 else cnt + 1
            que.append((nxt, new_cnt))

print(min_cnt_arr[K])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/220</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13549-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88-3-BFS-Gol5-Python#entry220comment</comments>
      <pubDate>Wed, 19 Jun 2024 19:38:17 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1697 숨바꼭질 | BFS | Sil1 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1697-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88-BFS-Sil1-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1697 숨바꼭질 | BFS | Sil1 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1697&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1697&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : bfs&lt;br /&gt;메모리 초과는 처음봐서 고생했다. 문제 조건을 잘못이해했음..~ 그래도 배운 게 있으니까&lt;/li&gt;
&lt;li&gt;TIP : dict, set보다 처음부터 초기화해서 쓰더라도 list가 더 메모리 적게 쓰고 빠를 수 있다.&lt;/li&gt;
&lt;li&gt;Q : 아니 다들 visited체크를 해서 한번 방문한 노드는 다시 방문하지 않게 짜는데, 그게 왜 되지?? 더 최적으로 방문한 경로가 답이면 답이 걸러지지 않나&lt;br /&gt;A : BFS라서 그게 가능함! BFS는 넓이 우선 탐색이고, 우리는 가장 짧은 깊이의 답을 찾아야 하는 상황.&lt;br /&gt;이 때 이미 방문한 노드라는 뜻은 같은 깊이이거나 더 짧은 깊이에서 이미 그 숫자에 도달할 수 있었다는 뜻이므로&lt;br /&gt;현재 경로는 이미 최적이 아님이 입증되어있음. 그래서 확인보지 않아도 됨!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;N에서 K로 가는 가장 빠른 횟수 찾기&lt;/li&gt;
&lt;li&gt;한&amp;nbsp;횟수에&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;이동의&amp;nbsp;종류는&amp;nbsp;3가지 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;+1 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;-1 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3. 2배&lt;/li&gt;
&lt;li&gt;0&amp;nbsp;&amp;le;&amp;nbsp;N,&amp;nbsp;K&amp;nbsp;&amp;le;&amp;nbsp;100,000&amp;nbsp;&lt;br /&gt;-&amp;gt; 이게 처음 주어지는 위치의 범위제한만 해당되는 걸로 이해했는데 이동할 수 있는 범위 자체가 저걸로 제한되어있다는 의미라고 한다&lt;/li&gt;
&lt;li&gt;시간제한 2초&lt;/li&gt;
&lt;li&gt;메모리제한 128 MB&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;가장 빠른 경우를 어떻게 찾지 그냥 bfs로 다 돌아보는 건가&lt;br /&gt;각 경우마다 -1, +1, *2 이렇게 세가지 경우 다 해보는 걸로&lt;br /&gt;그런가봐 중복만 잘 처리해서 짜보자 bfs&lt;/li&gt;
&lt;li&gt;큐에서 pop&lt;/li&gt;
&lt;li&gt;답인지 확인&lt;/li&gt;
&lt;li&gt;-1, +1, *2 한 값 중 직전값이 아닌걸 큐에 넣음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;방문했던 노드까지의 최소이동횟수를 모두 기록해두고 방문했던 노드이면서 최소이동횟수보다 많이 이동해서 도착한 경우라면 패스함&lt;/li&gt;
&lt;li&gt;이동 범위 제한 : 문제 조건 0 &amp;lt; N &amp;lt;= 1000000 가 움직일 수 있는 범위 자체를 제한한 거라고 한다&lt;br /&gt;=&amp;gt; 이게 핵심이었음.. 나머지는 그냥 겸사겸사&lt;/li&gt;
&lt;li&gt;1번 기록 시 메모리가 많이 드는 dict대신 list사용. 초기화 다 해줘도 list가 더 메모리 적게 쓰고 빠르다&lt;/li&gt;
&lt;li&gt;(x)&amp;nbsp;메모리가&amp;nbsp;늘어날&amp;nbsp;부분은&amp;nbsp;que밖에&amp;nbsp;없어서&amp;nbsp;que에&amp;nbsp;append해주기&amp;nbsp;전에&amp;nbsp;조건을&amp;nbsp;검사하는&amp;nbsp;걸로&amp;nbsp;변경해봤다&amp;nbsp;&lt;br /&gt;-&amp;gt;&amp;nbsp;백준&amp;nbsp;채점에서&amp;nbsp;메모리&amp;nbsp;초과&amp;nbsp;더&amp;nbsp;빨리&amp;nbsp;떠서&amp;nbsp;안바꾸고&amp;nbsp;다시&amp;nbsp;돌려놓음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from collections import deque
import sys
input = sys.stdin.readline
INF = int(2e9)

N, K = map(int, input().split())

que = deque()
que.append((N, 0)) #현재위치, 이동횟수

# min_move = dict() #방문했던 숫자의 최소이동횟수 기록
min_move = [INF]*1000001 #방문했던 숫자의 최소이동횟수 기록

min_cnt = INF

while que :
    cur, cnt = que.popleft()

    if min_cnt &amp;lt;= cnt :
        continue
    
    #정답
    if cur == K :
        min_cnt = cnt
        continue

    # if cur in min_move.keys() and min_move[cur] &amp;lt;= cnt :
    #     continue
    # min_move[cur] = cnt 

    #이전에 방문했던 최소이동횟수보다 많으면 탐색안함
    if min_move[cur] &amp;lt;= cnt :
        continue
    min_move[cur] = cnt

    for nxt in [cur+1, cur-1, cur*2] :
        if 0 &amp;lt;= nxt &amp;lt;= 1000000 : #TS
            que.append((nxt, cnt+1))

print(min_cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/219</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1697-%EC%88%A8%EB%B0%94%EA%BC%AD%EC%A7%88-BFS-Sil1-Python#entry219comment</comments>
      <pubDate>Wed, 19 Jun 2024 18:34:25 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 2667 단지번호붙이기 | DFS,BFS | Sil1 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2667-%EB%8B%A8%EC%A7%80%EB%B2%88%ED%98%B8%EB%B6%99%EC%9D%B4%EA%B8%B0-DFSBFS-Sil1-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 2667 단지번호붙이기 | DFS,BFS | Sil1 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2667&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/2667&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : dfs (bfs도 가능할듯)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;연결된 그룹의 수와 크기를 세는 문제&lt;/li&gt;
&lt;li&gt;가로세로로 인접되어있으면 같은 그룹임&lt;/li&gt;
&lt;li&gt;그룹의 총 개수 셈&lt;/li&gt;
&lt;li&gt;각 그룹마다 몇개가 포함되어있는지 개수 셈&lt;/li&gt;
&lt;li&gt;시간제한 1초&lt;/li&gt;
&lt;li&gt;5 &amp;lt;= N &amp;lt;= 25&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;칸 전체를 돈다&lt;/li&gt;
&lt;li&gt;visited면 넘어간다&lt;/li&gt;
&lt;li&gt;방문 전이면서 1인 칸을 만나면 dfs시작&lt;/li&gt;
&lt;li&gt;그 주변에 있는 1인 칸만 스택에 넣으며 dfs돈다 두두두&lt;/li&gt;
&lt;li&gt;돌면서 1 대신 해당되는 그룹 번호로 바꿔준다 (0,1은 이미 입력데이터에서 사용중이므로 그룹 번호는 2부터 사용)&lt;/li&gt;
&lt;li&gt;dfs끝나면 칸 전체 돌기를 이어서 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;문제 구상에서 실수해서 한번 헤맸다 dfs bfs도 아직 멀었구나..~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : dfs
#TS : 문제 구상에서 실수해서 한번 헤맸다 dfs bfs도 아직 멀었구나..~

'''
[문제 해석]
연결된 그룹의 수와 크기를 세는 문제
- 가로세로로 인접되어있으면 같은 그룹임
- 각 그룹마다 번호 붙임
- 각 그룹마다 몇개가 포함되어있는지 개수 셈
- 시간제한 1초
- 5 &amp;lt;= N &amp;lt;= 25

구상
- 칸 전체를 돈다
- visited면 넘어간다
- 방문 전이면서 1인 칸을 만나면 dfs
- 주변에 있는 1인 칸만 스택에 넣으며 dfs돈다 두두두
- 돌면서 1대신 그룹 번호로 바꿔준다 (0,1은 이미 입력데이터에서 사용중이므로 그룹 번호는 2부터 사용)
- dfs끝나면 칸 전체 돌기를 이어서 진행
'''

import sys
input = sys.stdin.readline

N = int(input())
board = [list(map(int, list(input().strip()))) for _ in range(N)] #strip()으로 개행문자 제거 후 문자열을 리스트로 만들어서 다시 map(int)씀
visited = [[False]*N for _ in range(N)]

dir_arr = [(0,1), (1,0), (-1,0), (0,-1)]
grp_size_arr = [0,0] #인덱스 2부터 사용할 것임

for i in range(N) :
    for j in range(N) :
        #이미 방문했던 칸이거나 1이 아니면 패스
        if visited[i][j] or board[i][j] != 1 :
            continue
            
        #방문 전이고, 1이면 새 그룹 dfs
        stack = []

        visited[i][j] = True
        stack.append((i,j))

        #새 그룹 세팅
        grp_size_arr.append(0)
        grp_idx = len(grp_size_arr)-1
        
        #dfs
        while stack :
            cur_i, cur_j = stack.pop()

            board[cur_i][cur_j] = grp_idx
            grp_size_arr[grp_idx] += 1

            #사방 칸 확인해서 1이면 스택에 넣음
            for dir in dir_arr :
                new_i, new_j = cur_i+dir[0], cur_j+dir[1]
                #범위 벗어나는지 확인 
                if new_i &amp;lt; 0 or new_j &amp;lt; 0 or N &amp;lt;= new_i or N &amp;lt;= new_j :
                    continue
                if visited[new_i][new_j] :
                    continue
                    
                visited[new_i][new_j] = True
                if board[new_i][new_j] == 1 :
                    stack.append((new_i, new_j))


print(len(grp_size_arr)-2) #그룹번호가 2부터 시작하니까
print('\n'.join(map(str, sorted(grp_size_arr[2:]))))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>DFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/218</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-2667-%EB%8B%A8%EC%A7%80%EB%B2%88%ED%98%B8%EB%B6%99%EC%9D%B4%EA%B8%B0-DFSBFS-Sil1-Python#entry218comment</comments>
      <pubDate>Wed, 19 Jun 2024 15:15:13 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 14698 전생했더니 슬라임 연구자였던 건에 대하여 (Hard) | Gol4 | 힙 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14698-%EC%A0%84%EC%83%9D%ED%96%88%EB%8D%94%EB%8B%88-%EC%8A%AC%EB%9D%BC%EC%9E%84-%EC%97%B0%EA%B5%AC%EC%9E%90%EC%98%80%EB%8D%98-%EA%B1%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-Hard-Gol4-%ED%9E%99-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 14698 전생했더니 슬라임 연구자였던 건에 대하여 (Hard) | Gol4 | 힙 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14698&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14698&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;최소값이 어떤 경우일지 고민을 했는데 찾아보니까 그냥 매번 현재 존재하는 슬라임 중 가장 작은 것들을 쓰면 최소라고 함. 왜지?&lt;/li&gt;
&lt;/ul&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;슬라임을 2마리씩 합성하는데, 합성 시마다 두 슬라임의 에너지의 곱만큼 전기가 든다.&lt;/li&gt;
&lt;li&gt;합성&amp;nbsp;결과로&amp;nbsp;두&amp;nbsp;슬라임의&amp;nbsp;에너지의&amp;nbsp;곱만큼의&amp;nbsp;에너지를&amp;nbsp;가진&amp;nbsp;한마리의&amp;nbsp;새로운&amp;nbsp;슬라임이&amp;nbsp;됨 &lt;/li&gt;
&lt;li&gt;슬라임을 모두 합성해 한마리로 만드는 데에 드는 모든 전기의 곱의 최소값 &lt;/li&gt;
&lt;li&gt;답 : 테스트케이스마다 모든 전기의 곱의 최솟값을 1, 000, 000, 007 로 나눈 나머지 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;최소값을 어케 구할까..&lt;br /&gt;-&amp;gt; 그냥 작은것부터 곱하면 된다고 한다 ㅎㅎ..ㅎ 왜지? 초반에 무조건 작게 가야해서 그런가&lt;/li&gt;
&lt;li&gt;매번 가장 작은 슬라임 두개를 뽑아서 새 슬라임을 만들고, 바로바로 새로 생기는 슬라임을 포함해 최소값을 찾아야 하기 때문에 정렬된 상태에서 잦은 삽입삭제가 필요하다&lt;br /&gt;-&amp;gt; 힙을 써보자~~ heapq 최소힙 그대로 쓰면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 힙
# TS : 문제 잘못읽고 곱을 합으로 계산함 ㅎㅎ... 정신차리라

'''
[문제 해석]
- 슬라임을 2마리씩 합성하는데, 합성 시마다 두 슬라임의 에너지의 곱만큼 전기가 든다.
- 합성 결과로 두 슬라임의 에너지의 곱만큼의 에너지를 가진 한마리의 새로운 슬라임이 됨
- 슬라임을 모두 합성해 한마리로 만드는 데에 드는 모든 전기의 곱의 최소값
- 답 : 테스트케이스마다 모든 전기의 곱의 최솟값을 1, 000, 000, 007 로 나눈 나머지 출력

최소값을 어케 구할까..
-&amp;gt; 그냥 작은것부터 곱하면 된다고 한다 ㅎㅎ..ㅎ 왜지? 초반에 무조건 작게 가야해서 그런가
    가장 작은 슬라임 두개를 뽑아서 새 슬라임을 만들고, 바로바로 새로 생기는 슬라임을 포함해 최소값을 찾아야 하기 때문에 잦은 삽입삭제가 필요하다
    -&amp;gt; 힙을 써보자~~ heapq 최소힙 그대로 쓰면 됨
'''

import heapq
import sys
input = sys.stdin.readline

T = int(input())
for _ in range(T) :
    _ = int(input()) #슬라임 개수 안씀
    heap = list(map(int, input().split()))

    heapq.heapify(heap) #최소힙으로 만듦

    energy = 1
    while 1 &amp;lt; len(heap) :
        #가장 작은 슬라임 2개 pop
        s1 = heapq.heappop(heap)
        s2 = heapq.heappop(heap)

        result = s1*s2
        energy *= result #쓰인 모든 에너지들의 곱
        heapq.heappush(heap, result) #새로 생성된 슬라임 push
    
    print(energy%1000000007)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>힙</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/217</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14698-%EC%A0%84%EC%83%9D%ED%96%88%EB%8D%94%EB%8B%88-%EC%8A%AC%EB%9D%BC%EC%9E%84-%EC%97%B0%EA%B5%AC%EC%9E%90%EC%98%80%EB%8D%98-%EA%B1%B4%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC-Hard-Gol4-%ED%9E%99-Python#entry217comment</comments>
      <pubDate>Fri, 14 Jun 2024 16:12:24 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 13094 과제 | Gol3 | 힙 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13094-%EA%B3%BC%EC%A0%9C-Gol3-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 13094 과제 | Gol3 | 힙 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/13904&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/13904&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : 별다른 자료구조 사용않고 그냥 구현함 (112ms) &lt;br /&gt;로직 특성상 힙을 활용한 방식보다 느리다.&lt;/li&gt;
&lt;li&gt;풀이 2 : 힙 (56ms)&lt;br /&gt;이 방식이 속도가 훨씬 빠르다!&lt;br /&gt;접근 방식을 아예 다르게 해야 힙을 쓸 수 있었다. 로직을 새로 짬. 더 효율적인 로직임!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;얻을 수 있는 점수 최댓값 구하기&lt;/li&gt;
&lt;li&gt;과제는 하루에 1개만, 완료 시 점수&lt;/li&gt;
&lt;li&gt;과제 별로 점수 다름. 마감일 지나면 점수x&lt;/li&gt;
&lt;li&gt;과제 개수N &amp;lt;= 1,000 : 오 얼마 안되네?&lt;/li&gt;
&lt;li&gt;마감일까지 남은 일수d &amp;lt;= 1,000&lt;/li&gt;
&lt;li&gt;시간 제한 : 1초&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 - 힙 쓰는 ver&lt;/p&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;구현
&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;점수가 가장 큰 과제부터 힙에서 pop하며 해당 과제 마감일에 배치해본다.&lt;/li&gt;
&lt;li&gt;해당 날짜에 이미 배치가 되어있으면 배치 가능한 날짜 중 마감일 이전 가장 늦은 날짜에 배치한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 이러면 어차피 점수가 큰 순이라 최적이 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무조건 점수 큰 걸 먼저 배치해야 유리하니까.. 오.. 나는 생각 못한 방식이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구상 - 그냥 구현 ver&lt;/p&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;구현
&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;날짜를 거꾸로 돌면서, 해당 날짜에 마감일이 아직 지나지 않은 과제 중 가장 점수가 높은 과제를 해당 날짜에 배치&lt;br /&gt;이 부분 구현 방법 생각나는 거 2가지
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;점수 기준으로 정렬하고, 점수 제일 큰 과제에서부터 날짜가 지났는지 확인. 날짜가 안지난 게 나오면 바로 pop (리스트pop이라 시간많이 걸릴듯)&lt;/li&gt;
&lt;li&gt;날짜 기준으로 정렬하고, 날짜가 아직 안지난 과제까지만 돌면서 점수들 중 최대값 갱신하면서 구함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2번 방식은 최악의 경우 nlogn + d*n = d*n (n^2) 인데 최악이라도 n, d가 1000밖에 안돼서 1000^2 = 10*6 &amp;lt; 10^8 이라 괜찮을 거 같은... 해볼까&lt;br /&gt;&amp;nbsp;-&amp;gt; 이걸로 풀었는데 성공했다. 힙 쓰는 풀이도 찾아봐야지&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;인덱스 -1로 초기화할 때 나중에 예외처리 항상 까먹지 않게 주의하기..~~~&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 힙 (빠름)&lt;/h3&gt;
&lt;pre id=&quot;code_1718276268162&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : 힙 (56ms)
#이 방식이 속도가 훨씬 빠르다!
#접근 방식을 아예 다르게 해야 힙을 쓸 수 있었다. 로직을 새로 짬. 더 효율적인 로직임!

'''
[문제 해석]
- 얻을 수 있는 점수 최댓값 구하기
- 과제는 하루에 1개만, 완료 시 점수
- 과제 별로 점수 다름. 마감일 지나면 점수x
- 과제 개수N &amp;lt;= 1,000 : 오 얼마 안되네?
- 마감일까지 남은 일수d &amp;lt;= 1,000
- 시간 제한 : 1초

구상 (힙 쓰는 ver)
- 과제 점수 기준으로 최대힙을 만든다.
- 점수가 가장 큰 과제부터 힙에서 pop하며 해당 과제 마감일에 배치해본다.
- 해당 날짜에 이미 배치가 되어있으면 배치 가능한 날짜 중 마감일 이전 가장 늦은 날짜에 배치한다. 
이러면 어차피 점수가 큰 순이라 최적이 된다! 무조건 점수 큰 걸 먼저 배치해야 유리하니까.. 오.. 나는 생각 못한 방식이다
'''

import heapq
import sys
input = sys.stdin.readline

N = int(input())
works = []
max_day = 0
for _ in range(N) :
    day, score = map(int, input().split())
    works.append((-1*score, day)) #heapq를 최대힙으로 쓰기위해 음수로 저장

    max_day = max(max_day, day) #가장 늦은 마감일 저장

heapq.heapify(works)

score_arr = [0]*(max_day+1)
while works :
    score, deadline = heapq.heappop(works) #가장 점수가 높은 과제
    score *= -1 
    # print(deadline, score)
    for day in range(deadline, 0, -1) :
        # print(day)
        if score_arr[day] == 0 :
            # print(&quot;set&quot;)
            score_arr[day] = score
            break

# print(score_arr)
print(sum(score_arr))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 딱히 자료구조 쓰지 않고 그냥 구현 (느림)&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 별다른 자료구조 사용않고 그냥 구현함 (112ms)
#로직 특성상 힙을 활용한 방식보다 느리다.
# TS : 인덱스 -1로 초기화할 때 나중에 예외처리 항상 까먹지 않게 주의하기..~~~

'''
[문제 해석]
- 얻을 수 있는 점수 최댓값 구하기
- 과제는 하루에 1개만, 완료 시 점수
- 과제 별로 점수 다름. 마감일 지나면 점수x
- 과제 개수N &amp;lt;= 1,000 : 오 얼마 안되네?
- 마감일까지 남은 일수d &amp;lt;= 1,000
- 시간 제한 : 1초

구상
- 과제를 마지막날부터 거꾸로 돌면서 배치하면 될 것 같다.
- 구현
    - 과제를 점수가 큰것부터 정렬
    - 날짜를 거꾸로 돌면서, 해당 날짜에 마감일이 아직 지나지 않은 과제 중 가장 점수가 높은 과제를 해당 날짜에 배치
        - 이 부분 구현 방법 생각나는 거 2가지
            1. 점수 기준으로 정렬하고, 점수 제일 큰 과제에서부터 날짜가 지났는지 확인. 날짜가 안지난 게 나오면 바로 pop (리스트pop이라 시간많이 걸릴듯)
            2. 날짜 기준으로 정렬하고, 날짜가 아직 안지난 과제까지만 돌면서 점수들 중 최대값 갱신하면서 구함
                - 최악의 경우 nlogn + d*n = d*n (n^2) 인데 최악이라도 n, d가 1000밖에 안돼서 1000^2 = 10*6 &amp;lt; 10^8 이라 괜찮을 거 같은... 해볼까
                -&amp;gt; 이걸로 풀었는데 성공했다. 힙 쓰는 풀이도 찾아봐야지
'''


import sys
input = sys.stdin.readline

N = int(input())
works = [tuple(map(int, input().split())) for _ in range(N)]
done = [False]*N

works.sort(reverse=True) # 튜플 첫번째 값인 날짜가 큰 기준으로 정렬됨

score_sum = 0
for day in range(works[0][0], 0, -1) : #가장 큰 마감일부터 날짜를 돈다

    score_max_idx = -1
    score_max = 0
    for i in range(len(works)) :
        deadline, score = works[i]

        #마감일 지난 과제가 나왔으면 이번 날짜 탐색 중지
        if deadline &amp;lt; day :
            break
        #이미 한 과제면 넘어감
        if done[i] :
            continue
        
        #점수 더 높은 과제가 나왔으면 갱신. 이때 점수가 동일해도 마감일이 늦은 걸 골라야 유리하므로 등호 미포함
        if score_max &amp;lt; score :
            score_max_idx = i
            score_max = score


    #아무 과제도 선택되지 않았으면 넘어감 # TS
    if score_max_idx == -1 :
        continue

    #최종적으로 이 날짜에 선택된 과제 완료 처리, 점수 증가
    done[score_max_idx] = True
    score_sum += score_max

print(score_sum)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>힙</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/216</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-13094-%EA%B3%BC%EC%A0%9C-Gol3-Python#entry216comment</comments>
      <pubDate>Thu, 13 Jun 2024 20:01:21 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 11000 강의실배정 | Gol5 | 힙? Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11000-%EA%B0%95%EC%9D%98%EC%8B%A4%EB%B0%B0%EC%A0%95-Gol5-%ED%9E%99-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 11000 강의실배정 | Gol5 | 힙? Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11000&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11000&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : 리스트 (시간초과, pypy만 통과)&lt;/li&gt;
&lt;li&gt;풀이 2 : 힙 (시간초과, pypy만 통과)&lt;br /&gt;**풀이1, 2 는 동일 로직인데 사용한 자료구조만 다른 코드&lt;/li&gt;
&lt;li&gt;풀이 3 : 힙
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;힙을 써서 통과된 게 아니라 로직을 더 효율적으로 개선해서 통과된 것임다&lt;br /&gt;동일 로직으로 리스트로도 가능할지도?&lt;br /&gt;그리고 다른 코드들 찾아보니까 더더 효율적인 로직이 있었다. 논리적으로 이해하기에는 내 코드가 쉽고, 대신 덜 효율적&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;시간대가 겹치는 수업들을 최소한의 강의실에 배치하라&lt;/li&gt;
&lt;li&gt;답 : 강의실 최소개수&lt;/li&gt;
&lt;li&gt;시간제한 : 1초&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;효율 개선한 로직
&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;강의 시작 시각을 돌며 해당 시작 시각 전에 끝난 강의가 있는지 체크하여 빈강의실+1&lt;/li&gt;
&lt;li&gt;빈 강의실이 있다면 그걸 사용하고, 없다면 새로운 강의실+1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;시간을 흐르게 하면서 각 시간에 빈 강의실이 있다면 그걸 사용하고, 없다면 새로운 강의실을 추가하자&lt;/li&gt;
&lt;li&gt;수업이&amp;nbsp;끝나면&amp;nbsp;빈강의실+1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다른 풀이들 보니까 힙 안쓰고도 구현 가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 힙, 효율 개선된 로직&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v3 : 힙
# TS : 시간이 등장하는 문제에서 꼭 시각을 하나하나 돌려고 하지 말자. 유의미한 시각만 돌게 생각해볼 것

'''
[문제 해석]
- 시간대가 겹치는 수업들을 최소한의 강의실에 배치하라
- 답 : 강의실 최소개수
- 시간제한 : 1초



구상
수업 시작시각, 종료시각을 시간순으로 정렬하고
강의 시작 시각을 돌며 해당 시작 시각 전에 끝난 강의가 있는지 체크하여 빈강의실+1
빈 강의실이 있다면 그걸 사용하고, 없다면 새로운 강의실+1
왜 힙이지? - 오 풀다보니까 알겠음..

근데 다른 풀이들 보니까 힙 안쓰고도 구현 가능함
'''

import heapq
import sys
input = sys.stdin.readline

N = int(input())

start = []
end = []

for _ in range(N) :
    s, e = map(int, input().split())
    start.append(s)
    end.append(e)

#시간순 정렬 - heapq사용해서 최소힙으로 만듦
heapq.heapify(start)
heapq.heapify(end)

#현재 사용중인 &amp;amp; 비어있는 강의실 개수 저장 변수
using = 0
empty = 0

while start : #더 시작할 강의가 남아있을 때까지 반복
    #강의 시작 시각
    t = heapq.heappop(start)

    #시작시각 전에 끝난 강의가 있다면 강의실 반납
    while end and end[0] &amp;lt;= t :
        heapq.heappop(end)
        using -= 1
        empty += 1
    
    #강의실 배정
    if empty :
        empty -= 1
    using += 1

#강의실 최소값 = 빈 강의실 수 + 현재 사용중인(끝나면 비게 될) 강의실 수
print(using + empty)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 힙, 비효율적 로직&lt;/h3&gt;
&lt;pre id=&quot;code_1718268057075&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : 힙


'''
[문제 해석]
- 시간대가 겹치는 수업들을 최소한의 강의실에 배치하라
- 답 : 강의실 최소개수
- 시간제한 : 1초



구상
일단 수업을 무조건 배치는 해야 됨
수업 시작시간, 종료시간을 시간순으로 정렬하고
시간을 흐르게 하면서 각 시간에 빈 강의실이 있다면 그걸 사용하고, 없다면 새로운 강의실을 추가하자
수업이 끝나면 빈강의실+1
왜 힙이지? - 오 풀다보니까 알겠음..

=&amp;gt; heapq써도 시간초과
    -&amp;gt; 로직을 더 줄일 수 있을 듯? for문을 시간별로 안돌고
'''

import heapq
import sys
input = sys.stdin.readline

N = int(input())

start = []
end = []

for _ in range(N) :
    s, e = map(int, input().split())
    start.append(s)
    end.append(e)

#시간순 정렬 - heapq사용해서 최소힙으로 만듦
heapq.heapify(start)
heapq.heapify(end)

#현재 사용중인 &amp;amp; 비어있는 강의실 개수 저장 변수
using = 0
empty = 0

#시간을 돌면서 강의실 배정
for t in range(max(start)+1) : #제일 늦게 시작하는 강의의 시작시각까지 반복
    #남은 강의 중 제일 먼저 끝나는 강의가 현재시간과 같으면 계속 확인
    while end and end[0] == t : 
        heapq.heappop(end)
        using -= 1
        empty += 1

    #남은 강의 중 제일 먼저 시작하는 강의가 현재시간과 같으면 계속 확인
    while start and start[0] == t : 
        heapq.heappop(start)
        #빈강의실 있으면 그거줌
        if empty != 0 : 
            empty -= 1
        using += 1

#강의실 최소값 = 빈 강의실 수 + 현재 사용중인(끝나면 비게 될) 강의실 수
print(using + empty)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 리스트, 비효율적 로직&lt;/h3&gt;
&lt;pre id=&quot;code_1718268132582&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# timeout : 리스트
#           python시간초과, pypy로는 통과!
#           논리는 맞았으나 리스트로는 시간초과가 난다. 힙으로도 해보자


'''
[문제 해석]
- 시간대가 겹치는 수업들을 최소한의 강의실에 배치하라
- 답 : 강의실 최소개수
- 시간제한 : 1초



구상
일단 수업을 무조건 배치는 해야 됨
수업 시작시간, 종료시간을 시간순으로 정렬하고
시간을 흐르게 하면서 각 시간에 빈 강의실이 있다면 그걸 사용하고, 없다면 새로운 강의실을 추가하자
수업이 끝나면 빈강의실+1
왜 힙이지? - 오 풀다보니까 알겠음..
'''

import sys
input = sys.stdin.readline

N = int(input())

start = []
end = []

for _ in range(N) :
    s, e = map(int, input().split())
    start.append(s)
    end.append(e)

#시간순 정렬 - 제일 먼저 시작하는 값이 제일 마지막에 위치. pop하기 쉽게
start.sort(reverse=True) #아 힙쓰면 항상 최솟값뽑을때 heappop으로 해서 편하긴 하겠네..
end.sort(reverse=True)

#현재 사용중인 &amp;amp; 비어있는 강의실 개수 저장 변수
using = 0
empty = 0

#시간을 돌면서 강의실 배정
for t in range(start[0]+1) : #제일 늦게 시작하는 강의의 시작시각까지 반복
    #남은 강의 중 제일 먼저 끝나는 강의가 현재시간과 같으면 계속 확인
    while end and end[-1] == t : 
        end.pop()
        using -= 1
        empty += 1

    #남은 강의 중 제일 먼저 시작하는 강의가 현재시간과 같으면 계속 확인
    while start and start[-1] == t : 
        start.pop()
        #빈강의실 있으면 그거줌
        if empty != 0 : 
            empty -= 1
        using += 1

#강의실 최소값 = 빈 강의실 수 + 현재 사용중인(끝나면 비게 될) 강의실 수
print(using + empty)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/215</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11000-%EA%B0%95%EC%9D%98%EC%8B%A4%EB%B0%B0%EC%A0%95-Gol5-%ED%9E%99-Python#entry215comment</comments>
      <pubDate>Thu, 13 Jun 2024 17:44:36 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 19638 센티와 마법의 뿅망치 | Sil1 | 힙 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-19638-%EC%84%BC%ED%8B%B0%EC%99%80-%EB%A7%88%EB%B2%95%EC%9D%98-%EB%BF%85%EB%A7%9D%EC%B9%98-Sil1-%ED%9E%99-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 19638 센티와 마법의 뿅망치 | Sil1 | 힙 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19638&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/19638&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : 힙&lt;br /&gt;최대힙 음수로 써줘야 하는거 꽤나 귀찮네.. 메서드 만들어서 쓸 걸 그랬나&lt;/li&gt;
&lt;li&gt;풀이 2 (시간초과) : heapq 안쓰고도 풀 수 있나 싶어서 그냥 리스트랑 sort써서도 해봤다.&lt;br /&gt;-&amp;gt; heappush 대신 값을 넣을 때마다 다시 정렬해준다.&lt;br /&gt;-&amp;gt; 예제는 통과했으나 시간초과!&lt;br /&gt;heapq는 값을 정렬된 상태로 유지하며 계속 넣었다뺐다 해야할 때 유용할 것임을 느꼈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;마법 뿅망치 : 키/2 가 됨. 키가 1일경우 영향x&lt;br /&gt;문제에 안적혀있었는데, 키가 홀수일 경우 2로 나눈 몫으로 하면됨&lt;/li&gt;
&lt;li&gt;항상 가장 키가 큰 사람 중 한명을 때림&lt;/li&gt;
&lt;li&gt;때리는 횟수T 제한있음&lt;/li&gt;
&lt;li&gt;답 : 모든 거인의 키가 센티의 키H보다 작게 할 수 있는가?
&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;할 수 없다면 가능한 횟수만큼 다 때린 후에 가장 큰 거인의 키는?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시간제한 : 1초&lt;/li&gt;
&lt;li&gt;거인의 수 N : N &amp;lt;= 10^5&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;최대힙이 필요하므로 heapq에 음수로 저장할 것&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 1 - 힙&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 힙
# TS : 로직 돌리기 전과 후 모두 조건을 만족하는지 확인해줘야 하는 경우에 처리 주의하기!

'''
[문제 해석]
- 마법 뿅망치 : 키/2 가 됨. 키가 1일경우 영향x
- 항상 가장 키가 큰 사람 중 한명을 때림
- 때리는 횟수T 제한있음
- 답 : 모든 거인의 키가 센티의 키H보다 작게 할 수 있는가? 
    할 수 있다면 뿅망치 사용 최소횟수는?
    할 수 없다면 가능한 횟수만큼 다 때린 후에 가장 큰 거인의 키는?
- 시간제한 : 1초
- 거인의 수 N : N &amp;lt;= 10^5

- 최대힙이 필요하므로 heapq에 음수로 저장할 것
+ 문제에 안적혀있었는데, 키가 홀수일 경우 2로 나눈 몫으로 하면됨
'''

import heapq
import sys
input = sys.stdin.readline
INF = int(2e9)

N, H, T = map(int, input().split())

heap = [-1*int(input()) for _ in range(N)]
heapq.heapify(heap)

#TS : 예외처리 - 이미 조건 만족함
if -1*heap[0] &amp;lt; H :
    print(&quot;YES\n0&quot;)
    sys.exit() #바로 실행 종료


#뿅망치 때리고 H보다 작아졌는지 확인
cnt = INF
for i in range(1, T+1) :
    h = -1*heapq.heappop(heap)
    new_h = h
    if h != 1 :
        new_h = h//2
    heapq.heappush(heap, -1*new_h)
    
    if -1*heap[0] &amp;lt; H :
        cnt = i
        break

#모두 작아지게 하기 실패 시
if cnt == INF :
    print(&quot;NO\n%d&quot;%(-1*heap[0]))
#성공 시
else : 
    print(&quot;YES\n%d&quot;%cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 2 - 리스트와 sort 사용 (시간초과)&lt;/h3&gt;
&lt;pre id=&quot;code_1718104308042&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# timeout : 정렬로 풀어보기~ 
#   -&amp;gt; heappush 대신 값을 넣을 때마다 다시 정렬해준다.
#   -&amp;gt; 예제는 통과했으나 시간초과!
#       heapq는 값을 정렬된 상태로 유지하며 계속 넣었다뺐다 해야할 때 유용할 것임을 느꼈다.


'''
[문제 해석]
- 마법 뿅망치 : 키/2 가 됨. 키가 1일경우 영향x
- 항상 가장 키가 큰 사람 중 한명을 때림
- 때리는 횟수T 제한있음
- 답 : 모든 거인의 키가 센티의 키H보다 작게 할 수 있는가? 
    할 수 있다면 뿅망치 사용 최소횟수는?
    할 수 없다면 가능한 횟수만큼 다 때린 후에 가장 큰 거인의 키는?
- 시간제한 : 1초
- 거인의 수 N : N &amp;lt;= 10^5

- 최대힙이 필요하므로 heapq에 음수로 저장할 것
+ 문제에 안적혀있었는데, 키가 홀수일 경우 2로 나눈 몫으로 하면됨
'''

from collections import deque
import sys
input = sys.stdin.readline
INF = int(2e9)

N, H, T = map(int, input().split())

height = [int(input()) for _ in range(N)]
height.sort() #height[-1] 이 가장 큰 수

#예외처리 - 이미 조건 만족함
if height[-1] &amp;lt; H :
    print(&quot;YES\n0&quot;)
    sys.exit() #바로 실행 종료


#뿅망치 때리고 H보다 작아졌는지 확인
cnt = INF
for i in range(1, T+1) :
    h = height.pop()
    new_h = h
    if h != 1 :
        new_h = h//2
    height.append(new_h)
    height.sort() #재정렬
    
    if height[-1] &amp;lt; H :
        cnt = i
        break

#모두 작아지게 하기 실패 시
if cnt == INF :
    print(&quot;NO\n%d&quot;%(height[-1]))
#성공 시
else : 
    print(&quot;YES\n%d&quot;%cnt)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>힙</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/213</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-19638-%EC%84%BC%ED%8B%B0%EC%99%80-%EB%A7%88%EB%B2%95%EC%9D%98-%EB%BF%85%EB%A7%9D%EC%B9%98-Sil1-%ED%9E%99-Python#entry213comment</comments>
      <pubDate>Tue, 11 Jun 2024 19:52:16 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 11279 최대 힙 | Sil2 | 힙 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11279-%EC%B5%9C%EB%8C%80-%ED%9E%99-Sil2-%ED%9E%99-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 11279 최대 힙 | Sil2 | 힙 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11279&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11279&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;br /&gt;파이썬은 그냥 heapq쓰면 되는 문제&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;최대힙이므로 heapq쓸 때 값을 음수로 저장&lt;/li&gt;
&lt;li&gt;입력된 자연수를 힙에 push&lt;/li&gt;
&lt;li&gt;입력이 0이면 가장 큰 값을 pop (출력하고 그 값을 배열에서 제거)&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 힙(최대힙)
#파이썬은 그냥 heapq쓰면 되는 문제

'''
- 최대힙이므로 heapq쓸 때 값을 음수로 저장
- 입력된 자연수를 힙에 push
- 입력이 0이면 가장 큰 값을 pop (출력하고 그 값을 배열에서 제거)
'''

import heapq
import sys
input = sys.stdin.readline

N = int(input())
heap = []

for _ in range(N) :
    oper = int(input())
    if oper == 0 :
        if len(heap) == 0 :
            print(0)
        else :
            print(-1*heapq.heappop(heap))
    else :
        heapq.heappush(heap, -1*oper)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>힙</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/212</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11279-%EC%B5%9C%EB%8C%80-%ED%9E%99-Sil2-%ED%9E%99-Python#entry212comment</comments>
      <pubDate>Tue, 11 Jun 2024 18:20:29 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 14675 단절점과 단절선 | Sil1 | 트리ver Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14675-%EB%8B%A8%EC%A0%88%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%88%EC%84%A0-Sil1-%ED%8A%B8%EB%A6%AC-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 14675 단절점과 단절선 | Sil1 | 트리 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14675&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14675&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;br /&gt;단절점, 단절선 개념&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진 트리에서 각 노드, 간선이 단절점, 단절선인지 묻는 질문에 답 출력&lt;/li&gt;
&lt;li&gt;질의 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;t=1 일 때 : k번 정점이 단절점인가?&lt;/li&gt;
&lt;li&gt;t=2 일 때 : k번째로 입력받았던 간선이 단절선인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;단절점, 단절선 : 특정 노드 또는 선을 제거했을 때 해당 그래프가 2개 이상으로 분리된다면 해당 노드 또는 선을 단절점 또는 단절선이라한다.&lt;br /&gt;DST(DFS Spanning Tree)를 활용해 단절점, 단절선을 판단할 수 있다.&lt;/li&gt;
&lt;li&gt;트리 : 모든 노드가 연결되어있는, 사이클 없는 그래프&lt;br /&gt;-&amp;gt; 사이클이 없고 모든 노드가 연결되어있으므로,
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단절점 : 간선이 2개 이상 연결되어있는 노드는 모두 단절점임 (노드를 제거했을 때 간선이 1개면 해당 노드만 사라지지만 2개이상이면 이로인해 연결끊기는 노드 존재)&lt;/li&gt;
&lt;li&gt;단절선 : 모든 간선이 단절선임 (사이클없이 모두 연결되어있으므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&amp;nbsp;-&amp;gt; 결국 이 문제의 포인트는 트리의 특성을 이해하는 거였다,, 단절점을 곁들인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;트리 입력받고 간선 개수에 따라 단절점 여부 판단 (dfs도 필요없구나^.^!)&lt;/li&gt;
&lt;li&gt;간선은 모두 단절선&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v2 : 그래프(트리만 입력되는 경우)
#단절점, 단절선 개념


'''
[문제해석]
- 주어진 트리에서 각 노드, 간선이 단절점, 단절선인지 묻는 질문에 답 출력
- 질의 내용
    - t=1 일 때 : k번 정점이 단절점인가?
    - t=2 일 때 : k번째로 입력받았던 간선이 단절선인가?

개념
- 단절점, 단절선 : 특정 노드 또는 선을 제거했을 때 해당 그래프가 2개 이상으로 분리된다면 해당 노드 또는 선을 단절점 또는 단절선이라한다.
    DST(DFS Spanning Tree)를 활용해 단절점, 단절선을 판단할 수 있다.
- 트리 : 모든 노드가 연결되어있는, 사이클 없는 그래프
    -&amp;gt; 사이클이 없고 모든 노드가 연결되어있으므로,
        단절점 : 간선이 2개 이상 연결되어있는 노드는 모두 단절점임 (노드를 제거했을 때 간선이 1개면 해당 노드만 사라지지만 2개이상이면 이로인해 연결끊기는 노드 존재)
        단절선 : 모든 간선이 단절선임 (사이클없이 모두 연결되어있으므로)
    -&amp;gt; 결국 이 문제의 포인트는 트리의 특성을 이해하는 거였다,, 단절점을 곁들인

        
구상
- 트리 입력받고 간선 개수에 따라 단절점 여부 판단 (dfs도 필요없구나^.^!)
- 간선은 모두 단절선
'''

from collections import defaultdict
import sys
input = sys.stdin.readline

graph = defaultdict(list)
N = int(input())

#간선 입력받기
for _ in range(N-1) :
    n1, n2 = map(int, input().split())
    graph[n1].append(n2)
    graph[n2].append(n1)

# 문제 입력받아서 답 출력
for _ in range(int(input())) :
    t, k = map(int, input().split())
    if t == 1 :
        print(&quot;yes&quot; if 1 &amp;lt; len(graph[k]) else &quot;no&quot;) #간선 개수로 단절점인지 판단
    elif t == 2 :
        print(&quot;yes&quot;) #모든 간선이 단절선&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>그래프</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>트리</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/211</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14675-%EB%8B%A8%EC%A0%88%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%88%EC%84%A0-Sil1-%ED%8A%B8%EB%A6%AC-Python#entry211comment</comments>
      <pubDate>Tue, 11 Jun 2024 17:46:25 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 1991 트리 순회 | Sil1 | 그래프,트리 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1991-%ED%8A%B8%EB%A6%AC-%EC%88%9C%ED%9A%8C-Sil1-%EA%B7%B8%EB%9E%98%ED%94%84%ED%8A%B8%EB%A6%AC-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 1991 &lt;span data-token-index=&quot;1&quot;&gt;트리 순회&lt;/span&gt; | Sil1 | 그래프,트리 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/1991&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/1991&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;유형 : 트리 (dfs)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진 트리를 전위, 중위, 후위 순회한 순으로 각각 출력&lt;/li&gt;
&lt;li&gt;항상 A가 루트노드임&lt;/li&gt;
&lt;li&gt;시간제한 : 2초&lt;/li&gt;
&lt;li&gt;순회 방식
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전위 순회 : 루트 &amp;rarr; 왼쪽 &amp;rarr; 오른쪽 자식 순으로 방문&lt;/li&gt;
&lt;li&gt;중위 순회 : 왼쪽 &amp;rarr; 루트 &amp;rarr; 오른쪽&lt;/li&gt;
&lt;li&gt;후위 순회 : 왼쪽 &amp;rarr; 오른쪽 &amp;rarr; 루트&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;일단 트리를 입력받고.. 루트가 누군지 알려주니까 거기서부터 찾아가자&lt;/li&gt;
&lt;li&gt;세가지 순회 모두 현재 노드 기준으로 우선순위 먼저인 것부터 재귀호출 돌게 하고, 루트노드를 탐색하는 순서에 현재노드를 답에 추가해주면 된다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 트리 (dfs)

'''
[문제 해석]
- 주어진 트리를 전위, 중위, 후위 순회한 순으로 각각 출력
- 항상 A가 루트노드임
- 시간제한 : 2초

순회 방식
- 전위 순회 : 루트 &amp;rarr; 왼쪽 &amp;rarr; 오른쪽 자식 순으로 방문
- 중위 순회 : 왼쪽 &amp;rarr; 루트 &amp;rarr; 오른쪽
- 후위 순회 : 왼쪽 &amp;rarr; 오른쪽 &amp;rarr; 루트

구상
일단 트리를 입력받고.. 루트가 누군지 알려주니까 거기서부터 찾아가자
세가지 순회 모두 현재 노드 기준으로 우선순위 먼저인 것부터 재귀호출 돌게 하고, 루트노드를 탐색하는 순서에 현재노드를 답에 추가해주면 된다.
'''

import sys
input = sys.stdin.readline

N = int(input()) #노드 개수
tree = dict()
for _ in range(N) :
    root, l, r = input().split()
    tree[root] = (l, r)
    
start = 'A'
result = []

#해당 노드가 존재하는지 반환해주는 메서드
def is_exist(node) : 
    return node != '.'

# 전위 순회
def preorder_dfs(cur) :
    global result
    left, right = tree[cur]
    result.append(cur)
    if is_exist(left) :
        preorder_dfs(left)
    if is_exist(right) :
        preorder_dfs(right)

preorder_dfs(start)
result.append('\n')

# 중위 순회
def inorder_dfs(cur) :
    global result
    left, right = tree[cur]
    if is_exist(left) :
        inorder_dfs(left)
    result.append(cur)
    if is_exist(right) :
        inorder_dfs(right)

inorder_dfs(start)
result.append('\n')

# 후위 순회
def postorder_dfs(cur) :
    global result
    left, right = tree[cur]
    if is_exist(left) :
        postorder_dfs(left)
    if is_exist(right) :
        postorder_dfs(right)
    result.append(cur)

postorder_dfs(start)

#출력
print(''.join(result))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>그래프</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>트리</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/210</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-1991-%ED%8A%B8%EB%A6%AC-%EC%88%9C%ED%9A%8C-Sil1-%EA%B7%B8%EB%9E%98%ED%94%84%ED%8A%B8%EB%A6%AC-Python#entry210comment</comments>
      <pubDate>Mon, 10 Jun 2024 19:34:25 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 14675 단절점과 단절선 | Sil1 | 그래프ver Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14675-%EB%8B%A8%EC%A0%88%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%88%EC%84%A0-Sil1-%EA%B7%B8%EB%9E%98%ED%94%84%ED%8A%B8%EB%A6%AC-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 14675 단절점과 단절선 | Sil1 | 그래프,트리 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/14675&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/14675&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;구상&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;**주의**&lt;br /&gt;이 문제는 입력으로 트리만 들어오는데, &lt;/b&gt;&lt;br /&gt;&lt;b&gt;이 글은 트리가 아닌 그래프가 입력되어도 풀 수 있게 짠 풀이입니다.&lt;/b&gt;&lt;br /&gt;&lt;b&gt;트리만 입력되는 경우에는 더 쉽게 풀 수 있으니 이렇게 안풀어도 됨!!!!!&lt;/b&gt;&lt;br /&gt;&lt;b&gt;더 쉬운 풀이는 따로 글 쓰겠습니다&lt;/b&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유형 : 그래프(트리)&lt;br /&gt;단절점, 단절선 개념 &lt;br /&gt;단절점 단절선 찾기 로직 이해하느라 한참걸렸다.. 나 이런거에 약해&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 아니 이문제는 이렇게 안풀어도 되는 문제입니다...ㅎㅎ... 트리만 주어지는 문제인데 나는 트리 아닌 그래프가 와도 되는 풀이로 풀고있던 거였다 어쩐지 어렵드라;;;; 트리만 입력될 때 쓸 수 있는 풀이로도 다시 풀어서 올릴게여 허허&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진 트리에서 각 노드, 간선이 단절점, 단절선인지 묻는 질문에 답 출력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;질의 내용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;t=1 일 때 : k번 정점이 단절점인가?&lt;/li&gt;
&lt;li&gt;t=2 일 때 : k번째로 입력받았던 간선이 단절선인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;트리 입력받고..&lt;/li&gt;
&lt;li&gt;노드마다 직접 끊어보고 그래프 분리되었는지 확인하기? &lt;br /&gt;-&amp;gt; 가능은 한데 오래걸림 O(v(v+e)) 로 v^2 이상&lt;/li&gt;
&lt;li&gt;특정 노드, 간선이 단절점, 단절선인지 판단하는 법 &lt;br /&gt;-&amp;gt; 찾아보니까 방법이 따로 있음. O(v+e). 그래프 입력받고나서 미리 판단해둔 후에 질의에 따라 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;단절점, 단절선 : &lt;br /&gt;특정 노드(간선)을 제거했을 때 그래프가 2개 이상으로 분리된다면 해당 노드(간선)을 단절점(단절선)이라한다.&lt;br /&gt;DST(DFS Spanning Tree)를 활용해 단절점, 단절선을 판단할 수 있다.&lt;/li&gt;
&lt;li&gt;단절점 판단법 O(v+e) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;dfs&amp;nbsp;로&amp;nbsp;내려가며&amp;nbsp;방문번호&amp;nbsp;매김 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;돌아나올&amp;nbsp;때,&amp;nbsp;내&amp;nbsp;short&amp;nbsp;값과&amp;nbsp;부모노드를&amp;nbsp;포함한&amp;nbsp;인접노드들의&amp;nbsp;방문번호&amp;nbsp;중&amp;nbsp;최솟값을&amp;nbsp;short값으로&amp;nbsp;기록하고,&amp;nbsp;다음&amp;nbsp;노드에&amp;nbsp;전파하면서&amp;nbsp;돌아나옴 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;전파받은&amp;nbsp;short값이&amp;nbsp;현재&amp;nbsp;short값보다&amp;nbsp;작으면&amp;nbsp;그걸로&amp;nbsp;갱신.&amp;nbsp;만약&amp;nbsp;현재&amp;nbsp;방문번호와&amp;nbsp;전파받은&amp;nbsp;short값이&amp;nbsp;같으면&amp;nbsp;해당&amp;nbsp;노드는&amp;nbsp;단절점! &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;+) 루트 노드는 자식이 2개 이상이면 무조건 단절점임. 따로 처리해줄 것&lt;/li&gt;
&lt;li&gt;단절선 판단법 (단절점 판단법과 유사함. 달라지는 부분은 &quot;&quot;로 감쌌음!) O(v+e) &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;dfs&amp;nbsp;로&amp;nbsp;내려가며&amp;nbsp;방문번호&amp;nbsp;매김 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2.&amp;nbsp;돌아나올&amp;nbsp;때,&amp;nbsp;내&amp;nbsp;short&amp;nbsp;값과&amp;nbsp;&quot;부모노드를&amp;nbsp;제외한&quot;&amp;nbsp;인접노드들의&amp;nbsp;방문번호&amp;nbsp;중&amp;nbsp;최솟값을&amp;nbsp;short값으로&amp;nbsp;기록하고,&amp;nbsp;다음&amp;nbsp;노드에&amp;nbsp;전파하면서&amp;nbsp;돌아나옴 &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3.&amp;nbsp;전파받은&amp;nbsp;short값이&amp;nbsp;현재&amp;nbsp;short값보다&amp;nbsp;작으면&amp;nbsp;그걸로&amp;nbsp;갱신.&amp;nbsp;만약&amp;nbsp;&quot;현재&amp;nbsp;방문번호보다&amp;nbsp;전파받은&amp;nbsp;short값이&amp;nbsp;크면&quot;&amp;nbsp;그&amp;nbsp;사이&amp;nbsp;간선은&amp;nbsp;단절선! &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;2차원 배열 오랜만에 썼더니 선언하는 법 까먹음;;;; 꾸준히 풀어라 마&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 그래프(트리)
#단절점, 단절선 개념
#단절점 단절선 찾기 로직 이해하느라 한참걸렸다.. 나 이런거에 약해


'''
[문제해석]
- 주어진 트리에서 각 노드, 간선이 단절점, 단절선인지 묻는 질문에 답 출력
- 질의 내용
    - t=1 일 때 : k번 정점이 단절점인가?
    - t=2 일 때 : k번째로 입력받았던 간선이 단절선인가?

구상
- 트리 입력받고..
- 노드마다 직접 끊어보고 그래프 분리되었는지 확인하기? -&amp;gt; 가능은 한데 오래걸림 O(v(v+e))
- 특정 노드, 간선이 단절점, 단절선인지 판단하는 법 -&amp;gt; 찾아보니까 방법이 따로 있음. 그래프 입력받고나서 미리 판단해둔 후에 질의에 따라 출력

개념
- 단절점, 단절선 : 특정 노드 또는 선을 제거했을 때 해당 그래프가 2개 이상으로 분리된다면 해당 노드 또는 선을 단절점 또는 단절선이라한다.
    DST(DFS Spanning Tree)를 활용해 단절점, 단절선을 판단할 수 있다.
    - 단절점 판단법 O(v+e)
        1. dfs 로 내려가며 방문번호 매김
        2. 돌아나올 때, 내 short 값과 부모노드를 포함한 인접노드들의 방문번호 중 최솟값을 short값으로 기록하고, 다음 노드에 전파하면서 돌아나옴
        3. 전파받은 short값이 현재 short값보다 작으면 그걸로 갱신. 만약 현재 방문번호와 전파받은 short값이 같으면 해당 노드는 단절점!
        +) 루트 노드는 자식이 2개 이상이면 무조건 단절점임. 따로 처리해줄 것
    - 단절선 판단법 (단절점 판단법과 유사함. 달라지는 부분은 &quot;&quot;로 감쌌음!) O(v+e)
        1. dfs 로 내려가며 방문번호 매김
        2. 돌아나올 때, 내 short 값과 &quot;부모노드를 제외한&quot; 인접노드들의 방문번호 중 최솟값을 short값으로 기록하고, 다음 노드에 전파하면서 돌아나옴
        3. 전파받은 short값이 현재 short값보다 작으면 그걸로 갱신. 만약 &quot;현재 방문번호보다 전파받은 short값이 크면&quot; 그 사이 간선은 단절선!
'''

from collections import defaultdict
import sys
input = sys.stdin.readline
INF = int(2e9)

graph = defaultdict(list)
edge_num = dict() #답 출력을 위해 몇번째로 입력받은 간선인지 저장해둬야 함
N = int(input())

#간선 입력받기
for i in range(1, N) :
    n1, n2 = map(int, input().split())
    graph[n1].append(n2)
    graph[n2].append(n1)

    edge_name = ' '.join(map(str, sorted([n1, n2]))) #일관적인 간선 네이밍을 위해 간선 숫자순으로 정렬해서 문자열로 변환해 키값 사용
    edge_num[edge_name] = i # i번째로 입력받은 간선


nums = [[INF]*2 for _ in range(N+1)] # i번째 인덱스에 i번 노드의 정보를 담음. 0번째 값은 방문번호, 1번째 값은 short값
visited = [False]*(N+1)
cut_vertex = [False]*(N+1) #단절점인지 기록

# 단절점 dfs
def vertex_dfs(cur, visit_num) :
    #방문번호 매김
    nums[cur][0] = visit_num

    #자식노드 dfs
    for node in graph[cur] :
        if not visited[node] : #방문했던 노드가 아니면
            visited[cur] = True
            new_short = vertex_dfs(node, visit_num+1)
            
            #전파받은 short값 기존값과 비교 후 갱신
            nums[cur][1] = min(nums[cur][1], new_short)
            #전파받은 short값이 현재 노드의 방문번호와 같다면 단절점!
            if nums[cur][0] == new_short :
                cut_vertex[cur] = True

    #돌아나오기 - 내 short값을 인접노드 방문번호들과 비교해 최솟값으로 갱신
    for node in graph[cur] :
        nums[cur][1] = min(nums[cur][1], nums[node][0])

    return nums[cur][1]


vertex_dfs(1, 1)

# 루트 노드는 따로 판단해줌
root = 1
cut_vertex[root] = True if 1 &amp;lt; len(graph[root]) else False

# print(cut_vertex)


### 단절선 판단 ###

nums = [[INF]*2 for _ in range(N+1)]
visited = [False]*(N+1)
cut_edge = [False]*(N) # i번 인덱스에 i번째로 입력받은 간선이 단절선인지 기록

# 단절선 dfs
def edge_dfs(cur, visit_num, parent) :
    #방문번호 매김
    nums[cur][0] = visit_num

    # 인접노드 dfs
    for node in graph[cur] :
        if not visited[node] :
            visited[node] = True
            new_short = edge_dfs(node, visit_num+1, cur)
        
            #새로운 short 값 전파받기
            nums[cur][1] = min(nums[cur][1], new_short)

            #현재 방문번호보다 전파받은 short 값이 더 크면 그 사이 간선은 단절선!
            if nums[cur][0] &amp;lt; new_short :
                edge_name = ' '.join(map(str, sorted([cur, node])))
                # print(edge_name)
                idx = edge_num[edge_name]
                cut_edge[idx] = True

    #부모노드를 제외한 인접노드들의 방문번호와 현재노드의 short 값 비교해 갱신
    for node in graph[cur] :
        if node == parent :
            continue
        nums[cur][1] = min(nums[cur][1], nums[node][0])
    
    return nums[cur][1]

edge_dfs(1, 1, 0)

# print(cut_edge)


# 문제 입력받아서 답 출력
for _ in range(int(input())) :
    t, k = map(int, input().split())
    if t == 1 :
        print(&quot;yes&quot; if cut_vertex[k] else &quot;no&quot;)
    elif t == 2 :
        print(&quot;yes&quot; if cut_edge[k] else &quot;no&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>그래프</category>
      <category>단절점</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>트리</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/209</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-14675-%EB%8B%A8%EC%A0%88%EC%A0%90%EA%B3%BC-%EB%8B%A8%EC%A0%88%EC%84%A0-Sil1-%EA%B7%B8%EB%9E%98%ED%94%84%ED%8A%B8%EB%A6%AC-Python#entry209comment</comments>
      <pubDate>Mon, 10 Jun 2024 19:22:02 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 9663 N-Queen | Gol4 | 백트래킹 Python (시간초과)</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-9663-N-Queen-Gol4-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python-%EC%8B%9C%EA%B0%84%EC%B4%88%EA%B3%BC</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 9663 N-Queen | Gol4 | 백트래킹 Python (시간초과)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9663&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9663&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;백트래킹 연습으로 풀어본 문제.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;돌고돌아서 테케가 통과되는 코드는 만들어냈지만 시간제한은 pypy로도 끝끝내 통과 못시킨 문제&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;통과된 코드들이랑 비교해서 고쳐도 봤는데, 아예 코드를 갈아엎어야 하는 것 같다&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;너무 시간을 많이 써서 여기까지만 하는 걸로.. 애초에 파이썬으로 통과되기 빡빡한 문제라고 하고,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;내 수준에서는 최적화에 시간을 더 쓰기보다는 다른 문제들을 풀어보는 게 맞는 것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래도 얻은 것들이 많아서 기록!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;구상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;N-Queen : 크기가 NxN인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N이 주어지고, N개를 놓을 수 있는 경우의 개수를 구하기&lt;/li&gt;
&lt;li&gt;제한시간 : 10초 (뭐든 하라는 건가)&lt;/li&gt;
&lt;li&gt;퀸은 가로세로대각선 어느방향으로든 원하는 만큼 이동할 수 있음&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-&amp;gt;퀸 특성상 한 행에 여러개가 올 수 없으므로 NxN인 행에 N개를 두려면 무조건 각 행에 퀸이 한개씩 배치되어야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤매기&lt;br /&gt;처음에는 체스판(좌표)이라 당연히 2차원 배열로 해보려고 했는데 중복제거 등의 문제를 어떻게 해결해야 할지 막힘&lt;br /&gt;점점 백트래킹과도 멀어지는 구상이 나와서 힌트 찾아봄&lt;br /&gt;-&amp;gt; 2차원 좌표는 1차원 배열에도 저장할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구상
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배치한 퀸 좌표를 1차원 배열에 저장&lt;/li&gt;
&lt;li&gt;체스판 좌표마다 이미 배치된 퀸 목록 돌며 배치 가능한 좌표인지 확인(좌표값으로 계산)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TS : 체스판 좌표를 다 돌지 않아도 됨. 퀸 특징때문에 어차피 한 줄에 하나밖에 못놔서 행변호만 반복문 돌면 된다..&lt;br /&gt;&amp;nbsp;-&amp;gt; 무조건 한 행에 한 퀸 배치! 다음 퀸은 다음 행에서 탐색&lt;br /&gt;해당 행에 퀸을 하나도 배치할 수 없는 경우 N개를 배치할 수 없다는 의미이므로 백트래킹 할 것!&lt;br /&gt;그리고 무조건 한 행에 퀸 하나씩이라 중복 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배치 가능하면 배치 후 dfs재귀&lt;/li&gt;
&lt;li&gt;어디에도 배치 불가능하면 백트래킹&lt;/li&gt;
&lt;li&gt;n개 모두 배치했으면 경우의 수 +1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;1차원 배열 솔루션
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2차원 배열 정보를 꼭 2차원에 담아야 하는 건 아님. 인덱스 자체를 정보로 쓸 수도 있음&lt;/li&gt;
&lt;li&gt;(x,y) 좌표를 x=인덱스, y=값 으로 해서 그냥 리스트에 넣을 수도 있음 &lt;br /&gt;-&amp;gt; 이러면 튜플로 바꿔서 set에 넣어 중복체크도 가능함 (but 이 문제에서는 상황상 중복체크가 필요없음..풀이 참고)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TIP : 리스트 인덱스도 함께 반복문 돌고 싶을 떈 enumerate(리스트)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# timeout : 백트래킹(시간초과)
# TS : 2차원 좌표는 1차원 배열에 저장할 수도 있다.
# TIP : 리스트 인덱스도 함께 반복문 돌고 싶을 떈 enumerate(리스트)

'''
[문제해석]
N-Queen : 크기가 NxN인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제
- N이 주어지고, N개를 놓을 수 있는 경우의 개수를 구하기
- 제한시간 : 10초 (뭐든 하라는 건가)
** 퀸은 가로세로대각선 어느방향으로든 원하는 만큼 이동할 수 있음
    -&amp;gt;퀸 특성상 한 행에 여러개가 올 수 없으므로 NxN인 행에 N개를 두려면 무조건 각 행에 퀸이 한개씩 배치되어야 함

구상 3
- 배치한 퀸 좌표를 1차원 배열에 저장
- 체스판 좌표마다 이미 배치된 퀸 목록 돌며 배치 가능한 좌표인지 확인(좌표값으로 계산)
    - TS : 체스판 좌표를 다 돌지 않아도 됨. 퀸 특징때문에 어차피 한 줄에 하나밖에 못놔서 행변호만 반복문 돌면 된다..
        -&amp;gt; 무조건 한 행에 한 퀸 배치! 다음 퀸은 다음 행에서 탐색
            해당 행에 퀸을 하나도 배치할 수 없는 경우 N개를 배치할 수 없다는 의미이므로 백트래킹 할 것!
            그리고 무조건 한 행에 퀸 하나씩이라 중복 없음
- 배치 가능하면 배치 후 dfs재귀
- 어디에도 배치 불가능하면 백트래킹
- n개 모두 배치했으면 경우의 수 +1
'''

N = int(input())

board = [-1]*N # x=인덱스, y=값에 좌표 저장
result = 0

def dfs(q_cnt) :
    if q_cnt == N :
        global result
        result += 1
        return
    
    i = q_cnt #현재 q_cnt개의 퀸이 이미 배치되어있으므로 q_cnt+1번째 행, 즉 인덱스 q_cnt인 행에 이번 퀸을 배치해야 함
    for j in range(N) : #이번 행의 열을 돈다
        # 퀸 목록 돌며 배치 가능한지 확인
        able = True
        # for x, y in enumerate(board) : #시간 줄여보려고 enumerate 빼봄
        for idx in range(q_cnt) : #직전행까지만 퀸이 배치되어있을 것이므로 직전행까지만 확인하면 됨
            x, y = idx, board[idx]
            if j == y or abs(x-i) == abs(y-j) : #열, 대각선 중 하나에 걸림
                able = False
        
        #이 열번호에 배치 불가. 다음 위치 탐색
        if not able :
            continue
        
        #배치 가능
        board[q_cnt] = j
        dfs(q_cnt+1)
        # board[q_cnt] = -1

dfs(0)
            
print(result)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그만 붙잡고 보내준다 나중에 성장해서 다시 만나..~&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>백트래킹</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/208</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-9663-N-Queen-Gol4-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python-%EC%8B%9C%EA%B0%84%EC%B4%88%EA%B3%BC#entry208comment</comments>
      <pubDate>Wed, 5 Jun 2024 18:08:38 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 15666 N과 M (12) | Sil2 | 백트래킹 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-15666-N%EA%B3%BC-M-12-Sil2-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 15666 N과 M (12) | Sil2 | 백트래킹 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15666&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/15666&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;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;N개의 자연수 중 M개 고르기&lt;/li&gt;
&lt;li&gt;같은 수 여러번 고르기 가능&lt;/li&gt;
&lt;li&gt;수열은 비내림차순(같거나 오름차순)이어야 함 &lt;br /&gt;-&amp;gt; 순서 다른것도 정렬해서 같은걸로 쳐야 함&lt;/li&gt;
&lt;li&gt;결과는 중복없이, 사전순으로&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 백트래킹

'''
[문제해석]
- N개의 자연수 중 M개 고르기
- 같은 수 여러번 고르기 가능
- 수열은 비내림차순(같거나 오름차순)이어야 함 -&amp;gt; 순서 다른것도 정렬해서 같은걸로 쳐야 함
- 결과는 중복없이, 사전순으로
'''

N, M = map(int, input().split())
nums = list(map(int, input().split()))
nums.sort()

cur_perm = []

def recur(limit) :
    if len(cur_perm) == M :
        print(*cur_perm)
        return
    
    prev = 0
    for num in nums :
        if num == prev : #중복제거 : 같은 자리에 이미 골랐던 수와 같은 값의 수를 고르는 건 제외
            continue
        if num &amp;lt; limit : #비내림차순
            continue
        
        cur_perm.append(num)
        recur(num)
        cur_perm.pop()
        prev = num

recur(0)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>백트래킹</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/207</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-15666-N%EA%B3%BC-M-12-Sil2-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python#entry207comment</comments>
      <pubDate>Wed, 5 Jun 2024 18:08:12 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 15663 N과 M (9) | Sil2 | 백트래킹 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-15663-N%EA%B3%BC-M9-Sil2-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 15663 N과 M (9) | Sil2 | 백트래킹 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/15663&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/15663&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;구상&lt;/h3&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;N개의 자연수 중 M개를 고른 모든 경우 출력
&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;/li&gt;
&lt;li&gt;중복 수열 없이&lt;/li&gt;
&lt;li&gt;사전순 출력&lt;/li&gt;
&lt;li&gt;구상 : 현재까지의 수열, 사용여부, 숫자 갯수 세기 해야겠군&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;정렬된 순서로 만들면서 중복을 제거해줘야 했는데, 두 가지 방법으로 풀 수 있었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;set은 사용할 수 없었음! (트러블슈팅 참고)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;풀이 1 : 백트래킹 - dict.fromkeys(리스트)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;list(dict.fromkeys(리스트))&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;: 순서 유지하면서 중복 제거할 때 사용 가능!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;풀이 2 : 백트래킹 - 숫자 자료를 정렬해 직전값과 비교&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;트러블 슈팅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 냅다 문자열로 만들어서 set에 넣어 중복제거하고 마지막에 정렬해서 출력했었는데,&amp;nbsp;&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;문자열의 정렬은 숫자의 정렬과는 다르다!&lt;br /&gt;숫자 사전순으로 출력 시 문자열인 상태로 정렬하면 안된다..&lt;br /&gt;문자열은 각 char 기준으로 정렬해서 두자릿수 이상의 숫자 정렬에서 큰 수가 더 앞에 올 수 있다.&lt;br /&gt;ex) 11과 9를 출력할 때,&lt;br /&gt;- 문자열 순으로 정렬 : 11 9&lt;br /&gt;- 숫자 순으로 정렬 : 9 11&lt;br /&gt;이렇게 됨.&lt;br /&gt;&lt;br /&gt;그럼 어떻게 해야할까?&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;숫자의 경우 숫자인 자료를 미리 정렬해두고 사용하면 됨&lt;br /&gt;정렬된 자료에서는, 같은 자리에 사용한 직전값과 비교하면서 만들면 결과를 중복 없이 생성할 수 있음 (이건 재귀일 때만 쓸 수 있는 스킬일까..?)&lt;/li&gt;
&lt;li&gt;또는 순서 유지하면서 중복 제거하려면 list(dict.fromkeys(리스트)) 사용 가능!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - dict.fromkeys()&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 백트래킹 - dict.fromkeys(리스트)
# TS : 문자열의 정렬은 숫자의 정렬과는 다르다. 숫자 사전순으로 출력 시 문자열인 상태로 정렬하면 안된다..
#       문자열은 각 char 기준으로 정렬해서 두자릿수 이상의 숫자 정렬에서 큰 수가 더 앞에 올 수 있다. 
#       그럼 어떡해?
#       -&amp;gt; 숫자의 경우 숫자인 자료를 미리 정렬해두고 사용하면 됨
#           정렬된 자료에서는, 같은 자리에 사용한 직전값과 비교하면서 만들면 결과를 중복 없이 생성할 수 있음 (이건 재귀일 때만 쓸 수 있는 스킬일까..?)
#       -&amp;gt; 또는 순서 유지하면서 중복 제거하려면 list(dict.fromkeys(리스트)) 사용 가능!

'''
[문제해석]
-답 : N개의 자연수 중 M개를 고른 모든 경우
- 순서 다르면 다름
- 중복 수열 없이 -&amp;gt; 문자열로 만들어서 set에 넣기
- 사전순 출력

구상 : 현재까지의 수열, 사용여부, 숫자 갯수 세기
'''


import sys
input = sys.stdin.readline

N, M = map(int, input().split())
nums = list(map(int, input().split()))
nums.sort() #사전순

perms = [] #순서 유지를 위해 set말고 list사용. 중복제거는 순서가 유지되는 dict.fromkeys()사용할 것
visited = [False]*N
cur_perm = []

#재귀 백트래킹
def recur() :
    #백트래킹 조건 1 : 수열 길이
    if len(cur_perm) == M :
        perms.append(&quot; &quot;.join(map(str, cur_perm))) #공백으로 구분된 문자열로 변환해 저장
        return
    
    for i in range(N) :
        #백트래킹 조건 2 : 이미 사용된 수인지
        if visited[i] :
            continue
        
        visited[i] = True
        cur_perm.append(nums[i])
        recur()
        cur_perm.pop()
        visited[i] = False

#재귀 메서드 시작
recur()

#출력
# TS
print(*list(dict.fromkeys(perms)), sep=&quot;\n&quot;) #중복제거 후 풀어서 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 숫자 자료를 정렬해두고 직전값과 비교해 사용하는 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1717576337803&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : 백트래킹 - 숫자 자료를 정렬해 직전값과 비교

import sys
input = sys.stdin.readline

N, M = map(int, input().split())
nums = list(map(int, input().split()))
nums.sort() #사전순

visited = [False]*N
cur_perm = []

#재귀 백트래킹
def recur() :
    #백트래킹 조건 1 : 수열 길이
    if len(cur_perm) == M :
        print(*cur_perm)
        return
    
    prev = 0
    for i in range(N) :
        #백트래킹 조건 2 : 이미 사용된 수인지, 같은 자리에 썼던 수와 같은수인지(중복제거)
        if visited[i] or prev==nums[i]:
            continue
        
        visited[i] = True
        cur_perm.append(nums[i])
        recur()
        cur_perm.pop()
        visited[i] = False
        prev = nums[i] #이 자리에 방금 사용된 수 저장

#재귀 메서드 시작
recur()&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>백트래킹</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/206</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-15663-N%EA%B3%BC-M9-Sil2-%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9-Python#entry206comment</comments>
      <pubDate>Wed, 5 Jun 2024 17:43:32 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 11725 트리의 부모 찾기 | Sil2 | BFS,DFS Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11725-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%B6%80%EB%AA%A8-%EC%B0%BE%EA%B8%B0-Sil2-BFSDFS-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] 11725 트리의 부모 찾기 | Sil2 | BFS,DFS Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/11725&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/11725&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bfs dfs 까먹을 것 같아서 몸풀기!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세가지 방법으로 풀어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : dfs - 재귀 (71496KB, 336ms)&lt;br /&gt;파이썬 재귀 깊이 제한을 조정하는 코드를 추가해서 통과했는데, 부하가 있는 것 같아 다른 방법도 풀어보기로&lt;/li&gt;
&lt;li&gt;풀이 2 : dfs - stack (61568KB, 312ms)&lt;/li&gt;
&lt;li&gt;풀이 3 : bfs - queue(deque) (62040KB, 300ms)&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;성능은 채점시마다 약간의 오차가 있는걸 감안하면, 세가지 풀이에 그렇게 큰 차이는 없는 듯 하다.&lt;br /&gt;그래도 역시 bfs가 젤 빠르게 나오긴 했다만&lt;/li&gt;
&lt;li&gt;출력문으로 print말고 더 빠르다는 sys.stdout.write() 을 사용해봤다.&lt;br /&gt;print는 알아서 string 으로 바꿔주지만 write는 str()을 써서 string으로 바꿔넣어줘야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;[문제 해석]&lt;/p&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;답 : 루트노드를 1번노드로 정했을 때, 2번노드부터 해당 노드의 부모노드 번호를 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;dfs 재귀로 구현했는데, 채점에서 Recursion error가 떴다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백준 채점 시 파이썬에는 1000번 이상의 재귀호출 시 Recursion 에러가 발생하도록 되어있는데, 이 값을 조정할 수 있다.&lt;br /&gt;sys.setrecursionlimit(100000) 이렇게&lt;/li&gt;
&lt;li&gt;이 방식으로 재귀 최댓값을 늘렸을 때, 재귀의 깊이가 채점 서버가 감당할 수 없는 정도로 깊어지면, Segmentation fault가 발생해 런타임 에러 이유로 SegFault를 받게된다고 한다.&lt;br /&gt;&amp;nbsp;-&amp;gt; sys.setrecursionlimit(10**9) 로 늘려서 해결&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - DFS 재귀&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : dfs - recursion (71496KB, 336ms)
# TS : dfs 재귀로 구현했는데, 채점에서 Recursion error가 떴다.
#       백준 채점 시 파이썬에는 1000번 이상의 재귀호출 시 Recursion 에러가 발생하도록 되어있는데, 이 값을 조정할 수 있다.
#       sys.setrecursionlimit(100000) 이렇게
#       이 방식으로 재귀 최댓값을 늘렸을 때, 재귀의 깊이가 채점 서버가 감당할 수 없는 정도로 깊어지면, Segmentation fault가 발생해 런타임 에러 이유로 SegFault를 받게된다고 한다.
#       -&amp;gt; sys.setrecursionlimit(10**9) 로 늘려서 해결. 부하가 있는 것 같으니 다른 방법으로도 풀어봐야겠음

'''
[문제 해석]
- 연결된 정점 목록 주어짐
답 : 루트노드를 1번노드로 정했을 때, 2번노드부터 해당 노드의 부모노드 번호를 출력
'''

from collections import defaultdict
import sys
input = sys.stdin.readline
sys.setrecursionlimit(10**9) # TS : Recursion error 방지

N = int(input())
edge = defaultdict(list)

for _ in range(N-1) :
    n1, n2 = map(int, input().split())
    edge[n1].append(n2)
    edge[n2].append(n1)


#dfs
parent_arr = [0]*(N+1)

def dfs(cur, parent) :
    for child in edge[cur] :
        if child == parent : #부모노드는 넘어감
            continue
        parent_arr[child] = cur #해당 자식노드의 부모노드로 기록
        dfs(child, cur)

dfs(1,0) #1의 부모노드는 존재하지 않으므로 무의미한 값인 0 넣음

#출력
# print('\n'.join(map(str, parent_arr[2:]))) #아래와 같은 동작
sys.stdout.write('\n'.join(map(str, parent_arr[2:])))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - DFS 스택&lt;/h3&gt;
&lt;pre id=&quot;code_1717072229000&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : dfs - stack (61568KB, 312ms)

'''
- 연결된 정점 목록
답 : 루트노드를 1번노드로 정했을 때, 2번노드부터 해당 노드의 부모노드 번호를 출력
'''

from collections import defaultdict
import sys
input = sys.stdin.readline

N = int(input())
edge = defaultdict(list)

for _ in range(N-1) :
    n1, n2 = map(int, input().split())
    edge[n1].append(n2)
    edge[n2].append(n1)


#dfs
parent_arr = [0]*(N+1)
stack = []
stack.append((1,0))

while stack :
    cur, parent = stack.pop()
    for child in edge[cur] :
        if child == parent : #부모노드는 넘어감
            continue
        parent_arr[child] = cur #해당 자식노드의 부모노드로 기록
        stack.append((child, cur)) #dfs

#출력
sys.stdout.write('\n'.join(map(str, parent_arr[2:])))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - BFS&lt;/h3&gt;
&lt;pre id=&quot;code_1717072306380&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v3 : bfs - queue(deque) (62040KB, 300ms)

'''
- 연결된 정점 목록
답 : 루트노드를 1번노드로 정했을 때, 2번노드부터 해당 노드의 부모노드 번호를 출력
'''

from collections import defaultdict, deque
import sys
input = sys.stdin.readline

N = int(input())
edge = defaultdict(list)

for _ in range(N-1) :
    n1, n2 = map(int, input().split())
    edge[n1].append(n2)
    edge[n2].append(n1)


#bfs
parent_arr = [0]*(N+1)
que = deque()
que.append((1,0))

while que :
    cur, parent = que.popleft()
    for child in edge[cur] :
        if child == parent : #부모노드는 넘어감
            continue
        parent_arr[child] = cur #해당 자식노드의 부모노드로 기록
        que.append((child, cur)) #bfs

#출력
sys.stdout.write('\n'.join(map(str, parent_arr[2:])))&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DSA/Algorithm</category>
      <category>BFS</category>
      <category>DFS</category>
      <category>백준</category>
      <category>알고리즘</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/205</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-11725-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EB%B6%80%EB%AA%A8-%EC%B0%BE%EA%B8%B0-Sil2-BFSDFS-Python#entry205comment</comments>
      <pubDate>Thu, 30 May 2024 21:33:17 +0900</pubDate>
    </item>
    <item>
      <title>[백준] Sil2 | 해시 | 19583 싸이버개강총회 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil2-%ED%95%B4%EC%8B%9C-19583-%EC%8B%B8%EC%9D%B4%EB%B2%84%EA%B0%9C%EA%B0%95%EC%B4%9D%ED%9A%8C-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] Sil2 | 해시 | 19583 싸이버개강총회 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/19583&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/19583&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : 해시 - dictionary &lt;br /&gt;알고리즘은 별거없는데 입력이나 시간 데이터 전처리가 귀찮았&lt;br /&gt;입력 종료 시점을 모르고 끝까지 받는 문제를 처음 풀어봐서 입력받는 법, 디버깅하는 법에서 헤맸다.&lt;/li&gt;
&lt;li&gt;풀이 2 : 해시 - set
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dictionary와 set은 공간, 시간복잡도가 거의 차이 안난다. set은 dictionary에서 key만 있는 거라고 보면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;제때 입장, 퇴장 모두 확인된 사람 수&lt;/li&gt;
&lt;li&gt;제때 입장 : 채팅시각이 개총 시작시각보다 작거나 같음&lt;/li&gt;
&lt;li&gt;제때&amp;nbsp;퇴장&amp;nbsp;:&amp;nbsp;채팅시각이&amp;nbsp;개총&amp;nbsp;종료시각보다&amp;nbsp;크거나&amp;nbsp;같고&amp;nbsp;개총&amp;nbsp;스트리밍&amp;nbsp;종료시각보다&amp;nbsp;작거나&amp;nbsp;같음 &lt;br /&gt;-&amp;gt; 즉, 개총 시작시각까지 채팅을 친 기록과 종료 후부터 개총 스트리밍 종료시각까지 채팅을 친 기록 이 두가지가 모두 있는 사람 수 세기&lt;/li&gt;
&lt;li&gt;시간 데이터는 모두 분단위로 바꿔서 사용&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;TS 1 : sys.stdin.readlines() 는 입력의 끝부분까지 전부 입력받아온다. 입력의 개수를 알 수 없을 때 사용!&lt;br /&gt;Ctrl+Z+엔터가 터미널 입력시 파일의 끝을 의미하므로 입력 끝까지 받는 테스트 시 사용할 것!&lt;/li&gt;
&lt;li&gt;TS&amp;nbsp;2&amp;nbsp;:&amp;nbsp;테케&amp;nbsp;2&amp;nbsp;오답&amp;nbsp;&lt;br /&gt;-&amp;gt;&amp;nbsp;이름&amp;nbsp;두번&amp;nbsp;세어지는&amp;nbsp;경우&amp;nbsp;생각&amp;nbsp;못함!!!&amp;nbsp;set사용해서&amp;nbsp;중복없이&amp;nbsp;세도록&amp;nbsp;수정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - set&lt;/h3&gt;
&lt;pre id=&quot;code_1717001392964&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : 해시 - set
#알고리즘은 별거없는데 입력이나 시간 데이터 전처리가 귀찮았
# TS 1 : sys.stdin.readlines() 는 입력의 끝부분까지 전부 입력받아온다. 입력의 개수를 알 수 없을 때 사용!
#           Ctrl+Z+엔터가 터미널 입력시 파일의 끝을 의미하므로 입력 끝까지 받는 테스트 시 사용할 것!
# TS 2 : 테케 2 오답 -&amp;gt; 이름 두번 세어지는 경우 생각 못함!!! set사용해서 중복없이 세도록 수정
# TIP : dictionary와 set은 공간, 시간복잡도가 거의 차이 안난다. set은 dictionary에서 key만 있는 거라고 보면 됨

'''
[문제 해석]
- 제때 입장, 퇴장 모두 확인된 사람 수
- 제때 입장 : 채팅시각이 개총 시작시각보다 작거나 같음
- 제때 퇴장 : 채팅시각이 개총 종료시각보다 크거나 같고 개총 스트리밍 종료시각보다 작거나 같음
-&amp;gt; 즉, 개총 시작시각까지 채팅을 친 기록과 종료 후부터 개총 스트리밍 종료시각까지 채팅을 친 기록 이 두가지가 모두 있는 사람 수 세기
- 시간 데이터는 모두 분단위로 바꿔서 사용
'''

import sys
input = sys.stdin.readline

#문자열 시간을 분단위 숫자로 변환해주는 메서드
def str_to_min(time_str) :
    h, m = map(int, time_str.split(&quot;:&quot;))
    return h*60 + m

#시각 3개 입력받기
time_table = []
for time_str in input().split() :
    time_table.append(str_to_min(time_str))

#채팅기록 입력받기 - 입력의 끝까지 한번에 받음(입력이 총 몇갠지 몰라서)
chats = sys.stdin.readlines() # TS 1

#채팅으로 입퇴장 확인
entered = set() # TIP
success = set() # TS 2
for chat in chats :
    time_str, name = chat.split()
    time = str_to_min(time_str)

    if time &amp;lt;= time_table[0] : #제때 입장한 사람 기록
        entered.add(name)

    elif time_table[1] &amp;lt;= time &amp;lt;= time_table[2] : #제때 퇴장한 사람이고
        if name in entered : #제때 입장한 사람이면
            success.add(name) # TS 2

print(len(success))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - dictionary&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 해시 - dictionary
#알고리즘은 별거없는데 입력이나 시간 데이터 전처리가 귀찮았
# TS 1 : sys.stdin.readlines() 는 입력의 끝부분까지 전부 입력받아온다. 입력의 개수를 알 수 없을 때 사용!
#           Ctrl+Z+엔터가 터미널 입력시 파일의 끝을 의미하므로 입력 끝까지 받는 테스트 시 사용할 것!
# TS 2 : 테케 2 오답 -&amp;gt; 이름 두번 세어지는 경우 생각 못함!!! set사용해서 중복없이 세도록 수정

'''
[문제 해석]
- 제때 입장, 퇴장 모두 확인된 사람 수
- 제때 입장 : 채팅시각이 개총 시작시각보다 작거나 같음
- 제때 퇴장 : 채팅시각이 개총 종료시각보다 크거나 같고 개총 스트리밍 종료시각보다 작거나 같음
-&amp;gt; 즉, 개총 시작시각까지 채팅을 친 기록과 종료 후부터 개총 스트리밍 종료시각까지 채팅을 친 기록 이 두가지가 모두 있는 사람 수 세기
- 시간 데이터는 모두 분단위로 바꿔서 사용
'''

import sys
input = sys.stdin.readline

#문자열 시간을 분단위 숫자로 변환해주는 메서드
def str_to_min(time_str) :
    h, m = map(int, time_str.split(&quot;:&quot;))
    return h*60 + m

#시각 3개 입력받기
time_table = []
for time_str in input().split() :
    time_table.append(str_to_min(time_str))

#채팅기록 입력받기 - 입력의 끝까지 한번에 받음(입력이 총 몇갠지 몰라서)
chats = sys.stdin.readlines() # TS 1

#채팅으로 입퇴장 확인
entered = {} # set으로도 해보기
success = set() # TS 2
for chat in chats :
    time_str, name = chat.split()
    time = str_to_min(time_str)

    if time &amp;lt;= time_table[0] : #제때 입장한 사람 기록
        entered[name] = True

    elif time_table[1] &amp;lt;= time &amp;lt;= time_table[2] : #제때 퇴장한 사람이고
        if name in entered.keys() : #제때 입장한 사람이면
            success.add(name) # TS 2

print(len(success))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>해시</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/204</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil2-%ED%95%B4%EC%8B%9C-19583-%EC%8B%B8%EC%9D%B4%EB%B2%84%EA%B0%9C%EA%B0%95%EC%B4%9D%ED%9A%8C-Python#entry204comment</comments>
      <pubDate>Thu, 30 May 2024 01:50:28 +0900</pubDate>
    </item>
    <item>
      <title>[백준] Sil5 | 해시 | 7785 회사에 있는 사람 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil5-%ED%95%B4%EC%8B%9C-7785-%ED%9A%8C%EC%82%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%82%AC%EB%9E%8C-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] Sil5 | 해시 | 7785 회사에 있는 사람 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/7785&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/7785&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;풀이 1 : 딕셔너리 쓰면 곰방곰방 풀린다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;딕셔너리 값 삭제는 del이나 pop을 쓴다.&lt;/li&gt;
&lt;li&gt;items()를 쓰면 key, value 쌍을 얻지만&lt;br /&gt;keys()를 쓰면 key만 얻을 수 있고 더 빠르다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;풀이 2 : set을 써서 풀 수도 있다. dictionary써도 value에 넣을 게 딱히 없음.. key만 갖고있는 set으로도 풀어봤다.
&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;/li&gt;
&lt;li&gt;join을 쓰면 출력이 훨 빨라져서 츄라이!&lt;/li&gt;
&lt;li&gt;sys.stdout.write도 써볼까..&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - set&lt;/h3&gt;
&lt;pre id=&quot;code_1717001185748&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v2 : 해시 - set

import sys
input = sys.stdin.readline

N = int(input())

people = set()
for _ in range(N) :
    name, log = input().split()
    if log == &quot;leave&quot; :
        people.discard(name) #discard : 해당 키값 존재여부 상관없이 삭제
    else : #log == &quot;enter&quot;
        people.add(name)


#키값만 사전순 역순으로 출력
#출력 - 그냥 for문 돌면서 이렇게 해도 되고
# for name in sorted(people, reverse=True) :
#     print(name)

#출력 - join써서 이렇게 해도 됨. 이게 훨 빠름!
print(&quot;\n&quot;.join(sorted(people, reverse=True)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - dictionary&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 해시 - dictionary

import sys
input = sys.stdin.readline

N = int(input())

people = {} # set으로도 해보기
for _ in range(N) :
    name, log = input().split()
    if log == &quot;leave&quot; :
        del people[name]
    else : #log == &quot;enter&quot;
        people[name] = True #암것도 저장 안해도 돼서 암거나 저장


#키값만 사전순 역순으로 출력
#출력 - 그냥 for문 돌면서 이렇게 해도 되고
# for name in sorted(people.keys(), reverse=True) :
#     print(name)

#출력 - join써서 이렇게 해도 됨. 이게 훨 빠름!
print(&quot;\n&quot;.join(sorted(people.keys(), reverse=True)))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>해시</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/203</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil5-%ED%95%B4%EC%8B%9C-7785-%ED%9A%8C%EC%82%AC%EC%97%90-%EC%9E%88%EB%8A%94-%EC%82%AC%EB%9E%8C-Python#entry203comment</comments>
      <pubDate>Wed, 29 May 2024 02:45:13 +0900</pubDate>
    </item>
    <item>
      <title>[백준] Sil3 | 해시 | 17219 비밀번호 찾기 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil3-%ED%95%B4%EC%8B%9C-17219-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%B0%BE%EA%B8%B0-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] Sil3 | 해시 | 17219 비밀번호 찾기 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/17219&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/17219&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;br /&gt;그냥 딕셔너리에 때려넣고 꺼내면 될 거 같은데..넴 됨&lt;br /&gt;시간 최적화한 다른 코드들 쫌 봤는데 입출력 한번에 하기나 sys.stdout.write나 다른 요상압축코드 써서 줄이고 계셨다&lt;/li&gt;
&lt;li&gt;간만에 기본 딕셔너리 복습했다&lt;/li&gt;
&lt;li&gt;날 열어주는 단 하나뿐인 비밀번호야&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;x&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v1 : 해시(딕셔너리)
#그냥 딕셔너리에 때려넣고 꺼내면 될 거 같은데..넴 됨

import sys
input = sys.stdin.readline

N, M = map(int, input().split())

dic = {}
for _ in range(N) :
    site, pw = input().split()
    dic[site] = pw

for _ in range(M) :
    site = input().strip()
    print(dic[site])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>해시</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/202</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Sil3-%ED%95%B4%EC%8B%9C-17219-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%B0%BE%EA%B8%B0-Python#entry202comment</comments>
      <pubDate>Wed, 29 May 2024 02:14:45 +0900</pubDate>
    </item>
    <item>
      <title>[백준] Gol5 | 투 포인터,이진탐색 | 9024 두 수의 합 Python</title>
      <link>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Gol5-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89-9024-%EB%91%90-%EC%88%98%EC%9D%98-%ED%95%A9-Python</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;[백준] Gol5 | 투 포인터,이진탐색 | 9024 두 수의 합 Python&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/9024&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.acmicpc.net/problem/9024&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;구상&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직전에 푼 3273번과 매우 유사한 문제!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;풀이 1 : 이진탐색 (시간초과)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TS : 테케 통과, 시간초과.. pypy는 통과!!! 논리는 맞았으나 시간이 문제.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;풀이 2 : 투 포인터&lt;br /&gt;이진탐색 문제는 투 포인터로도 풀 수 있다!&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;주어진 서로다른 수들 중 두 수의 합이 특정 수k에 가장 가까운 조합의 수&lt;/li&gt;
&lt;li&gt;숫자들은 -10^8 ~ 10^8(1억) 사이 정수. 숫자의 개수n는 1,000,000개 이하&lt;/li&gt;
&lt;li&gt;시간제한 : 1초&lt;/li&gt;
&lt;li&gt;답 : 합이 k에 가장 가까운 두 수 조합의 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;2천만 연산 내에 짜야 함. n^2이면 시간초과&lt;/li&gt;
&lt;li&gt;3273번처럼 정렬 + 이진탐색 으로 짜면 될까? nlogn -&amp;gt; 이것도 파이썬은 시간초과. (pypy는 통과)&lt;/li&gt;
&lt;li&gt;투 포인터로 풀어보자!&lt;/li&gt;
&lt;/ul&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&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;두 수의 합res와 k의 갭이 그동안의 min_gap보다 작으면, min_gap갱신 &amp;amp; 조합수cnt초기화 후 +1 해준 뒤&lt;br /&gt;갭이 +이면 작은 쪽 포인터를 +1이동, 갭이 -이면 큰 쪽 포인터를 -1이동시켜 탐색을 이어서 진행한다.&lt;br /&gt;이때 갭이 0이면, 아무 포인터 하나만 1 이동 시키고 이어서 탐색한다.&lt;/li&gt;
&lt;li&gt;갭이 그동안의 min_gap과 같으면, cnt +1 해준 뒤&lt;br /&gt;앞선 경우와 동일하게 이어서 진행한다.&lt;/li&gt;
&lt;li&gt;갭이 그동안의 min_gap보다 크면, 다른 작업 없이&lt;br /&gt;앞선 경우와 동일하게 이어서 진행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;고정수보다 더 큰 쪽 범위 이진탐색&lt;/li&gt;
&lt;li&gt;두 수의 합res와 k의 갭이 그동안의 min_gap보다 작으면, min_gap갱신 &amp;amp; 조합수cnt초기화 후 +1 해준 뒤&lt;br /&gt;갭이 +인지 -인지에 따라 큰 쪽 수를 더 키울지 줄일지 정해 이진탐색 범위를 조정하여 이번 이진탐색을 이어서 진행한다.&lt;br /&gt;이때 갭이 0이면, 더 탐색하지 않는다.&lt;/li&gt;
&lt;li&gt;갭이 그동안의 min_gap과 같으면, cnt +1 해준 뒤&lt;br /&gt;앞선 경우와 동일하게 이진탐색 범위를 조정하여 이어서 진행한다.&lt;/li&gt;
&lt;li&gt;갭이 그동안의 min_gap보다 크면, 다른 작업 없이&lt;br /&gt;앞선&amp;nbsp;경우와&amp;nbsp;동일하게&amp;nbsp;이진탐색&amp;nbsp;범위를&amp;nbsp;조정하여&amp;nbsp;이어서&amp;nbsp;진행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; 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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 투 포인터&lt;/h3&gt;
&lt;pre id=&quot;code_1716141719612&quot; class=&quot;python&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# v2 : 투 포인터
#직전에 푼 3273과 매우 유사한 문제!
#이진탐색 문제는 투 포인터로도 풀 수 있다!

'''
[문제 해석]
- 주어진 서로다른 수들 중 두 수의 합이 특정 수k에 가장 가까운 조합의 수
- 숫자들은 -10^8 ~ 10^8(1억) 사이 정수. 숫자의 개수n는 1,000,000개 이하
- 시간제한 : 1초
- 답 : 합이 k에 가장 가까운 두 수 조합의 개수

[예제]
10 4
-7 9 2 -4 12 1 5 -3 -2 0
정렬 -&amp;gt; [-7, -4, -3, -2, 0, 1, 2, 5, 9, 12]



구상
- 2천만 연산 내에 짜야 함. n^2이면 시간초과
- 3273번처럼 정렬 + 이진탐색 으로 짜면 될까? nlogn -&amp;gt; 이것도 파이썬은 시간초과. (pypy는 통과)
- 투 포인터로 풀어보자!
- 풀이
    - 투 포인터로, 시작점과 끝점에 포인터를 하나씩 두고 시작한다.
    - 두 수의 합res와 k의 갭이 그동안의 min_gap보다 작으면, min_gap갱신 &amp;amp; 조합수cnt초기화 후 +1 해준 뒤
        갭이 +이면 작은 쪽 포인터를 +1이동, 갭이 -이면 큰 쪽 포인터를 -1이동시켜 탐색을 이어서 진행한다.
        이때 갭이 0이면, 아무 포인터 하나만 1 이동 시키고 이어서 탐색한다.
    - 갭이 그동안의 min_gap과 같으면, cnt +1 해준 뒤
        앞선 경우와 동일하게 이어서 진행한다.
    - 갭이 그동안의 min_gap보다 크면, 다른 작업 없이
        앞선 경우와 동일하게 이어서 진행한다.
'''


import sys
input = sys.stdin.readline
INF = int(2e9)

T = int(input())
for _ in range(T) :
    N, k = map(int, input().split())
    nums = sorted(list(map(int, input().split())))

    min_gap = INF
    cnt = 0
    #투 포인터 탐색
    left, right = 0, N-1
    while left &amp;lt; right :
        res = nums[left] + nums[right]

        gap = k - res
        gap_abs = abs(gap)

        #갭 차이에 따른 min_gap, cnt 처리
        if gap_abs &amp;lt; min_gap :
            min_gap = gap_abs
            cnt = 1
        elif gap_abs == min_gap :
            cnt += 1
        
        #갭의 부호에 따른 포인터 이동
        if gap &amp;lt;= 0 : 
            right -= 1 #현재 합이 k보다 큰 경우 큰 쪽 포인터를 -1 이동 (gap==0일 경우 아무 포인트 하나만 1 이동시키고 다음 탐색)
        elif 0 &amp;lt; gap :
            left += 1 #현재 합이 k보다 작은 경우 작은 쪽 포인터를 +1 이동
    
    print(cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;코드 - 이진탐색&lt;/h3&gt;
&lt;pre id=&quot;code_1716891411500&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# v1 : 정렬, 이진탐색
#직전에 푼 3273과 매우 유사한 문제!
# TS : 테케 통과, 시간초과.. pypy는 통과!!!
#       어떻게 시간을 더 줄이지?
#       -&amp;gt; 이진탐색 문제는 투 포인터로도 풀 수 있다!

'''
[문제 해석]
- 주어진 서로다른 수들 중 두 수의 합이 특정 수k에 가장 가까운 조합의 수
- 숫자들은 -10^8 ~ 10^8(1억) 사이 정수. 숫자의 개수n는 1,000,000개 이하
- 시간제한 : 1초
- 답 : 합이 k에 가장 가까운 두 수 조합의 개수

[예제]
10 4
-7 9 2 -4 12 1 5 -3 -2 0
정렬 -&amp;gt; [-7, -4, -3, -2, 0, 1, 2, 5, 9, 12]



구상
- 2천만 연산 내에 짜야 함. n^2이면 시간초과
- 3273번처럼 정렬 + 이진탐색 으로 짜면 될까? nlogn
- 탐색 풀이
    - 작은 쪽 수 하나 고정
    - 고정수보다 더 큰 쪽 범위 이진탐색
    - 두 수의 합res와 k의 갭이 그동안의 min_gap보다 작으면, min_gap갱신 &amp;amp; 조합수cnt초기화 후 +1 해준 뒤
        갭이 +인지 -인지에 따라 큰 쪽 수를 더 키울지 줄일지 정해 이진탐색 범위를 조정하여 이번 이진탐색을 이어서 진행한다.
        이때 갭이 0이면, 더 탐색하지 않는다.
    - 갭이 그동안의 min_gap과 같으면, cnt +1 해준 뒤
        앞선 경우와 동일하게 이진탐색 범위를 조정하여 이어서 진행한다.
    - 갭이 그동안의 min_gap보다 크면, 다른 작업 없이
        앞선 경우와 동일하게 이진탐색 범위를 조정하여 이어서 진행한다.
왜 골드이려나 . .
'''


import sys
input = sys.stdin.readline
INF = int(2e9)

T = int(input())
for _ in range(T) :
    N, k = map(int, input().split())
    nums = sorted(list(map(int, input().split())))

    min_gap = INF
    cnt = 0
    #완전 탐색
    for i in range(N) :
        #이진 탐색
        start, end = i+1, N-1
        while start &amp;lt;= end :
            mid = (start + end) // 2
            res = nums[i] + nums[mid]

            gap = k - res
            gap_abs = abs(gap)

            #갭 차이에 따른 min_gap, cnt 처리
            if gap_abs &amp;lt; min_gap :
                min_gap = gap_abs
                cnt = 1
            elif gap_abs == min_gap :
                cnt += 1
            
            #갭의 부호에 따른 이진탐색 범위 처리
            if gap &amp;lt; 0 : 
                end = mid - 1 #현재 합이 k보다 큰 것이므로 작은 쪽으로 범위 이동
            elif gap == 0:
                break #이진탐색 중지
            elif 0 &amp;lt; gap :
                start = mid + 1 #현재 합이 k보다 작은 것이므로 큰 쪽으로 범위 이동
    
    print(cnt)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DSA/Algorithm</category>
      <category>백준</category>
      <category>알고리즘</category>
      <category>이진탐색</category>
      <category>투 포인터</category>
      <author>돌래씨</author>
      <guid isPermaLink="true">https://onedaythreecoding.tistory.com/201</guid>
      <comments>https://onedaythreecoding.tistory.com/entry/%EB%B0%B1%EC%A4%80-Gol5-%ED%88%AC-%ED%8F%AC%EC%9D%B8%ED%84%B0%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89-9024-%EB%91%90-%EC%88%98%EC%9D%98-%ED%95%A9-Python#entry201comment</comments>
      <pubDate>Tue, 28 May 2024 19:17:18 +0900</pubDate>
    </item>
  </channel>
</rss>