<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>공부 기록</title>
    <link>https://guiyum.tistory.com/</link>
    <description>컴공 졸업생</description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 16:41:49 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>너나나</managingEditor>
    <image>
      <title>공부 기록</title>
      <url>https://tistory1.daumcdn.net/tistory/4353046/attach/4fbead28b4364043b5c78da3f3dfa2b1</url>
      <link>https://guiyum.tistory.com</link>
    </image>
    <item>
      <title>1년 만에 쓰는 글</title>
      <link>https://guiyum.tistory.com/144</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;블로그에 글을 마지막으로 썼던게 22년 5월 14일 코테준비한다고 알고리즘 올렸던거니까 벌써 1년이 지났다!!!!!!&lt;br&gt;대학교 2학년때 그냥 공부 정리해볼까!! 하는 마음으로 만들었던 블로그에 개인 공부하는 것도 올리고, 학교 수업 들은것도 정리하고 하면서 많은 도움을 받았었다!!!!!!!&lt;br&gt;&amp;nbsp;&lt;br&gt;결론부터 말하면 작년 여름 졸업하기 전에 취뽀를 하게 되면서 지금은 회사를 잘 다니고 있다!!!!&lt;br&gt;금융쪽에 디지털 직군으로 입사를 했고 이제 거의 1년쯤 됐다!!!!!!!&lt;br&gt;유관부서 배치 받기 전까지는 it와는 관련 없는 일을 해야 해서 슬슬 기억도 잘 안나고,, 공부의 필요성을 느끼고 있다!!!!!!&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서 공부했던거 다 까먹기 전에 자기 개발겸!!! 다시 공부를 시작하고 블로그에 정리를 하려고 한다!!!&lt;br&gt;목표는 한 달에 한번이라도 뭐라도 올리기!!!!!!!&lt;br&gt;&amp;nbsp;&lt;br&gt;퇴근하면 피곤해서 공부할 정신이 없긴 하지만!!! 그래도 열심히 해보자!!!!!!!!&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM3diy/btsgC22TYuK/OesNqr8InkEBW5kwRSzwc0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM3diy/btsgC22TYuK/OesNqr8InkEBW5kwRSzwc0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM3diy/btsgC22TYuK/OesNqr8InkEBW5kwRSzwc0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM3diy%2FbtsgC22TYuK%2FOesNqr8InkEBW5kwRSzwc0%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;3024&quot; height=&quot;4032&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;</description>
      <category>2023/잡담</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/144</guid>
      <comments>https://guiyum.tistory.com/144#entry144comment</comments>
      <pubDate>Sat, 20 May 2023 13:25:53 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 네트워크 C++</title>
      <link>https://guiyum.tistory.com/143</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/43162&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/43162&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652501965224&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;코딩테스트 연습 - 네트워크&quot; data-og-description=&quot;네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/43162&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/43162&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bZ8ZtM/hyOoL64L1C/KkNAphbcXBtaBwbdVzVoek/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwjoRI/hyOoCCg87C/PEoClBVnlaKrbZz26HyHIk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/BkMv3/hyOoC3kHbQ/VXbtMZpSancsxnfvkjA2oK/img.png?width=714&amp;amp;height=622&amp;amp;face=0_0_714_622&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/43162&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/43162&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bZ8ZtM/hyOoL64L1C/KkNAphbcXBtaBwbdVzVoek/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bwjoRI/hyOoCCg87C/PEoClBVnlaKrbZz26HyHIk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/BkMv3/hyOoC3kHbQ/VXbtMZpSancsxnfvkjA2oK/img.png?width=714&amp;amp;height=622&amp;amp;face=0_0_714_622');&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;코딩테스트 연습 - 네트워크&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;네트워크란 컴퓨터 상호 간에 정보를 교환할 수 있도록 연결된 형태를 의미합니다. 예를 들어, 컴퓨터 A와 컴퓨터 B가 직접적으로 연결되어있고, 컴퓨터 B와 컴퓨터 C가 직접적으로 연결되어 있&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2Qd68/btrB582E6cQ/8qiBuZ46MmTDb6dxx4uzx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2Qd68/btrB582E6cQ/8qiBuZ46MmTDb6dxx4uzx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2Qd68/btrB582E6cQ/8qiBuZ46MmTDb6dxx4uzx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2Qd68%2FbtrB582E6cQ%2F8qiBuZ46MmTDb6dxx4uzx1%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;714&quot; height=&quot;622&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 2개의 네트워크가 있는거(1-2, 3)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XpIjA/btrB67hDhvD/wq9XhUSKeaWZWiIRRw2881/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XpIjA/btrB67hDhvD/wq9XhUSKeaWZWiIRRw2881/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XpIjA/btrB67hDhvD/wq9XhUSKeaWZWiIRRw2881/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXpIjA%2FbtrB67hDhvD%2Fwq9XhUSKeaWZWiIRRw2881%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;732&quot; height=&quot;592&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 1개의 네트워크가 있는거(1-2-3)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결된 네트워크까지 쭉 dfs로 탐색하고 아직 탐색도 안했고 연결도 안됐으면 dfs가 다시 실행되니까 그 새로 실행되는 dfs가 몇 번인지 알면 된다!!&lt;/p&gt;
&lt;pre id=&quot;code_1652501941409&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;

using namespace std;

int check[200]; // 노드에 방문했는지 안했는지 체크

void dfs(int x, int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; computers) {
    for(int i = 0; i &amp;lt; n; i++) {
        check[x] = 1; // 방문한거 표시
        if(!check[i] &amp;amp;&amp;amp; computers[x][i]) {
            check[i] = 1; // 방문한거 표시
            dfs(i, n, computers);
        }
    }
    return;
}

int solution(int n, vector&amp;lt;vector&amp;lt;int&amp;gt;&amp;gt; computers) {
    int answer = 0;
    for(int i = 0; i &amp;lt; n; i++) {
        if(!check[i]) {
            dfs(i, n, computers);
            answer++;
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>study/알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/143</guid>
      <comments>https://guiyum.tistory.com/143#entry143comment</comments>
      <pubDate>Sat, 14 May 2022 13:23:08 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 더 맵게 C++</title>
      <link>https://guiyum.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42626&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652325251792&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;코딩테스트 연습 - 더 맵게&quot; data-og-description=&quot;매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ncDK5/hyOmxopL9H/RRwLNwRk9vj9Ca25H0AWU1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ElkDa/hyOnMj05MW/h7RSwKyMot3nYiEinqkKQk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42626&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ncDK5/hyOmxopL9H/RRwLNwRk9vj9Ca25H0AWU1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/ElkDa/hyOnMj05MW/h7RSwKyMot3nYiEinqkKQk/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - 더 맵게&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;매운 것을 좋아하는 Leo는 모든 음식의 스코빌 지수를 K 이상으로 만들고 싶습니다. 모든 음식의 스코빌 지수를 K 이상으로 만들기 위해 Leo는 스코빌 지수가 가장 낮은 두 개의 음식을 아래와 같&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;우선순위큐&lt;/span&gt;를 사용한다면 어렵지 않은 문제!!!!&lt;/p&gt;
&lt;pre id=&quot;code_1652325234273&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;queue&amp;gt;
using namespace std;

int solution(vector&amp;lt;int&amp;gt; scoville, int K) {
    int answer = 0;
    priority_queue&amp;lt;int, vector&amp;lt;int&amp;gt;, greater&amp;lt;int&amp;gt;&amp;gt; pq;
    for(int i: scoville) { 
        pq.push(i);
    }
    while(pq.top() &amp;lt; K) {
        if(pq.size() == 1) return -1; // 스코빌 지수를 넘지 않았는데 사이즈가 1이라면 스코빌지수 K이상으로 만들 수 x
        int a = pq.top();
        pq.pop();
        pq.push(a + pq.top()*2);
        pq.pop();
        answer++;
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>study/알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/142</guid>
      <comments>https://guiyum.tistory.com/142#entry142comment</comments>
      <pubDate>Thu, 12 May 2022 12:14:30 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 기능개발 C++</title>
      <link>https://guiyum.tistory.com/141</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42586&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652235702271&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;코딩테스트 연습 - 기능개발&quot; data-og-description=&quot;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다. 또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는 &quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ssavX/hyOk1pVNF3/OAo0HJH6LQJSCLZ3KhAqCK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/JIJI3/hyOmzL6QsH/iiKLKteynmK7glfzEZYsE0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42586&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ssavX/hyOk1pVNF3/OAo0HJH6LQJSCLZ3KhAqCK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/JIJI3/hyOmzL6QsH/iiKLKteynmK7glfzEZYsE0/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - 기능개발&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로그래머스 팀에서는 기능 개선 작업을 수행 중입니다. 각 기능은 진도가 100%일 때 서비스에 반영할 수 있습니다. 또, 각 기능의 개발속도는 모두 다르기 때문에 뒤에 있는 기능이 앞에 있는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;pre id=&quot;code_1652235682513&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;cmath&amp;gt;
#include &amp;lt;iostream&amp;gt;
using namespace std;

vector&amp;lt;int&amp;gt; solution(vector&amp;lt;int&amp;gt; progresses, vector&amp;lt;int&amp;gt; speeds) {
    vector&amp;lt;int&amp;gt; answer;
    vector&amp;lt;int&amp;gt; period; // 빠져나갈때까지 걸리는 시간 넣자

    for(int i = 0; i &amp;lt; progresses.size(); i++) {
        period.push_back(ceil((float)(100 - progresses[i])/speeds[i]));
    }
    int count = 1;
    int base = period[0];
    for(int i = 0; i &amp;lt; progresses.size() - 1; i++) {
        if(base &amp;gt;= period[i + 1]) {
            count++;
            continue;
        }
        else {
            answer.push_back(count);
            base = period[i + 1];
            count = 1;
        }
    }
    answer.push_back(count);
    return answer;
}&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;항상 기준은 배포가 안된 제일 첫번째껄로 해주기 위해서 배포가 되면 base를 period[i+1]로 바꾸면서 기준을 바꿨다!!&lt;/p&gt;</description>
      <category>study/알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/141</guid>
      <comments>https://guiyum.tistory.com/141#entry141comment</comments>
      <pubDate>Wed, 11 May 2022 11:24:04 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 완주하지 못한 선수 C++</title>
      <link>https://guiyum.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/42576&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652228995665&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;코딩테스트 연습 - 완주하지 못한 선수&quot; data-og-description=&quot;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다. 마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VEfPU/hyOmAxcGN8/5shLEbQ9wnAxYOnqMkTCKK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bomD09/hyOk1wqevl/vQNtJD6n7QpM8MzXkuBdYK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/42576&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VEfPU/hyOmAxcGN8/5shLEbQ9wnAxYOnqMkTCKK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/bomD09/hyOk1wqevl/vQNtJD6n7QpM8MzXkuBdYK/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626');&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;코딩테스트 연습 - 완주하지 못한 선수&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다. 마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참여자중에 한 명만 완주를 못하고 나머지는 다 완주를 하니까 참여자, 완주자를 각각 정렬했을 때 같은 위치에 완주자랑 참여자 이름이 다를 때 그 참여자가 완주 못한 친구겠구만!!! 이라고 생각!!!&lt;/p&gt;
&lt;pre id=&quot;code_1652228939212&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;algorithm&amp;gt;
using namespace std;

string solution(vector&amp;lt;string&amp;gt; participant, vector&amp;lt;string&amp;gt; completion) {
    string answer = &quot;&quot;;
    sort(participant.begin(), participant.end());
    sort(completion.begin(), completion.end());
    for(int i = 0; i &amp;lt; participant.size(); i++){
        if(participant[i] != completion[i]){
            answer = participant[i];
            break;
        }
    }
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 맞긴 한데 문제가 해시를 이용해서 풀기를 바라는 애라 다른사람 풀이 보면서 해시도 사용해봤다!!&lt;/p&gt;
&lt;pre id=&quot;code_1652229592659&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;unordered_set&amp;gt;
using namespace std;

string solution(vector&amp;lt;string&amp;gt; participant, vector&amp;lt;string&amp;gt; completion) {
    unordered_multiset&amp;lt;string&amp;gt; names;
    for(int i = 0; i &amp;lt; participant.size(); i++) {
        names.insert(participant[i]);
    }
    for(int i = 0; i &amp;lt; completion.size(); i++) {
        unordered_multiset&amp;lt;string&amp;gt;::iterator iter = names.find(completion[i]);
        names.erase(iter);
    }
    
    return *names.begin();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>study/알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/140</guid>
      <comments>https://guiyum.tistory.com/140#entry140comment</comments>
      <pubDate>Wed, 11 May 2022 09:42:11 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 숫자 문자열과 영단어 C++</title>
      <link>https://guiyum.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/81301&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://programmers.co.kr/learn/courses/30/lessons/81301&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1652078415296&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;코딩테스트 연습 - 숫자 문자열과 영단어&quot; data-og-description=&quot;네오와 프로도가 숫자놀이를 하고 있습니다. 네오가 프로도에게 숫자를 건넬 때 일부 자릿수를 영단어로 바꾼 카드를 건네주면 프로도는 원래 숫자를 찾는 게임입니다. 다음은 숫자의 일부 자&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81301&quot; data-og-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81301&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/eU64x/hyOkUJr9sx/Uy5S5JEQrsRSzD21jjUmq1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/dQxLjY/hyOkTDL2zu/l9NdRmnqpnhTRAFCK0Yq91/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/qKd06/hyOiZyWJaB/e3rh7nCSsya2ZtsiK43m21/img.png?width=1000&amp;amp;height=310&amp;amp;face=0_0_1000_310&quot;&gt;&lt;a href=&quot;https://programmers.co.kr/learn/courses/30/lessons/81301&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://programmers.co.kr/learn/courses/30/lessons/81301&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/eU64x/hyOkUJr9sx/Uy5S5JEQrsRSzD21jjUmq1/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/dQxLjY/hyOkTDL2zu/l9NdRmnqpnhTRAFCK0Yq91/img.jpg?width=626&amp;amp;height=626&amp;amp;face=0_0_626_626,https://scrap.kakaocdn.net/dn/qKd06/hyOiZyWJaB/e3rh7nCSsya2ZtsiK43m21/img.png?width=1000&amp;amp;height=310&amp;amp;face=0_0_1000_310');&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;코딩테스트 연습 - 숫자 문자열과 영단어&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;네오와 프로도가 숫자놀이를 하고 있습니다. 네오가 프로도에게 숫자를 건넬 때 일부 자릿수를 영단어로 바꾼 카드를 건네주면 프로도는 원래 숫자를 찾는 게임입니다. 다음은 숫자의 일부 자&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;one4sevneeight -&amp;gt; 1478&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;23four5six7 -&amp;gt; 234567&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1zerotwozero3 -&amp;gt; 10203&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 영어로 된 친구들까지 숫자로 바꿔주는 문제다!!&lt;/p&gt;
&lt;pre id=&quot;code_1652078404168&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;map&amp;gt;
using namespace std;

map&amp;lt;string, int&amp;gt; number = {{&quot;zero&quot;, 0}, {&quot;one&quot;, 1}, {&quot;two&quot;, 2}, {&quot;three&quot;, 3}, {&quot;four&quot;, 4}, {&quot;five&quot;, 5}, {&quot;six&quot;, 6}, {&quot;seven&quot;, 7}, {&quot;eight&quot;, 8}, {&quot;nine&quot;, 9}};
map&amp;lt;string, int&amp;gt;::iterator iter;

int solution(string s) {
    int answer = 0;
    for(iter = number.begin(); iter != number.end(); iter++) {
        int location = s.find(iter-&amp;gt;first);
        if(location != -1) {
            s.replace(location, (iter-&amp;gt;first).length(), to_string(iter-&amp;gt;second));
        }
        if(s.find(iter-&amp;gt;first) != -1) {
            iter--;
        }
    }
    answer = stoi(s);
    return answer;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 무식하게 풀었다!!!! map을 이용해서 각 문자열과 숫자 쌍을 만들었고 iterator로 각 맵을 순회하면서 해당하는 문자열이 있으면 해당 숫자로 바꿔주게 구현했다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 if(s.find(iter-&amp;gt;first) != -1) { iter--; } 저 부분을 안적으니까 해당 문자열의 첫번째 인덱스만 반환해주는 replace 특징때문에 1zerotwozero3 -&amp;gt; 10203 이렇게 해당하는 문자열이 2개 이상 있는 경우를 처리를 못해서 테스트케이스를 통과하지 않았다!!!&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;저렇게 풀고 다른사람들 풀이 봤는데 regex_replace(해당 문자열, regex(정규식), 치환 문자열) 함수를 사용해서 간단하게 풀 수 있었다!!!&lt;/p&gt;
&lt;pre id=&quot;code_1652078909029&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;string&amp;gt;
#include &amp;lt;regex&amp;gt;
using namespace std;

int solution(string s){
    int answer = 0;
    s = regex_replace(s, regex(&quot;zero&quot;), &quot;0&quot;);
    s = regex_replace(s, regex(&quot;one&quot;), &quot;1&quot;);
    s = regex_replace(s, regex(&quot;two&quot;), &quot;2&quot;);
    s = regex_replace(s, regex(&quot;three&quot;), &quot;3&quot;);
    s = regex_replace(s, regex(&quot;four&quot;), &quot;4&quot;);
    s = regex_replace(s, regex(&quot;five&quot;), &quot;5&quot;);
    s = regex_replace(s, regex(&quot;six&quot;), &quot;6&quot;);
    s = regex_replace(s, regex(&quot;seven&quot;), &quot;7&quot;);
    s = regex_replace(s, regex(&quot;eight&quot;), &quot;8&quot;);
    s = regex_replace(s, regex(&quot;nine&quot;), &quot;9&quot;);
    return stoi(s);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 유용한 친구들이 많은데 여러 개 찾아봐야겠다는 생각이 들었땅&lt;/p&gt;</description>
      <category>study/알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/139</guid>
      <comments>https://guiyum.tistory.com/139#entry139comment</comments>
      <pubDate>Mon, 9 May 2022 15:49:24 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 이진 탐색트리</title>
      <link>https://guiyum.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://guiyum.tistory.com/129&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;2022.01.14 - [2022/자료구조 + 알고리즘] - [자료구조] 이진 트리&lt;/span&gt;&lt;/a&gt; 참고하면 좋다!!!&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[자료구조] 이진 트리&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;이진 트리 : 모든 노드가 최대로 두 개의 자식 노드를 갖는 순서화된 트리 각 노드가 최대 2개의 자식을 갖는다. 각 노드가 자식을 왼쪽 자식 또는 오른쪽 자식으로 분류한다. 왼쪽 자식이 오른쪽&quot; data-og-host=&quot;guiyum.tistory.com&quot; data-og-source-url=&quot;https://guiyum.tistory.com/129&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bXhOhB/hyM9tNxsiQ/GG4pKcjKTqCetVkHMukKV0/img.png?width=800&amp;amp;height=508&amp;amp;face=0_0_800_508,https://scrap.kakaocdn.net/dn/bckONc/hyM9m1Vtqf/628Odeo9pC2X0Ezkwwu9mK/img.png?width=800&amp;amp;height=508&amp;amp;face=0_0_800_508,https://scrap.kakaocdn.net/dn/c8d2x2/hyM9pK7EZi/irVZUYOo0JhKH50DsNWHG1/img.png?width=2605&amp;amp;height=1000&amp;amp;face=0_0_2605_1000&quot; data-og-url=&quot;https://guiyum.tistory.com/129&quot;&gt;
 &lt;a href=&quot;https://guiyum.tistory.com/129&quot; target=&quot;_blank&quot; data-source-url=&quot;https://guiyum.tistory.com/129&quot;&gt;
  &lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bXhOhB/hyM9tNxsiQ/GG4pKcjKTqCetVkHMukKV0/img.png?width=800&amp;amp;height=508&amp;amp;face=0_0_800_508,https://scrap.kakaocdn.net/dn/bckONc/hyM9m1Vtqf/628Odeo9pC2X0Ezkwwu9mK/img.png?width=800&amp;amp;height=508&amp;amp;face=0_0_800_508,https://scrap.kakaocdn.net/dn/c8d2x2/hyM9pK7EZi/irVZUYOo0JhKH50DsNWHG1/img.png?width=2605&amp;amp;height=1000&amp;amp;face=0_0_2605_1000')&quot;&gt; 
  &lt;/div&gt;
  &lt;div class=&quot;og-text&quot;&gt;
   &lt;p class=&quot;og-title&quot;&gt;[자료구조] 이진 트리&lt;/p&gt;
   &lt;p class=&quot;og-desc&quot;&gt;이진 트리 : 모든 노드가 최대로 두 개의 자식 노드를 갖는 순서화된 트리 각 노드가 최대 2개의 자식을 갖는다. 각 노드가 자식을 왼쪽 자식 또는 오른쪽 자식으로 분류한다. 왼쪽 자식이 오른쪽&lt;/p&gt;
   &lt;p class=&quot;og-host&quot;&gt;guiyum.tistory.com&lt;/p&gt;
  &lt;/div&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이진 탐색트리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 트리 T의 각 내부 노드 v가 엔트리 (k, x)를 저장하는 이진 트리이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;v의 왼쪽 서브트리에 있는 노드들에 저장된 키들은 k보다 작거나 같다.&lt;/li&gt;
 &lt;li&gt;v의 오른쪽 서브트리에 있는 노드들에 저장된 키들은 k보다 크거나 같다.&lt;/li&gt;
&lt;/ul&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7YVGT/btrrdnoIWaz/bv1nn0BvgKItcsUHXdtFSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7YVGT/btrrdnoIWaz/bv1nn0BvgKItcsUHXdtFSK/img.png&quot; data-alt=&quot; 정수 키들을 가진 맵을 나타내는 이진 탐색트리 T &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7YVGT/btrrdnoIWaz/bv1nn0BvgKItcsUHXdtFSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7YVGT%2FbtrrdnoIWaz%2Fbv1nn0BvgKItcsUHXdtFSK%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;1508&quot; height=&quot;1000&quot; data-origin-width=&quot;1508&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 정수 키들을 가진 맵을 나타내는 이진 탐색트리 T &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;T의 노드들에 저장된 키들은 일련의 내부 노드들에서 비교를 함으로써 탐색을 수행하는 방법을 제공하고 있다. 현재 노드 v에서 탐색이 중단되거나 왼쪽, 오른쪽 자식 노드에서 탐색이 계속될 수 있다. 따라서 여기서는 이진 탐색트리를 포화 이진트리(proper binary tree: 자식이 0 또는 2)로 생각한다. 이진 탐색 트리의 내부 노드에만 엔트리를 저장하고 외부 노드는 placeholder(자리 소유자)의 역할만 한다. 이렇게 하면 탐색과 갱신 알고리즘을 단순하게 한다. 비포화 이진 탐트리는 공간 사용율을 더 좋게 할 순 있지만 복잡한 탐색과 갱신함수를 사용하게 한다. &lt;br&gt;이진 탐색트리의 중요한 특성은 순서 맵(또는 딕셔너리)의 실현이다. 즉 이진 탐색트리는 계층적으로 부모와 자식 간의 관계를 이용하여 키들의 순서를 나타내어야 한다. 이진 탐색트리 T의 노드들에 대한 중위 순회는 감소하지 않는 순서로 키들을 방문해야 한다.&lt;br&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;탐색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 탐색트리 T로 나타낸 맵 M에서 find(k) 연산을 수행하기 위해서 트리 T를 의사 결정 트리(decision tree)로 생각한다.&lt;br&gt;T의 각 내부 노드 v에서 탐색 키 k가 노드 v에 저장된 키(key(v)로 나타냄)보다 작은가, 같은가, 큰가에 대해 물어본다. &lt;br&gt;답이 &quot;더 작다&quot;이면 탐색을 왼쪽 서브트리에서 계속 한다. &quot;같다&quot;이면 탐색은 성공적으로 종료된다. &quot;보다 크다&quot;이면 탐색은 오른쪽 서브트리에서 계속 된다. 마지막으로 외부 노드에 도달하게 되면 탐색은 성공하지 못하고 종료된다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2702&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oHV59/btrrcHOvLyd/0ml7nYOMc1AT3haDHv1YW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oHV59/btrrcHOvLyd/0ml7nYOMc1AT3haDHv1YW0/img.png&quot; data-alt=&quot; (a) 정수 키들을 가진 맵을 나타내는 이진 탐색트리 T, (b) M에서 find(76) (성공)과 find(25) (실패) 연산을 실행했을 때 방문한 T의 노드들 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oHV59/btrrcHOvLyd/0ml7nYOMc1AT3haDHv1YW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoHV59%2FbtrrcHOvLyd%2F0ml7nYOMc1AT3haDHv1YW0%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;2702&quot; height=&quot;1000&quot; data-origin-width=&quot;2702&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; (a) 정수 키들을 가진 맵을 나타내는 이진 탐색트리 T, (b) M에서 find(76) (성공)과 find(25) (실패) 연산을 실행했을 때 방문한 T의 노드들 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 알고리즘으로 나탄보자. 탐색 키 k와 T의 노드가 주어지면 이 함수(TreeSearch)는 v를 루트로 하는 T의 서브트리 T(v)의 노드(위치) w를 리턴하는데 다음 중 한 가지가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;w는 내부 노드이고 w의 엔트리는 k와 같은 키를 가진다.&lt;/li&gt;
 &lt;li&gt;w는 T(v)의 중위 순회에서 k의 위치를 나타내는 외부노드이나 k는 T(v)에 포함되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 find(k) 함수는 TreeSearch(k, T.root())를 호출하여 수행할 수 있다. 이 호출로 반환되는 T의 노드를 w라고 하자. 만약에 w가 내부 노드이면 w의 엔트리를 반환한다. 아니면 null을 반환한다.&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;html&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;Algorithm TreeSearch(k, v): if T.isExternal(v) then return null if k &amp;lt; key(v) then return TreeSearch(k, T.left(v)) else if k &amp;gt; key(v) then return TreeSearch(k, T.right(v)) return v {k = key(v)}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &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;insertAtExternal(v, e): 외부 노드 v에 요소 e를 삽입. v를 내부 노드로 확장하고 v는 새로운 (빈) 외부 자식 노드 가짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;html&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;Algorithm TreeInsert(k, x, v): input: 탐색키 k, 연계값 x, T의 노드 v output: 엔트리(k, x)를 저장하고 있는 서브트리 T(v) 내의 새로운 노드(w) w &amp;lt;- TreeSearch(k,v) if T.isInternal(w) then return TreeInsert(k, x, T.left(w)) 오른쪽으로 진행해도 상관없음 T.insertAtExternal(w, (k,x)) return w&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 알고리즘은 T의 루트로부터 새로운 엔트리를 수용하는 새로운 내부 노드로 확장된 외부노드까지의 경로를 따라간다(아래 그림 참고).&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2380&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTYz7R/btrrcPeVu35/6kSshrJtLnua0mKIEbFLxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTYz7R/btrrcPeVu35/6kSshrJtLnua0mKIEbFLxK/img.png&quot; data-alt=&quot; 이 앞의 그림의 탐색 트리에 키 78을 갖는 엔트리 삽입: (a) 삽입할 위치 찾기 (b) 결과 트리 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTYz7R/btrrcPeVu35/6kSshrJtLnua0mKIEbFLxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTYz7R%2FbtrrcPeVu35%2F6kSshrJtLnua0mKIEbFLxK%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;2380&quot; height=&quot;1000&quot; data-origin-width=&quot;2380&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 이 앞의 그림의 탐색 트리에 키 78을 갖는 엔트리 삽입: (a) 삽입할 위치 찾기 (b) 결과 트리 &lt;/figcaption&gt;
&lt;/figure&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;removeAboveExternal(v): 외부 노드 v와 부모 노드를 제거하고 v의 부모 노드를 v의 형제 노드로 대체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 연산이 주어지면, 키값이 k인 엔트리를 저장하고 있는 T의 한 노드를 찾기 위해 T에 대해서 TreeSearch(k, T.root())를 호출하여 맵 ADT의 erase(k) 연산 구현을 시작한다. TreeSearch가 외부 노드를 반환하면 맵 M에 키 k를 가진 엔트리가 없는 것이며 따라서 오류 신호를 보낸다. 대신에 TreeSearch가 내부 노드 w를 반환하면 w는 제거하고자 하는 엔트리를 저장하고 있는 것이며 다음과 같이 두 경우를 구분한다(아래 그림 참고).&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2223&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q3O6s/btrrcNhcWLu/I6f7bIdsOAQBK9U0SUJ5l1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q3O6s/btrrcNhcWLu/I6f7bIdsOAQBK9U0SUJ5l1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q3O6s/btrrcNhcWLu/I6f7bIdsOAQBK9U0SUJ5l1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq3O6s%2FbtrrcNhcWLu%2FI6f7bIdsOAQBK9U0SUJ5l1%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;2223&quot; height=&quot;999&quot; data-origin-width=&quot;2223&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;노드 w의 자식 중 하나가 외부 노드이면(그림의 노드 z), Tㄴ에 대하여 removeAboveExternal(z) 연산을 수행하여 T로부터 간단히 w와 z를 제거한다. 이 연산은 w를 z의 형제 노드로 대체하고 w와 z를 T에서 제거함으로써 T를 재구성&lt;/li&gt;
 &lt;li&gt;w의 두 자식 노드가 내부 노드이면 T로부터 노드 w를 간단히 제거할 수 없음. 다음과 같이 진행(아래 사진 참고)&lt;/li&gt;
&lt;/ul&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2450&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmM6Fx/btrrhFib2NL/sywTVQpz5yBWy4NoKXePIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmM6Fx/btrrhFib2NL/sywTVQpz5yBWy4NoKXePIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmM6Fx/btrrhFib2NL/sywTVQpz5yBWy4NoKXePIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmM6Fx%2FbtrrhFib2NL%2FsywTVQpz5yBWy4NoKXePIk%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;2450&quot; height=&quot;1000&quot; data-origin-width=&quot;2450&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;T의 중위 순회로 w다음에 오는 첫 번째 내부 노드 y를 찾는다. 노드 y는 w의 오른쪽 서브트리에서 가장 왼쪽에 있는 내부 노드이고 w의 오른쪽 자식 노드로 먼저 가고 거기서 T의 왼쪽 자식 노드를 따라 내려간다. 또한, y의 왼쪽 자식 x는 T의 중위 순회에서 노드 w 바로 다음에 오는 외부 노드이다.&lt;/li&gt;
 &lt;li&gt;y의 엔트리를 w로 이동한다. 이러한 실행은 w에 저장되었던 엔트리를 삭제하는 효과를 갖는다&lt;/li&gt;
 &lt;li&gt;T에 대한 removeAboveExternal(x)를 호출하여 T로부터 x와 y 노드를 삭제한다. 이러한 실행은 y를 x의 형제로 대체하고 T로 부터 x와 y를 삭제한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 이진 탐색트리의 성능&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탐색, 삽입, 삭제 알고리즘의 분석은 유사하다. 방문한 각 노드에서 O(1) 시간을 소비하고 최악의 경우에 방문한 노드의 수는 T의 높이 h에 비례한다. 따라서 이진 탐색트리 T로 구현된 맵 M에서 find, insert, erase 함수는 h가 T의 높이일 때 O(h) 시간에 수행된다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4x8jP/btrrcua8RXB/05ppFPYOa4FwdOHdWrGcRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4x8jP/btrrcua8RXB/05ppFPYOa4FwdOHdWrGcRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4x8jP/btrrcua8RXB/05ppFPYOa4FwdOHdWrGcRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4x8jP%2Fbtrrcua8RXB%2F05ppFPYOa4FwdOHdWrGcRK%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;197&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;참고 문헌&lt;/i&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;[1] Michael T. Goodrich, Roberto Tamassia, Davaid M. Mount, C++로 구현하는 자료구조와 알고리즘, WILEY, 2013.&lt;/span&gt;&lt;/p&gt;</description>
      <category>2022/자료구조 + 알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/136</guid>
      <comments>https://guiyum.tistory.com/136#entry136comment</comments>
      <pubDate>Thu, 20 Jan 2022 16:11:17 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 이진 검색</title>
      <link>https://guiyum.tistory.com/135</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;정렬된 맵&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵의 엔트리들을 어떤 전체 순서(total order)에 따라 정렬하고, 이렇게 정렬된 순서에 따라 키와 값을 검색하는 정렬된 맵을 사용할 필요가 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;firstEntry(k): 가장 작은 키값을 가진 엔트리에 대한 반복자를 반환. 맵이 비었다면 end를 반환&lt;/li&gt;
 &lt;li&gt;lastEntry(k): 가장 큰 키값을 가진 엔트리에 대한 반복자를 반환. 맵이 비었다면 end 반환&lt;/li&gt;
 &lt;li&gt;ceilingEntry(k): k보다 크거나 같은 키값 중 가장 작은 키값을 가진 엔트리에 대한 반복자를 반환. 그런 엔트리가 없다면 end 반환&lt;/li&gt;
 &lt;li&gt;floorEntry(k): k보다 작거나 같은 키값 중 가장 큰 키값을 가진 엔트리에 대한 반복자 반환. 그러한 엔트리 없다면 end 반환&lt;/li&gt;
 &lt;li&gt;lowerEntry(k): k보다 작은 키값 중 가장 큰 키값을 가진 엔트리에 대한 반복자를 반환. 그러한 엔트리 없다면 end 반환&lt;/li&gt;
 &lt;li&gt;higherEntry(k): k보다 큰 키값 중 가장 작은 키값을 가진 엔트리에 대한 반복자 반환. 그러한 엔트리 없다면 end 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GXvP8/btrq9WZduxu/5QTHVkmvSVBuBun57xL3H0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GXvP8/btrq9WZduxu/5QTHVkmvSVBuBun57xL3H0/img.png&quot; data-alt=&quot; 정렬된 검색 테이블에 의한 맵 구현. 맵의 키만 표시된 것 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GXvP8/btrq9WZduxu/5QTHVkmvSVBuBun57xL3H0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGXvP8%2Fbtrq9WZduxu%2F5QTHVkmvSVBuBun57xL3H0%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;506&quot; height=&quot;78&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 정렬된 검색 테이블에 의한 맵 구현. 맵의 키만 표시된 것 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;만약 맵의 키들이 전체 순서(total order) 관계를 가지면, 우리는 맵의 엔트리들을 벡터 L에 키들이 증가하는 순서로 저장할 수 있다. 맵의 정렬된 벡터 구현을 &lt;b&gt;정렬된 검색 테이블&lt;/b&gt;(ordered search table)이라 부른다.&lt;br&gt;정렬된 검색 테이블의 요구 공간은 O(n)이다. 비정렬 리스트와는 다르게 검색 테이블의 수정은 시간이 많이 소요된다. 특히 검색 테이블에서 insert(k, v) 연산을 수행하는 데 최악의 경우 O(n) 시간이 필요하다. 새 엔트리 (k, v)가 들어갈 자리를 만들기 위해 벡터 내에 k보다 큰 키를 가진 모든 엔트리를 한 칸씩 움직여야 하기 때문이다. erase(k) 연산도 삭제된 엔트리가 남긴 빈자리를 채우기 위해 벡터 내에서 k보다 큰 키를 가진 모든 엔트리를 움직여야 한다. 따라서 검색 테이블 구현은 맵 수정 연산의 최악의 경우 실행 시간은 연결 리스트보다 나쁘다. 하지만 find 연산은 검색 테이블에서 훨씬 빠르게 수행된다.&lt;br&gt; &lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이진 검색&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 벡터 L을 이용하여 n개의 엔트리를 갖는 맵을 구현하는 방법의 장점은 L의 원소를 인덱스에 의해 O(1) 시간에 접근할 수 있다는 점이다. 벡터에서 어떤 원소의 인덱스는 그것보다 앞에 있는 원소의 개수이다. 따라서 L의 첫 번째 원소는 인덱스 0이고 마지막 원소는 인덱스 n - 1이다. 정렬된 검색 테이블에서 엔트리를 찾는 고전적인 알고리즘인 이진 검색(binary search)를 보자!!&lt;br&gt;L의 원소들은 맵의 엔트리들이다. 그리고 L이 정렬되어 있기 때문에 인덱스 i에 있는 엔트리는 인덱스 0, ..., i - 1에 있는 엔트리들보다 작지 않고 i + 1, ..., n - 1에 있는 엔트리들보다 크지 않다.&lt;br&gt; &lt;br&gt;그림으로 설명해본다!!!! 우리가 찾는 엔트리의 키를 k라고 하자. L에는 총 n개의 엔트리가 있다. &lt;br&gt;첫 번째 엔트리를 low, 마지막 엔트리를 high라고 지정했다!!&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWwEG/btrrb7eYMjk/l8K1GVEFpjKqj3DD4fzIRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWwEG/btrrb7eYMjk/l8K1GVEFpjKqj3DD4fzIRK/img.png&quot; data-alt=&quot; 초기 상태 &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWwEG/btrrb7eYMjk/l8K1GVEFpjKqj3DD4fzIRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWwEG%2Fbtrrb7eYMjk%2Fl8K1GVEFpjKqj3DD4fzIRK%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;1440&quot; height=&quot;351&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; 초기 상태 &lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리는 키 값이 k인 엔트리를 찾을 것이다. 이 배열의 중간 인덱스는 ⌊(low+high)/2⌋이다.&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;378&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l41g0/btrq7Vs2siH/obPwKt5SgINKyPN3fnQDpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l41g0/btrq7Vs2siH/obPwKt5SgINKyPN3fnQDpK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l41g0/btrq7Vs2siH/obPwKt5SgINKyPN3fnQDpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl41g0%2Fbtrq7Vs2siH%2FobPwKt5SgINKyPN3fnQDpK%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;1440&quot; height=&quot;378&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;378&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;저 중간 인덱스를 mid라고 하자. mid를 기준으로 왼쪽들은 mid보다 키값이 작고 오른쪽은 mid보다 키 값이 클것이다. 따라서 mid.key()와 k값을 비교하여 k 값이 크면 low를 mid 다음으로 옮기고, k값이 작다면 high를 mid의 왼쪽으로 옮긴다. 같다면 mid에서 엔트리를 찾을 수 있다.&lt;br&gt;그림으로 보자!!&lt;br&gt;만약 mid.key값이 k값이 크다면&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H6Dbj/btrrctB3kLV/gvu6t90oKtzOce4isXGKD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H6Dbj/btrrctB3kLV/gvu6t90oKtzOce4isXGKD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H6Dbj/btrrctB3kLV/gvu6t90oKtzOce4isXGKD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH6Dbj%2FbtrrctB3kLV%2Fgvu6t90oKtzOce4isXGKD1%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;1440&quot; height=&quot;408&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #66f8ff;&quot;&gt; &lt;/span&gt; 이 색깔이 칠해져 있는 배열만 조사하면 된다!! 왜냐면 어짜피 mid의 오른쪽 부분은 이 키값보다 더 큰 애들만 있을 테니까 mid보다 키 값이 작은 부분에서 k값을 찾을수 있다!!!! 여기는 키 값대로 정렬되어있으므로!!!! 그래서 low는 그대로 두고 high를 mid-1로 옮겨서 범위를 mid보다 키값이 작은 배열들만 조사하는 것으로 좁히는 것이다.&lt;br&gt; &lt;br&gt;반대로 mid.key값이 k값보다 작다면&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;397&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bd7vTR/btrrcg32t7R/tb9DC5V5Disc1VM27dahyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bd7vTR/btrrcg32t7R/tb9DC5V5Disc1VM27dahyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bd7vTR/btrrcg32t7R/tb9DC5V5Disc1VM27dahyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbd7vTR%2Fbtrrcg32t7R%2Ftb9DC5V5Disc1VM27dahyk%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;1440&quot; height=&quot;397&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;397&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #66f8ff;&quot;&gt; &lt;/span&gt; 이 색깔이 칠해져 있는 배열만 조사하면 된다!! mid의 왼쪽 부분은 이 키값보다 작은 애들만 모여있을테니까 볼 필요가 없지!!! k가 mid보다 크니까 mid보다 큰 쪽에서 k를 찾는 것이다. 그래서 여기서는 high를 그대로 두고 low를 mid+1로 옮겨서 mid보다 큰 키값을 데리고 있는 배열들만 조사하는 것으로 범위를 좁혔다!!&lt;br&gt; &lt;br&gt;이렇게 중간에 있는 키값보다 k값이 크냐 안크냐를 따져보고 범위를 계속해서 좁혀나가면 된다!!!&lt;br&gt; &lt;br&gt;만약 mid.key가 k와 같다면 엔트리를 찾은거니까 더 생각할 필요없이 종료하면 된다!!! 그래서 이걸 정리하면&lt;br&gt;초기에 low = 0, high = n - 1이고 k를 중간 엔트리의 키와 비교한다. 이때 중간 엔트리를 e, 인덱스를 mid라고 하면 mid = ⌊(low+high)/2⌋이다. 다음과 같은 세가지 경우가 발생한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
 &lt;li&gt;만약 k = e.key()이면 찾고자 하는 키를 찾은 것이므로 e를 반환하고 검색을 성공적으로 종료한다.&lt;/li&gt;
 &lt;li&gt;만약 k &amp;lt; e.key()이면 찾고자 하는 엔트리가 e의 앞에 있는 경우(반으로 나눈 것들 중 앞에꺼)이다. 따라서 인덱스 low부터 mid-1까지 다시 검색한다.&lt;/li&gt;
 &lt;li&gt;만약 k &amp;gt; e.key()이면 찾고자 하는 엔트리가 e의 뒤에 있는 경우(반으로 나눈 것들 중 뒤에꺼)이다. 따라서 인덱스 mid+1부터 high까지 다시 검색한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 수도 코드로 보자!!&lt;/p&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;Algorithm BinarySearch(L, k, low, high): input: n개의 엔트리를 저장한 정렬된 벡터 L과 검색하고자 하는 키 k 그리고 정수 low와 high output: L의 인덱스 low와 high 사이에서 키값으로 k를 갖는 엔트리, 존재하지 않다면 특수 센티널 end if low &amp;gt; high then 이게 교차되는 순간 엔트리가 존재하지 않다는 것 return end else mid &amp;lt;- ⌊(low+high)/2⌋ e &amp;lt;- L.at(mid) mid에 위치하는 엔트리 if k = e.key() then return e else if k &amp;lt; e.key() then return BinarySearch(L, k, low, mid - 1) else return BinarySearch(L, k, mid + 1, high)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 검색을 실행하면 호출될 때 마다 후보 엔트리의 수(키값으로 k를 가질 수 있는 가능성이 있는 엔트리들)가 반으로 감소한다. 따라서 이진검색의 실행시간은 O(logn)이다.&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;참고 문헌&lt;/i&gt;&lt;/span&gt;&lt;br&gt;&lt;span style=&quot;color: #666666;&quot;&gt;[1] Michael T. Goodrich, Roberto Tamassia, Davaid M. Mount, C++로 구현하는 자료구조와 알고리즘, WILEY, 2013.&lt;/span&gt;&lt;/p&gt;</description>
      <category>2022/자료구조 + 알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/135</guid>
      <comments>https://guiyum.tistory.com/135#entry135comment</comments>
      <pubDate>Wed, 19 Jan 2022 18:52:59 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 맵, 해시 테이블</title>
      <link>https://guiyum.tistory.com/134</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;맵&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵은 원소를 저장하고 키를 이용하여 빠르게 찾을 수 있도록 한다. 엔트리라 불리는 키-값 쌍(k, v)을 저장한다. 맵 ADT에서 각 키는 유일하여 키와 값의 연관은 매핑을 정의한다. 가장 높은 수준의 일반화를 위해, 맵에 저장된 키와 값은 어떠한 객체타입도 가능하다. 키는 일종의 위치 역할을 하는 객체이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGYsUV/btrq6uuGya7/BcZJWwi9QuWN9FMJmnyBR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGYsUV/btrq6uuGya7/BcZJWwi9QuWN9FMJmnyBR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGYsUV/btrq6uuGya7/BcZJWwi9QuWN9FMJmnyBR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGYsUV%2Fbtrq6uuGya7%2FBcZJWwi9QuWN9FMJmnyBR0%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;496&quot; height=&quot;242&quot; data-origin-width=&quot;496&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵은 객체에 연관된 키가 그 자료구조에서 객체의 위치를 결정하기 때문에 연관된 저장소(associative stores) 또는 연관된 컨테이너(associative container)라고 불리기도 한다.&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;엔트리는 구성 패턴(Composition Pattern)이라 불리는 객체지향 설계 패턴의 한 예로 다른 객체들로 구성된 단일 객체를 정의한다. 쌍은 가장 간단한 구성이다. 두 객체가 하나의 쌍 객체로 합쳐진다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 개념의 구현을 위해 두 개의 멤버 변수를 저장하는 클래스를 정의하고, 그 변수들에 접근하고 수정할 수 있는 함수를 제공한다.&lt;/p&gt;
&lt;pre id=&quot;code_1642504138554&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;typename K, typename V&amp;gt;
class Entry {				// (키, 값) 쌍
public:
    Entry(Const K&amp;amp; k = K(), const V&amp;amp; v = V())
     : _key(k), _value(v) {}
    const K&amp;amp; key() const { return _key; }	// 키를 반환
    const V&amp;amp; value() const { return _value; }	// 값을 반환
    void setKey(const K&amp;amp; k) { _key = k; }	// 키 수정
    void setValue(const V&amp;amp; v) { _value = v; }	// 값 수정
private:
    K _key;					// 키
    V _value;				// 값
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;맵 ADT&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵 반복자 p가 주어졌으면, 연관된 엔트리는 p를 디레퍼런스(= *p)하여 접근할 수 있고 개별 키와 값은 p-&amp;gt;key()와 p-&amp;gt;value()를 이용하여 접근할 수 있다. 객체가 맵에 존재하지 않는 것을 나타내기 위해 end라 불리는 특수한 센티널(sentinal) 반복자가 정의되었다고 가정한다. 지금까지 사용했던 것과 마찬가지로 이 센티널은 맵의 마지막 원소의 바로 뒤에 위치한 가상 원소를 가리킨다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;size(): M에 있는 엔트리의 개수를 반환&lt;/li&gt;
&lt;li&gt;empty(): M이 비어있으면 true, 아니면 false&lt;/li&gt;
&lt;li&gt;find(k): 만약 M이 엔트리 e = (k, v), 즉 키가 k와 같은 엔트리를 포함하면 이 엔트리를 가리키는 반복자 p를 반환하고 그렇지 않으면 특수 반복자 end를 반환한다.&lt;/li&gt;
&lt;li&gt;put(k, v): 만약 M이 키가 k와 같은 엔트리를 포함하지 않으면 M에 엔트리 (k, v)를 추가한다. k와 같은 엔트리가 존재한다면 기존 엔트리 값을 v로 대체한다. 삽입되었거나 수정된 엔트리에 대한 반복자를 반환한다.&lt;/li&gt;
&lt;li&gt;erase(k): 키가 k와 같은 엔트리를 삭제한다.&lt;/li&gt;
&lt;li&gt;erase(p): 반복자 p가 가리키는 엔트리를 M에서 삭제한다.&lt;/li&gt;
&lt;li&gt;begin(): M의 첫 번째 엔트리에 대한 반복자를 반환한다.&lt;/li&gt;
&lt;li&gt;end(): 맵의 마지막 원소 바로 뒤의 위치에 대한 반복자를 반환한다.&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;393&quot; data-origin-height=&quot;313&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tqKO6/btrq6OfnuXM/IzVgC0VHapp5hGVpBmyVr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tqKO6/btrq6OfnuXM/IzVgC0VHapp5hGVpBmyVr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tqKO6/btrq6OfnuXM/IzVgC0VHapp5hGVpBmyVr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtqKO6%2Fbtrq6OfnuXM%2FIzVgC0VHapp5hGVpBmyVr0%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;393&quot; height=&quot;313&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;313&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;STL map 클래스&lt;/h3&gt;
&lt;pre id=&quot;code_1642505228517&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;map&amp;lt;string, int&amp;gt; myMap;						// (string, int) 맵
map&amp;lt;string, int&amp;gt;::iterator p;				// 맵에 대한 반복자
myMap.insert(pair&amp;lt;string, int&amp;gt;(&quot;Rob&quot;, 28));	// (&quot;Rob&quot;, 28) 삽입
myMap[&quot;Joe&quot;] = 38;							// (&quot;Joe&quot;, 38) 삽입
myMap[&quot;Joe&quot;] = 50;							// (&quot;Joe&quot;, 50)으로 수정
myMap[&quot;Sue&quot;] = 75;							// (&quot;Sue&quot;, 75) 삽입
p = myMap.find(&quot;Joe&quot;);						// *p = (&quot;Joe&quot;, 50)
myMap.erase(p);								// (&quot;Joe&quot;, 50) 삭제
myMap.erase(&quot;Sue&quot;);							// (&quot;Sue&quot;, 75) 삭제
p = myMap.find(&quot;Joe&quot;);
if (p == myMap.end()) cout &amp;lt;&amp;lt; &quot;nonexistent\n&quot;;	// Joe가 존재하지 않는다면 nonexistent 출력
for (p == myMap.begin(); p != myMap.end(); p++) {	// 모든 엔트리를 출력
    cout &amp;lt;&amp;lt; &quot;(&quot; &amp;lt;&amp;lt; p-&amp;gt;first &amp;lt;&amp;lt; &quot;,&quot; &amp;lt;&amp;lt; p-&amp;gt;second &amp;lt;&amp;lt; &quot;)\n&quot;;	// (키, 쌍) 형태로 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;p-&amp;gt;first, p-&amp;gt;second로 키와 value를 참조할 수 있다.&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;Simple List-Based Map 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵을 구현하는 간단한 방법은 이중 링크드 리스트로 구현된 리스트 L에 n개의 엔트리를 저장하는 것이다. 기본적인 함수들 find(k), put(k, v), erase(k)를 수행하기 위해서는 키 k를 찾으며 L을 스캔하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1642505890973&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Algorithm find(k):
    input: 키 k
    output: L에서 키 k를 갖는 엔트리의 위치, 또는 L에 키 k가 없으면 end
    for each position p in [L.begin(), L.end()) do
    	if p.key() = k then
        	return p
     return end		k와 같은 키를 갖는 엔트리가 없다
     
 Algorithm put(k, v):
     input: 키-값 쌍 (k, v)
     output: 삽입/수정된 엔트리의 위치
     for each position p in [L.begin(), L.end()) do
     	if p.key() = k then
        	*p &amp;lt;- (k, v)
            return p
     p &amp;lt;- L.insertBack((k, v))
     n &amp;lt;- n + 1
     return p
     
Algorithm erase(k):
    input: 키 k
    output: 없음
    for each position p in [L.begin(), L.end()) do
    	if p.key() = k then
        	L.erase(p)
            n &amp;lt;- n - 1	엔트리의 개수를 하나 감소시킴&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list-based 맵은 간단하지만 매우 작은 맵에서만 효율적이다. 모든 주요 함수들이 n개의 엔트리를 갖는 맵에서 실행시간이 O(n)이다(최악의 경우 전체 리스트를 검색해야 하기 때문에). 따라서 훨씬 빠른 다른 것이 필요하다!!&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;해시 테이블(Hash Tables)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵을 구현하는 가장 효율적인 방법 중 하나이다. 해시 테이블을 사용할 경우 맵 연산의 실행시간은 최악의 경우 O(n)이지만, 평균 O(1) 시간에 실행할 수 있다. 버켓 배열과 해시 함수가 해시 테이블의 두 개의 주요 구성원소이다!!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;935&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V8obT/btrq7Vy1Hs7/KdOVq68mZdGiG926mfIff0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V8obT/btrq7Vy1Hs7/KdOVq68mZdGiG926mfIff0/img.png&quot; data-alt=&quot;출처: https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%85%8C%EC%9D%B4%EB%B8%94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V8obT/btrq7Vy1Hs7/KdOVq68mZdGiG926mfIff0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV8obT%2Fbtrq7Vy1Hs7%2FKdOVq68mZdGiG926mfIff0%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;1280&quot; height=&quot;935&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;935&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: https://ko.wikipedia.org/wiki/%ED%95%B4%EC%8B%9C_%ED%85%8C%EC%9D%B4%EB%B8%94&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;버켓(bucket) 배열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 테이블을 위한 버켓 배열은 크기가 N인 배열 A이다. 여기서 A의 각 셀을 &lt;b&gt;키-원소 쌍을 저장&lt;/b&gt;하는 컨테이너 &quot;&lt;b&gt;버켓&lt;/b&gt;&quot;으로 생각하고 정수 N은 배열의 용량(capacity)이다. 만약 키들이 [0, N-1] 범위에 잘 분포된 정수라면 버켓 배열만으로 충분하다. 키 k를 갖는 원소 e는 버켓 A[k]에 삽입된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 키가 [0, N-1] 범위에 있는 유일한 정수라면 각&amp;nbsp; 버켓은 최대 하나의 엔트리를 저장한다. 따라서 버켓 배열의 검색, 삽입, 삭제는 최악의 경우 O(1) 시간이 소요된다. 충돌에 대한 걱정을 할 필요가 없다!!! 하지만 여기에는 두 가지 단점이 있다. 첫 번째는 O(N) 공간을 사용한다는 것이다. N이 실제 맵에 존재하는 엔트리의 개수 n에 비해 너무 크다면 이 구현 방법은 공간을 낭비하는 것이다. 두 번째는 버켓 배열에서 키들이 [0, N-1] 범위에 있는 유일한 정수여아 하는데 이 경우는 별로 흔하지 않다. 이러한 단점들 때문에 키를 [0, N-1] 범위 안에 정수로 변환하는 매핑과 함께 버켓 배열을 사용한다!!&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;해시 함수(Hash Function)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 함수라고 불리는 h 함수는 맵의 각 키 k를 [0, N-1] 범위의 정수로 변환하는데, 여기서 N은 이 테이블을 위한 버켓 배열의 용량이다. 해시 함수가 있으면 임의의 키에 대해 버켓 배열 방법을 적용할 수 있다. 키 k 대신 키의 해시 함수값 h(k)를 버켓 배열 A의 인덱스로 사용한다!!! 즉, 엔트리 (k, v)를 버켓 A[h(k)]에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 두 개 이상의 키들이 같은 해시값을 가진다면(k1 != k2 &amp;amp;&amp;amp; h(k1) == h(k2) 라면) 두 개 이상의 원소들이 A의 같은 버켓에 매핑되는 충돌(collision)이 발생할 수 있다!!! 해시 함수는 맵 내의 키들을 매핑하는 과정에서 충돌을 가능한 최소화해야 좋은 함수라고 할 수 있다. 해시 함수의 계산이 빠르고 쉬운 것 또한 바람직하다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 함수 h(k)의 계산을 두 단계로 나누어 생각한다. 해시 코드(hash code)라고 불리는 키 k를 정수로 바꾸는 과정과 압축 함수(compression function)라 불리는 해시 코드를 버켓 배열의 인덱스 범위([0, N-1])로 매핑하는 과정이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnP0Mu/btrq6vgXPg0/GCP2trwNfpA8nYKQrSyyoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnP0Mu/btrq6vgXPg0/GCP2trwNfpA8nYKQrSyyoK/img.png&quot; data-alt=&quot;해시 함수의 두 단계: 해시 코드와 압축 함수&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnP0Mu/btrq6vgXPg0/GCP2trwNfpA8nYKQrSyyoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnP0Mu%2Fbtrq6vgXPg0%2FGCP2trwNfpA8nYKQrSyyoK%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;422&quot; height=&quot;228&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;해시 함수의 두 단계: 해시 코드와 압축 함수&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해시 코드(Hash Codes)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 함수의 첫 번째 단계는 맵의 임의의 키 k에 주어진 정수값을 할당하는 것이다. 키 k에게 할당된 정수값을 k에 대한 해시 코드라 한다!! 이 정수값은 [0, N-1] 범위에 있지 않아도 되고 음수값일 수도 있다. 키들에 주어진 해시 코드들은 가능한 충돌하지 않는 것이 바람직하다. 만약 키들의 해시 코드가 충돌하면 압축 함수도 충돌하게 된다. 또한 모든 키의 일관성을 위해 키 k에 주어진 해시 코드는 k와 같은 키의 해시 코드와 같아야 한다!!&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;압축 함수(Compression Functions)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키에 대한 해시 코드의 범위가 보통 버켓 배열 A의 인덱스 범위를 벗어나기 때문에, 키 k에 대한 해시 코드를 버켓 배열에 바로 사용하기는 적당하지 않다. 범위가 맞지 않는 해시 코드를 버켓 배열에 사용하면 out-of-bound 예외가 발생하게 된다. 따라서 객체 k에 대한 정수 해시 코드를 결정한 다음에도 그 정수를 범위 [0, N-1]으로 매핑하는 문제가 남아있다. 이러한 압축 단계는 해시 함수가 수행하는 두 번째 과정이다.&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;충돌-처리 기법(Collision-Handling Schemes)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 테이블의 핵심은 버켓 배열 A와 해시 함수 h를 사용하여 맵의 각 엔트리 (k, v)를 버켓 A[h(k)]에 저장하는 것이다. 하지만 두 개의 키 k1과 k2가 같은 해시값을 가지면(h(k1) == h(k2)) 충돌의 문제가 생긴다. 이러한 충돌 때문에 새 엔트리 (k, v)를 버켓 A[h(k)]에 직접 넣을 수가 없다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;별도 체이닝(Seperate Chaining)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌을 해결하는 간단하면서 효과적인 방법은 각 버켓 A[i]에 리스트, 벡터, 또는 시퀀스 Si의 참조를 저장하는 것이다. 해시 함수가 버켓 A[i]로 매핑하는 모든 엔트리를 얘네들에 저장한다. 시퀀스 Si는 비정렬된 시퀀스 또는 로그 파일 방법으로 구현된 작은 맵을 생각할 수 있으며, h(k) = i인 모든 엔트리 (k, v)를 저장한다. 이러한 충돌 해결 방법을 별도 체이닝이라 한다. 각 비어 있지 않은 버켓을 이러한 로그 파일 방식에 의한 작은 맵으로 구현하였다고 가정하면 맵의 기본적인 연산들을 아래 코드로 수행할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1642571794534&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Algorithm find(k):
    output: 맵에서 일치하는 엔트리의 위치, 또는 맵에 키 k가 없으면 end
    
    return A[h(k)].find(k)	find(k)를  A[h(k)]에 있는 list-based map에 위임

Algorithm put(k, v):
    p &amp;lt;- A[h(k)].put(k, v)	put을 A[h(k)]에 있는 list-based map에 위임
    n &amp;lt;- n + 1
    return p

Algorithm erase(k):
    output: 없음
    A[h(k)].erase(k)		erase를 A[h(k)]에 있는 list-based map에 위임
    n &amp;lt;- n - 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 k에 관련된 각각의 기본적인 맵 연산을 위해, A[h(k)]에 저장된 작은 시퀀스에 의한 맵의 연산을 이용한다. 따라서 put(k, v)는 이 리스트를 따라 k와 키가 같은 엔트리를 검색한다. 발견하면 값을 v로 교체하고, 그렇지 않으면 리스트의 끝에 (k, v)를 삽입한다. 비슷하게 find(k)는 리스트를 따라 검색하여 k와 같은 키를 갖는 엔트리를 찾거나 리스트의 끝에 도달한다. erase(k)는 유사한 검색을 실행한 후, 만약 엔트리를 발견하였다면 삭제한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵이 n개의 엔트리를 사이즈 N인 버켓 배열에 저장하기 위해 좋은 해시 함수를 사용하고 있다고 가정하면, 각 버켓의 사이즈는 평균 n/N이 될 것이다. 해시 테이블의 적재율(load factor)로 불리는 이 값(&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;로 표시)은 작은 것이 바람직하며, 가능하면 1보다 작아야 한다. 이 함수를 이용한 해시 테이블로 구현된 맵의 연산 find, put, erase의 예상 실행 시간은 O(&amp;lceil;n/N&amp;rceil;) &lt;/span&gt;이다. 따라서 n이 O(N)인 경우 표준 맵 연산들의 예상 실행 시간은 O(1)이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;493&quot; data-origin-height=&quot;335&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dq0ZFk/btrq7GB5XXt/edIKBhAWrxbAUwWj4NH8R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dq0ZFk/btrq7GB5XXt/edIKBhAWrxbAUwWj4NH8R0/img.png&quot; data-alt=&quot;크기가 13인 해시 테이블에 정수 키를 갖는 10개의 엔트리를 별도 체이닝 방법에 의해 충돌을 해결하여 저장한 예. 이 경우 압축 함수는 h(k) = k mod 13이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dq0ZFk/btrq7GB5XXt/edIKBhAWrxbAUwWj4NH8R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdq0ZFk%2Fbtrq7GB5XXt%2FedIKBhAWrxbAUwWj4NH8R0%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;493&quot; height=&quot;335&quot; data-origin-width=&quot;493&quot; data-origin-height=&quot;335&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;크기가 13인 해시 테이블에 정수 키를 갖는 10개의 엔트리를 별도 체이닝 방법에 의해 충돌을 해결하여 저장한 예. 이 경우 압축 함수는 h(k) = k mod 13이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;오픈 어드레싱(Open Addressing)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도 체이닝은 맵 연산의 구현을 간단히 할 수 있는 등의 많은 장점을 가지고 있지만, 충돌하는 키들을 저장하기 위해 별도의 자료구조인 리스트가 필요하다는 단점이 있다. 공간이 충분하지 않은 경우에는 각 엔트리를 버켓에 직접 저장하는 방식(각 버켓은 최대 하나의 엔트리를 저장)을 사용할 수 있다. 이 방식은 별도의 자료구조를 사용하지 않아 공간을 절약할 수 있지만, 좀 더 복잡한 충돌 해결 방법이 필요하다. 이러한 방법을 전체적으로 오픈 어드레싱 기법이라고 하고, 이때 적재율은 항상 1보다 작아야 하고 엔트리들은 버켓 배열에 직접 저장된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;선형 검사(Linear Probing)와 그 변형들&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 오픈 어드레싱 충돌-처리 기법이 선형 검사이다. 만약 엔트리 (k, v)를 삽입할 버켓 A[i](여기서 i == h(k))에 다른 엔트리가 이미 저장되어 있는 경우, 다음 버켓 A[(i+1) mod N]에 저장하는 방식이다. A[(i+1) mod N]에도 다른 엔트리가 이미 저장되어 있으면 A[(i+2) mod N]에 저장한다. 이런 방식으로 A에서 빈 버켓을 찾을 때 까지 계속 다음 버켓을 검사한다. 이렇게 해서 빈 버켓을 찾은 후에 엔트리(k, v)를 거기에 저장하면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBwGLT/btrq6ZaQFir/uowGzQiyYMfkQaKcarw9mK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBwGLT/btrq6ZaQFir/uowGzQiyYMfkQaKcarw9mK/img.png&quot; data-alt=&quot;충돌 해결을 위해 선형 검사를 사용하여 해시 테이블에 삽입하는 예. 여기서 압축 함수는 h(k) = k mod 11을 사용한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBwGLT/btrq6ZaQFir/uowGzQiyYMfkQaKcarw9mK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBwGLT%2Fbtrq6ZaQFir%2FuowGzQiyYMfkQaKcarw9mK%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;573&quot; height=&quot;180&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;충돌 해결을 위해 선형 검사를 사용하여 해시 테이블에 삽입하는 예. 여기서 압축 함수는 h(k) = k mod 11을 사용한다.&lt;/figcaption&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;이 방법은 맵의 엔트리를 뭉치게 하여 연속적으로 저장하는 경향이 있으며 심지어 여러 키가 겹치기도 한다. 이러한 현상은 검색 시간을 현저히 떨어트린다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;제곱 검사(Quadratic Probing)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 버켓을 찾을 때 까지 연속적으로 A[(i + f(j)) mod N](여기서 j = 0, 1, 2, ... 이고 f(j) = j^2)를 검사한다. 제곱 검사 전략은 선형 검사에서 발생하는 뭉치는(clustering) 패턴을 피할 수 있다. 하지만 이 방법은 2차 클러스터링이라 불리는 채워진 셀들이 배열 내에서 정해진 패턴으로 &quot;움직이는&quot; 클러스터링을 발생시킨다. 만약 N을 소수로 하지 않으면 꽉 차지 않은 배열 A에서 빈 배열을 못 찾을 수도 있다. 또 N이 소수이더라도 버켓 배열이 반 이상 차있다면 또 빈 버켓을 찾지 못할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이중 해싱(Double Hashing)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형검사 방법이나 제곱 검사 방법에 의해 발생하는 클러스트링을 만들지 않는 오픈 어드레싱 전략이다. 이 방법에서는 보조 해시 함수 h'를 만들어 만약 키 k에 대한 버켓 A[i](i = h(k))가 이미 사용되고 있으면 버켓(A[(i + f(j)) mod N](여기서 j = 1, 2, 3, ... 이고 f(j) = j &amp;middot; h'(k))를 차례로 검사한다. 보조 해시 함수는 0이 되지 않아야 하며 일반적으로 h'(k) = q - (k mod q)를 사용한다. 여기서 q는 N보다 작은 소수이고 또한 N도 소수여야 한다. 보조 해시 함수를 결정하는 데 있어 클러스터링을 최대한 줄이도록 해야 한다.&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;적재율과 재해싱(Load Factors and Rehashing)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명한 모든 해시 테이블 체계에서 적재율 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda; = n/N은 항상 1보다 작은 것이 바람직하다. 실험과 통계적인 분석에서 오픈 어드레싱은 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;&amp;lt;0.5이고 별도 체이닝에서는 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;&amp;lt;0.9인 것이 바람직하고 한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #202124;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;오픈 어드레싱에서 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;가 0.5를 넘어 증가하기 시작하여 1에 접근하기 시작하면서, 버켓 배열에서 엔트리들의 클러스터도 같이 증가하기 시작한다. 이러한 클러스터들은 검사 정책(probing strategy)들이 상당히 큰 시간 동안 버켓 배열을 여기저기 검색하도록 하는 원인이 된다. &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;가 1에 가까워지면, 몇 개 남은 비어있는 셀 중 하나를 발견하기 전에 만나야 하는 버켓의 수가 선형일 것이기 때문에 모든 맵 연산은 선형의 실행시간을 갖는다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;새 테이블로의 재해싱(Rehashing into a New Table)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적재율 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;을 정해진 임계 값 이하로 유지하는 것이 중요하다. 만약 해시 테이블의 &lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&amp;lambda;가 특정 임계값보다 상당히 높아지면, 보통 정해진 적재율 아래로 유지하기 위해 테이블 사이즈를 증가시키고 모든 객체를 새 테이블로 삽입한다. 새 테이블로 재해시할 경우, 새 테이블의 사이즈는 이전 사이즈보다 적어도 두 배 이상 되는 것이 좋다. 새로운 버켓 배열을 할당한 후에는 함께 사용할 해시 함수를 다시 정의해야 한다. 이렇게 새로운 해시 함수를 준비한다면, 이전 배열의 모든 엔트리를 새 해시 함수를 이용하여 새 배열로 삽입한다. 이러한 과정을 재해싱(rehashing)이라고 한다.&lt;/span&gt;&lt;/span&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;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #202124;&quot;&gt;C++ 구현&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1642578292034&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;typename K, typename V, typename H&amp;gt;
class HashMap {
public:
    typedef Entry&amp;lt;const K, V&amp;gt; Entry;		// (key, value) pair
    class Iterator;							// 반복자(위치)
	HashMap(int capacity = 100)				// 생성자
    : n(0), B(capacity) {}
    int size() const {						// 엔트리 개수
    	return n;
    }
    bool empty() const {					// 맵이 비었는지
    	return size() == 0;
    }
    Iterator find(const K&amp;amp; k) {				// 키 k를 갖는 엔트리를 검색
    	Iterator p = finder(k);				// k를 찾는다
        if (endOfBkt(p))					// 못찾았나?
        	return end();					// 그럼 end 반복자 반환
        else
        	return p;						// 찾았으면 위치를 반환
    }
    Iterator put(const K&amp;amp; k, const V&amp;amp; v) {	// (k,v)를 삽입하거나 이미 키가 k인 엔트리가 존재한다면 (k, v)로 교체
    	Iterator p = finder(k);				// k를 검색
        if (endOfBkt(p)) {					// k가 없나
        	return inserter(p, Entry(k, v));	// 버켓 끝에 삽입
        }
        else {
        	p.ent-&amp;gt;setValue(v);				// 있다면 k의 값을 v로 교체
            return p;
        }
    }
    void erase(const K&amp;amp; k) {				// 키 k를 갖는 엔트리 삭제
    	Iterator p = finder(k);				// k를 검색
        if (endOfBkt(p))					// 못찾았나?
        	throw NonexistentElement(&quot;Erase of nonexistent&quot;);	// 에러
        erase(p);							// 삭제
    }
    void erase(const Iterator&amp;amp; p) {			// p에 있는 엔트리 삭제
    	eraser(p);
    }
    Iterator begin() {						// 첫 번째 엔트리의 반복자
    	if (empty()) return end();			// 비어있으면 end반환
        BItor bkt = B.begin();				//그렇지 않으면 엔트리를 검색
        while (bkt-&amp;gt;empty()) ++bkt;			// 비지 않는 버켓을 찾는다
        return Iterator(B, bkt, bkt-&amp;gt;begin());	// 버켓의 first 반환
    Iterator end() {						// end 엔트리의 반복자
		return Iterator(B, B.end);
    }
protected:
    typedef std::list&amp;lt;Entry&amp;gt; Bucket;		// 엔트리들의 버켓
    typedef std::vector&amp;lt;Bucket&amp;gt; BktArray;	// 버켓 배열
    Iterator finder(const K&amp;amp; k) {			// 검색 편의 함수
    	int i = hash(k)%B.size();			// 해시 인덱스 i를 구함
        BItor bkt = B.begin() + i;			// i번째 버켓
        Iterator p(B, bkt, bkt-&amp;gt;begin());	// i번째 버켓의 첫 엔트리
        while (!endOfBkt(p) &amp;amp;&amp;amp; (*p).key() != k)		// k를 검색
        	nextEntry(p);
        return p;
    }
    Iterator inserter(const Iterator&amp;amp; p, const Entry&amp;amp; e) {	// 삽입 편의 함수
    	EItor ins = p.bkt-&amp;gt;insert(p.ent, e);	// p 앞에 삽입
        n++;
        return Iterator(B, bkt, ins);			// 이 위치 반환
    }
    void eraser(const Iterator&amp;amp; p) {			// 삭제 편의 함수
    	p.bkt-&amp;gt;erase(p.ent);					// 엔트리를 버켓에서 삭제
        n--;									// 엔트리의 개수를 감소
    }
    typedef typename BktArray::iterator BItor;	// 버켓 반복자
    typedef typename Bucket::iterator EItor;	// 엔트리 반복자
    static void nextEntry(Iterator&amp;amp; p) {		// 버켓의 다음 엔트리
    	++p.ent;
    }
    static bool endOfBkt(const Iterator&amp;amp; p) {	// 버켓의 끝인지
    	return p.ent == p.bkt-&amp;gt;end();
    }
private:
    int n;									// 엔트리의 개수
    H hash;									// 해시 비교자
    BktArray B;								// 버켓 배열
public:
    class Iterator {						// 반복자
    private:
    	EItor ent;							// 엔트리
        BItor bkt;							// 버켓
        const BktArray* ba;					// 버켓 배열
    public:
        Iterator(const BktArray&amp;amp; a, const BItor&amp;amp; b, const EItor&amp;amp; q = EItor())
        : ent(q), bkt(b), ba(&amp;amp;a) {}
        Entry&amp;amp; operator*() const {			// 엔트리 반환
        	return *ent;
        }
        bool operator==(const Iterator&amp;amp; p) const {	// 반복자가 같은지
        	if (ba != p.ba || bkt != p.bkt) return false;	// ba 또는 bkt가 다른가
            else if (bkt == ba-&amp;gt;end()) return true;	// 둘 다 end인가?
            else return (ent == p.ent);				// 엔트리가 같은지 확인
        }
        Iterator&amp;amp; operator++() {			// 다음 엔트리로 전진
       		++ent;
            if (endOfBkt(*this)) {			// 버켓의 끝인가?
            	++bkt;
                while (bkt != ba-&amp;gt;end() &amp;amp;&amp;amp; bkt-&amp;gt;empty())	// 비지 않은 버켓을 검색
                	++bkt;
                if (bkt == ba-&amp;gt;end()) return *this;			// 버켓 배열의 끝?
                ent = bkt-&amp;gt;begin();
            }
            return *this;
        }   
        friend class HashMap;				// HashMap에 접근을 허용
    };
};&lt;/code&gt;&lt;/pre&gt;
&lt;hr class=&quot;text-block before-moreless&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;참고 문헌&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;[1] Michael T. Goodrich, Roberto Tamassia, Davaid M. Mount, C++로 구현하는 자료구조와 알고리즘, WILEY, 2013.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>2022/자료구조 + 알고리즘</category>
      <category>맵</category>
      <category>자료구조</category>
      <category>해시테이블</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/134</guid>
      <comments>https://guiyum.tistory.com/134#entry134comment</comments>
      <pubDate>Wed, 19 Jan 2022 16:44:58 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조] 힙 정렬</title>
      <link>https://guiyum.tistory.com/133</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;힙-정렬&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://guiyum.tistory.com/131&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2022.01.18 - [2022/자료구조 + 알고리즘] - [자료구조] 힙&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1642492453589&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조] 힙&quot; data-og-description=&quot;https://guiyum.tistory.com/130 [자료구조] 우선순위 큐 우선순위 큐 : 임의의 순서로 입력된 우선순위된 원소들을 저장하고 우선순위에 따라 출력하는 추상 데이터 타입 저장된 원소들 중 가장 우선순&quot; data-og-host=&quot;guiyum.tistory.com&quot; data-og-source-url=&quot;https://guiyum.tistory.com/131&quot; data-og-url=&quot;https://guiyum.tistory.com/131&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxXFm3/hyM741rusB/FEoFjCGGXvB5WnXrmKqFDk/img.png?width=800&amp;amp;height=257&amp;amp;face=0_0_800_257,https://scrap.kakaocdn.net/dn/bBYVtk/hyM8bztRc6/tXLKvENWs0EwJQJZR936gK/img.png?width=800&amp;amp;height=257&amp;amp;face=0_0_800_257,https://scrap.kakaocdn.net/dn/kXVqO/hyM76ybt46/Yq6UCUB0E1wyvcFk9qjCo0/img.png?width=1440&amp;amp;height=464&amp;amp;face=0_0_1440_464&quot;&gt;&lt;a href=&quot;https://guiyum.tistory.com/131&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://guiyum.tistory.com/131&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxXFm3/hyM741rusB/FEoFjCGGXvB5WnXrmKqFDk/img.png?width=800&amp;amp;height=257&amp;amp;face=0_0_800_257,https://scrap.kakaocdn.net/dn/bBYVtk/hyM8bztRc6/tXLKvENWs0EwJQJZR936gK/img.png?width=800&amp;amp;height=257&amp;amp;face=0_0_800_257,https://scrap.kakaocdn.net/dn/kXVqO/hyM76ybt46/Yq6UCUB0E1wyvcFk9qjCo0/img.png?width=1440&amp;amp;height=464&amp;amp;face=0_0_1440_464');&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;[자료구조] 힙&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;https://guiyum.tistory.com/130 [자료구조] 우선순위 큐 우선순위 큐 : 임의의 순서로 입력된 우선순위된 원소들을 저장하고 우선순위에 따라 출력하는 추상 데이터 타입 저장된 원소들 중 가장 우선순&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;guiyum.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 설명한 바와 같이, 힙으로 우선순위 큐를 구현할 경우 모든 우선순위 큐 ADT의 함수들을 로그 시간 또는 그보다 효율적으로 구현할 수 있는 장점이 있다. 이번에는 우선순위 큐 P를 이용하여 리스트 L을 정렬하는 PriorityQueueSort 정렬 기법을 다시 보자!!&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;첫 번째 단계에서, i번째의 (1 &amp;le; i &amp;le; n) insert 연산은 각각 O(1 + log i) 시간이 소요된다(연산이 수행된 후 힙에 i개의 원소가 있기 때문에).&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 유사하게 두 번째 단계에서 j번째의 (1 &amp;le; j &amp;le; n) removeMin 연산은 각각 O(1 + log(n - j + 1)) 시간이 소요된다(연산이 수행된 후 힙에 n - j + 1개의 원소가 있기 때문에).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 각 단계의 실행 시간은 O(nlogn)이 되고, 우선순위를 구현하기 위해 힙을 사용할 경우 전체 우선순위 큐 알고리즘의 실행시간은 O(nlogn)이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정렬 알고리즘을 힙-정렬(heap-sort)이라 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;힙-정렬 알고리즘은 n개의 원소를 갖는 리스트 L을 O(nlogn) 시간에 정렬한다. 이때, L의 두 원소는 O(1) 시간에 비교 가능하다고 가정한다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;힙-정렬의 인-플레이스(In-place) 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 리스트 L이 배열로 구현되어 있다면, 힙 정렬의 속도를 향상시킬 수 있으며 리스트 L 자체를 이용하여 힙을 저장하여 별도의 힙 자료구조를 사용할 필요가 없다. 다음 알고리즘을 살펴보자!!&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;역 비교자(reverse comparator)를 사용하여 가장 큰 원소가 힙의 루트에 오도록 한다. 알고리즘을 실행하는 동안, L의 왼쪽 부분(순위(인덱스) i - 1까지)에 힙의 원소들을 저장하고, L의 오른쪽 부분(i부터 n - 1까지)에 시퀀스(배열)의 원소들을 저장한다. 따라서 L의 처음 i개 원소들(0, ..., i - 1)이 힙의 벡터 표현이 된다(노드 번호가 1 대신 0에서 시작). 즉 순위 k에 저장된 원소는 순위 2k+1과 2k+2에 저장된 그 자식들보다 크거나 같다.&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;999&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cABq29/btrq6PkMxDF/UGNvAWFKPN65L2WbXGFKVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cABq29/btrq6PkMxDF/UGNvAWFKPN65L2WbXGFKVK/img.png&quot; data-alt=&quot;L의 왼쪽 부분은 힙 원소 저장, 오른쪽 부분은 시퀀스 원소들 저장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cABq29/btrq6PkMxDF/UGNvAWFKPN65L2WbXGFKVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcABq29%2Fbtrq6PkMxDF%2FUGNvAWFKPN65L2WbXGFKVK%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;1183&quot; height=&quot;999&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;999&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;L의 왼쪽 부분은 힙 원소 저장, 오른쪽 부분은 시퀀스 원소들 저장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;알고리즘의 첫 단계에서, &lt;b&gt;비어 있는 힙&lt;/b&gt;에서 출발하여 힙과 시퀀스의 경계를 한번에 하나씩 왼쪽에서 오른쪽으로 이동한다. 단계 i(i = 1, ..., n)에서, 순위 i-1에 있는 원소를 힙에 추가한다. 위에 그림 (a)를 보면 1단계로 순위(인덱스) 0에 있는 value가 3인 원소를 힙에 추가하였다.&lt;/li&gt;
&lt;li&gt;알고리즘의 두 번째 단계에서, &lt;b&gt;비어있는 시퀀스&lt;/b&gt;에서 출발하여 힙과 시퀀스의 경계를 한 번에 하나씩 오른쪽에서 왼쪽으로 이동한다. 단계 i(i = 1, ..., n)에서, 우리는 힙에서 최대값을 제거하여 순위 n - i에 저장한다. 아래 그림의 (,e)를 보면 시퀀스의 모든 원소를 힙으로 이동시켜서 시퀀스가 비어있다. 그래서 (f)에서 힙의 최댓값 7을 제거하고 처음 시퀀스로 옮기는 것이기 때문에 첫 단계니까 이 7을 5 - 1인 4번째 인덱스에 저장하였다. 나머지도 차례대로 진행한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGxhOe/btrq4vgdhuK/ZKAYSRoqzUKrLxCIgHAtaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGxhOe/btrq4vgdhuK/ZKAYSRoqzUKrLxCIgHAtaK/img.png&quot; data-alt=&quot;(a)부터 (e)까지 원소들을 힙에 추가, (f)부터 (j)까지 연속적으로 원소를 삭제하여 시퀀스에 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGxhOe/btrq4vgdhuK/ZKAYSRoqzUKrLxCIgHAtaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGxhOe%2Fbtrq4vgdhuK%2FZKAYSRoqzUKrLxCIgHAtaK%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;552&quot; height=&quot;589&quot; data-origin-width=&quot;552&quot; data-origin-height=&quot;589&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(a)부터 (e)까지 원소들을 힙에 추가, (f)부터 (j)까지 연속적으로 원소를 삭제하여 시퀀스에 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 자체 외에는 일정한 양의 공간만 추가로 필요하기 때문에, 위와 같은 힙-정렬의 변형을 인-플레이스(in-place)라 한다. 즉, 원소들을 시퀀스의 밖으로 옮기거나 다시 들여오는 대신, 시퀀스 내부에서 움직인다. 일반적으로 정렬되는 객체 자신이 필요한 메모리 외에 일정한 양의 공간만 추가로 사용할 경우, 이러한 정렬 알고리즘을 인-플레이스라고 한다.&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;상향식(Bottom-Up) 힙 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙-정렬 알고리즘의 분석을 통하여 n개의 키-원소 쌍을 저장하는 힙을 O(nlogn) 시간에 생성할 수 있음을 알았다!! 즉, n번의 연속적인 insert를 통하여 생성된다. 다음에 생성된 힙을 사용하여 순서대로 원소를 삭제한다. 하지만 힙에 저장된 모든 키가 사전에 주어진다면, O(n)시간에 실행되는 상향식 생성 방법을 사용할 수 있다.&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;1167&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DnAEB/btrq60sZqGh/KbMJDnOcRPRSNN0w4rbZ90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DnAEB/btrq60sZqGh/KbMJDnOcRPRSNN0w4rbZ90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DnAEB/btrq60sZqGh/KbMJDnOcRPRSNN0w4rbZ90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDnAEB%2Fbtrq60sZqGh%2FKbMJDnOcRPRSNN0w4rbZ90%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;1167&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1167&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n이 2^1 -1개 주어졌다고 가정하자(위 사진 참고). 즉, 사진의 첫 번째 힙은 모든 레벨이 꽉 찬 완전 이진트리이고, 따라서 힙의 높이 h = log(n + 1)이다. 비재귀적인 방법으로 설명하면, 상향식 힙 생성은 다음과 같은 h = log(n + 1) 단계로 이루어진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UQvsn/btrq4xk3cCe/lWCSAJ7jHQFYAzdo0MskMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UQvsn/btrq4xk3cCe/lWCSAJ7jHQFYAzdo0MskMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UQvsn/btrq4xk3cCe/lWCSAJ7jHQFYAzdo0MskMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUQvsn%2Fbtrq4xk3cCe%2FlWCSAJ7jHQFYAzdo0MskMK%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;700&quot; height=&quot;708&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;첫 번째 단계에서(위 그림의 (a) 참고), 각각 하나의 엔트리를 갖는 (n + 1)/2개의 기본 힙들을 생성한다.&lt;/li&gt;
&lt;li&gt;두 번째 단계에서(위 그림 (b)-(c)), 기본 힙들을 두 개씩 합치고 새로운 엔트리를 하나씩 추가하여 각각 세 개의 엔트리를 갖는 (n + 1)/4개의 힙을 구성한다. 새로운 키는 처음에는 루트에 놓이지만 힙-순서 특성을 만족하기 위해 자식과 교환될 수 있다.&lt;/li&gt;
&lt;li&gt;세 번째 단계에서(위 그림 (d)-(e)), 두 개씩의 3-엔트리 힙(전 단계에서 만들어진)을 합치고 새로운 엔트리를 하나 추가하여 각각 일곱 개의 엔트리를 갖는 (n + 1)/8개의 힙을 구성한다. 새로운 키는 처음에 루트에 놓이지만 힙-순서 특성을 만족하기 위해 다운-힙 버블링을 통해 아래로 움직일 수 있다.&lt;/li&gt;
&lt;li&gt;2 &amp;le; i &amp;le; h인 i번째 단계에서, 두 개씩의 2^(i-1) - 1 엔트리를 갖는 힙(전 단계에서 만들어진)을 합치고 새로운 엔트리를 추가하여 각각 2^1 - 1개의 엔트리를 갖는 (n + 1)/2^i 힙을 생성한다. 새로운 엔트리는 처음에 루트에 놓이지만 힙-순서 특성을 만족하기 위해 다운-힙 버블링을 통해 아래로 움직일 수 있다.&lt;/li&gt;
&lt;li&gt;마지막 단계에서(위 그림 (f)-(g)), 두 개의 (n - 1)/2 엔트리를 갖는 힙(전 단계에서 만들어진)을 합치고 새로운 엔트리를 하나 추가하여 n개의 엔트리를 갖는 최종 힙을 생성한다. 새로운 엔트리는 처음에는 루트에 놓이지만 힙-순서 특성을 만족하기 위해 다운-힙 버블링을 통해 아래로 움직일 수 있다.&lt;/li&gt;
&lt;/ol&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;pre id=&quot;code_1642498472161&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Algorithm BottomUpHeap(L):
    input: n = 2^(h+1)-1개의 엔트리를 저장하는 STL 리스트 L
    output: L의 엔트리를 저장하는 힙 T
    if L.empty() then
    	return an empty heap
    e &amp;lt;- L.front()
    L.pop_front()
    Split L into two lists, L1 and L2, each of size (n - 1)/2
    T1 &amp;lt;- BottomUpHeap(L1)
    T2 &amp;lt;- BottomUpHeap(L2)
    Create binary tree T with root r storing e, left subtree T1, and right subtree T2
    Perform a down-heap bubbling from the root r of T, if necessary
    return T&lt;/code&gt;&lt;/pre&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;어따 써야할지 몰라서 그냥 여기 추가!!&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선순위 큐에서 removeMin 함수를 사용하면 가장 작은 키를 엔트리를 삭제한다. 그런데 가장 작은 키 말고도 임의의 엔트리를 삭제해야 할 필요가 있다!! 따라서 새 연산이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복수의 같은 키들이 있을 수 있기 때문에 엔트리를 삭제하기 위해 키 값을 사용할 수는 없다. 대신 우선순위 큐 연산 insert(e)가 확장되어 원소 e를 삽입한 후 새롭게 생성된 엔트리의 레퍼런스 즉 위치(position)를 반환한다고 가정하자. 이 위치가 엔트리에 영구히 부착되어 만약 엔트리의 순서가 내부에서 변경되더라도 이 위치는 이 엔트리에 고정되어 있다. 따라서 위치는 각 연산이 적용되는 엔트리를 유일하게 식별할 수 있는 방법을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 추가적인 연산을 지원하는 우선순위 큐 P를 정의하자!!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;insert(e): 원소 e를 P에 삽입한 후 그 엔트리를 가리키는 위치를 반환&lt;/li&gt;
&lt;li&gt;remove(p): P로부터 p가 가리키는 엔트리를 삭제&lt;/li&gt;
&lt;li&gt;replace(p, e): p가 가리키는 엔트리에 연관된 원소를 e로 교체하고 수정된 엔트리의 위치를 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1642500023694&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template &amp;lt;typename E, typename C&amp;gt;
class AdaptPriorityQueue {		// 적응 가능한 우선순위 큐
protected:
    typedef std::list&amp;lt;E&amp;gt; ElementList;	// 원소들의 리스트
public:
    int size() const;			// 원소의 개수
    bool empty() const;			// 큐가 비었나
    const E&amp;amp; min() const;		// 최소 원소
    Position insert(const E&amp;amp; e) {	// 원소 삽입
    	typename ElementList::iterator p = L.begin();
        while (p != L.end() &amp;amp;&amp;amp; !isLess(e, *p)) ++p;	// 더 큰 원소를 탐색
        L.insert(p, e);			// p 앞에 삽입
        Position pos;			// 삽입된 위치
        pos.q = --p;		
        return pos;
    }
    void removeMin();			// 최소 원소 삭제
    void remove(const Position&amp;amp; p) {	// 위치 p의 원소 삭제
    	L.erase(p.q);			// 포지션 p 삭제
    }
    Position replace(const Position&amp;amp; p, const E&amp;amp; e) {	// 위치 p의 원소 교체
    	L.erace(p.q);			// 기존 엔트리 제거
        return insert(e);		// 새 엔트리 삽입
    }
private:
    ElementList L;				// 우선순위 큐 내용
    C isLess;					// less-than 비교자
};

class Position {				// 큐 내의 위치
private:
    typename ElementList::iterator q;	// 리스트 내의 위치
public:
    const E&amp;amp; operator*() { return *q; } // 이 위치에 있는 원소
    friend class AdaptPriorityQueue;	// 접근 허가
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr class=&quot;text-block before-moreless&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;참고 문헌&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;[1] Michael T. Goodrich, Roberto Tamassia, Davaid M. Mount, C++로 구현하는 자료구조와 알고리즘, WILEY, 2013.&lt;/span&gt;&lt;/p&gt;</description>
      <category>2022/자료구조 + 알고리즘</category>
      <author>너나나</author>
      <guid isPermaLink="true">https://guiyum.tistory.com/133</guid>
      <comments>https://guiyum.tistory.com/133#entry133comment</comments>
      <pubDate>Tue, 18 Jan 2022 18:40:24 +0900</pubDate>
    </item>
  </channel>
</rss>