<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>eunseo_thatsme.log</title>
        <link>https://velog.io/</link>
        <description>내가 공부한 것들</description>
        <lastBuildDate>Thu, 20 Oct 2022 05:43:17 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>eunseo_thatsme.log</title>
            <url>https://velog.velcdn.com/images/eunseo_thatsme/profile/7a3f4e54-74cf-42ee-a636-af6e3845799d/image.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. eunseo_thatsme.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/eunseo_thatsme" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[군집화 (Clustering)]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EA%B5%B0%EC%A7%91%ED%99%94-Clustering</link>
            <guid>https://velog.io/@eunseo_thatsme/%EA%B5%B0%EC%A7%91%ED%99%94-Clustering</guid>
            <pubDate>Thu, 20 Oct 2022 05:43:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>지금까지 공부했던 내용들을 정리한 문서입니다. 참고 서적을 정리한 내용도 있지만 제가 알고 있는 지식을 기반으로 정리한 내용도 있기 때문에 틀린 부분이 있을 수 있습니다. 혹시 발견하신다면 피드백 부탁드립니다 😁</p>
</blockquote>
<blockquote>
<p>또한 이 문서는 군집화와 군집화 알고리즘의 개념적인 부분을 다룹니다. 좀 더 심화된 이론이 필요하신 분은 다른 글을 참고해 주세요 😋</p>
</blockquote>
<p><strong>군집화(clustering)</strong>란, 데이터의 특성을 분석하여 유사한 속성을 갖는 데이터끼리 하나의 군집(cluster)으로 묶는 작업을 의미한다. 여기서 유사한 속성을 갖는 데이터란 특정 데이터 공간에서 비슷한 구역에 위치한 데이터 점들을 말한다. 기계학습 알고리즘 중 <strong><em>비지도 학습</em></strong>에 속하는 방법이며, 데이터 분석에서 군집화 기법을 이용하면 데이터가 굉장히 많아도 데이터의 특성을 파악하기 비교적 쉬워진다. 군집화 기법은 <code>소프트 클러스터링</code>과 <code>하드 클러스터링</code>으로 세분화될 수 있다.</p>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/da70a0bc-3082-4236-a65a-a40d929b047b/image.png>

<p>  <i>군집화 예시</i></p>
</p>

<blockquote>
<p>💡 <strong>소프트 클러스터링과 하드 클러스터링</strong></p>
</blockquote>
<ul>
<li><strong>소프트 클러스터링(soft clustering)</strong>: 각 데이터가 여러 개의 군집에 속할 수 있는 방법</li>
<li><strong>하드 클러스터링(hard clustering)</strong>: 각 데이터가 오직 한 개의 군집에만 속할 수 있는 방법</li>
</ul>
<h4 id="✅-거리-함수distance-function">✅ 거리 함수(Distance Function)</h4>
<p>데이터 점들을 특징짓기 위해 군집화에서는 기본적으로 거리 함수(distance function)를 사용한다. 거리 함수는 말 그대로 두 원소 쌍(여기서는 데이터 샘플 간) 사이의 거리를 계산하는 함수로써, 군집화에서는 보통 그 값이 작으면 비슷한 속성(혹은 비슷한 공간에 위치)을 가지고 있다고 판단한다. 대표적인 거리 함수는 다음과 같으며, 일반적으로 유클리디언 거리가 많이 사용된다.</p>
<table>
<thead>
<tr>
<th>거리 함수 종류</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>맨하탄 거리 (Manhattan Distance, L1 Norm)</td>
<td>두 점이 존재하는 좌표에서 각 차원 별 거리의 합 <em>(Fig A 빨간선, 파란선, 노란선)</em></td>
</tr>
<tr>
<td>유클리디언 거리 (Euclidean Distance, L2 Norm)</td>
<td>두 점의 최단 거리 <em>(Fig A 초록선)</em></td>
</tr>
<tr>
<td>마할라노비스 거리 (Mahalanobis Distance)</td>
<td>표본 점과 분포 사이의 거리, 표준 편차 개수를 기준으로 y가 평균에서 얼마나 떨어져 있는지를 나타냄 <em>(Fig B 참고)</em></td>
</tr>
<tr>
<td>상관계수 거리 (Correlation Distance)</td>
<td>데이터 간의 피어슨 상관계수 값, 데이터 전체의 경향성을 비교하기 위한 척도</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/e828f36d-a712-4562-aa4b-42818037fce6/image.png" alt=""></p>
<p><a href="https://ko.wikipedia.org/wiki/%EB%A7%A8%ED%95%B4%ED%8A%BC_%EA%B1%B0%EB%A6%AC">Fig A 출처</a>, <a href="https://ratsgo.github.io/machine%20learning/2017/04/17/KNN/">Fig B 출처</a></p>
<h4 id="✅-군집화-결과-평가-하기">✅ 군집화 결과 평가 하기</h4>
<p>군집화 알고리즘을 통해 만들어진 군집이 잘 만들어졌는지 평가하기 위한 척도가 존재한다. 여러 지표가 존재하지만 유명한 지표들은 다음과 같다.</p>
<table>
<thead>
<tr>
<th>군집화 평가 지표</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Dunn Index</strong></td>
<td>군집 내(Intra Cluster) 요소간 최대 거리에 대한 군집 간(Inter Cluster) 거리의 최소 거리의 비를 계산한 지표, 값이 작을수록 군집화 결과가 좋음을 의미</td>
</tr>
<tr>
<td><strong>Silhouette 계수</strong></td>
<td>각 데이터의 그 데이터가 속한 군집 내(Intra Cluster) 요소들과의 유사도와 인접한 군집과의 유사도를 비교하는 지표, 값이 클수록 군집화 결과가 좋음을 의미</td>
</tr>
</tbody></table>
<blockquote>
<p>💡 <strong>(참고) 군집화 평가 지표 수식</strong></p>
</blockquote>
<p>** 1. Dunn Index ** </p>
<ul>
<li>$\delta(C_i, Cj)$: 군집 $i$와 $j$ 간의 거리</li>
<li>$\triangle_k$: $k$번째 군집 내 거리</li>
</ul>
<p>$$
DI_m  = \frac{\underset{1\le i &lt; j \le m}{\min}\delta(C_i, Cj)}{\underset{1\le k \le m}{\max}\triangle_k}
$$</p>
<p>** 2. Silhouette 계수 **</p>
<ul>
<li>자세한 계산 방법은 <a href="https://en.wikipedia.org/wiki/Silhouette_(clustering)">링크</a> 참고
$$
SC = \underset{\kappa}{\max}\ \tilde{s}(\kappa)
$$</li>
</ul>
<h1 id="군집화-알고리즘">군집화 알고리즘</h1>
<hr>
<h2 id="1️⃣-k-평균-군집화-k-means-clustering">1️⃣ K-평균 군집화 (K-means Clustering)</h2>
<p><strong>K-평균 군집화</strong>는 대표적인 군집화 알고리즘으로, 주어진 데이터 셋을 K개의 군집으로 나누는 작업을 수행한다. K-평균 군집화를 수행하기 위해서는 <code>K</code>값을 사람이 직접 설정해 주어야 한다. K-평균 군집화가 수행되는 과정은 다음과 같다.</p>
<blockquote>
<p>💡 <strong>K-평균 군집화 프로세스</strong></p>
</blockquote>
<p><strong>1.</strong> <code>k</code>개의 데이터 표본을 선택하여 <code>군집 중심점(centroid)</code>으로 정한다. <em>(초기값 설정 과정)</em>
<strong>2.</strong> 군집 중심점으로 선택된 데이터 표본을 제외한 다른 데이터들은, 군집 중심점과의 <code>거리(distance)</code>를 계산하여 가장 가까운 군집에 할당한다.
<strong>3.</strong> 모든 데이터의 군집이 결정되었다면, 할당된 데이터들을 기준으로 군집의 중심점을 다시 계산한다. <em>(군집 중심은 클러스터 내 데이터들의 평균값으로 계산된다.)</em>
<strong>4.</strong> 최적의 군집이 만들어질 때까지 <code>2</code>번과 <code>3</code>번이 반복적으로 수행된다. <em>(군집 중심 변동이 거의 없을 때까지 수행)</em></p>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/43ac1acf-1ef5-438b-ac9a-f2b1842eea96/image.png" alt="">
<a href="https://ko.wikipedia.org/wiki/K-%ED%8F%89%EA%B7%A0_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98">그림출처</a></p>
<p><a href="https://scikit-learn.org/stable/">scikit-learn</a> 라이브러리를 사용하여 <code>Python3</code>으로 구현하면 다음과 같다.</p>
<blockquote>
<p>K-평균 군집화를 수행하기 전 초기 군집 중심점을 정할 때 사용하는 방법들이 존재한다. 여러 방법이 있지만 아래 예제에서는 <a href="http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf">k-means++</a> 방법을 사용하니 참고하자.</p>
</blockquote>
<ul>
<li>k-means++: 초기에 무작위로 선택된 중심점을 기준으로 각 데이터 간의 거리를 계산하여 거리 비례 확률에 따라 다음 중심점을 정하는 방법, 처음에 선택된 중심점으로부터 최대한 먼 곳에 위치한 점을 선택하여 군집을 잘 나눌 수 있도록 함, k개가 될 때까지 반복</li>
</ul>
<pre><code class="language-python">from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from pandas import DataFrame
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

# Generate data samples
np.random.seed(1)
data, _ = make_blobs(n_samples=1000, centers=5, cluster_std=0.8)

# Fit kmeans model
KMeans = KMeans(n_clusters=5, init=&quot;k-means++&quot;)
KMeans.fit(data)

# Label of each point
labels = KMeans.labels_ 
print(&#39;labels: &#39;, labels) 

# Coordinates of cluster centers
centroids = KMeans.cluster_centers_ 
print(&#39;centroids: &#39;, centroids)

# Make dataframe
df = DataFrame({&#39;X&#39;:data[:,0], &#39;Y&#39;: data[:,1]})

# Visualize clustering result
plt.figure(figsize=(15, 10))
sns.scatterplot(x=&quot;X&quot;, y=&quot;Y&quot;, data=df, hue=labels, palette=&#39;coolwarm&#39;)
plt.scatter(centroids[:, 0], centroids[:, 1], c=&#39;red&#39;, alpha=0.5, s=150)</code></pre>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/9a738658-c8a1-44e0-a896-3d195960bc78/image.png>

<p>  <i>K-평균 군집화 결과</i></p>
</p>

<h2 id="2️⃣-계층적-군집화-hierarchical-clustering">2️⃣ 계층적 군집화 (Hierarchical Clustering)</h2>
<p><strong>계층적 군집화</strong>란 트리 모형을 이용해 개별 개체들을 순차적, 계층적으로 유사한 개체 내지 그룹과 통합하여 군집화를 수행하는 알고리즘이다. K-평균 군집화(K-means Clustering)와 다르게 <u>사전에 군집 수를 정해주지 않아도 학습이 가능</u>하다. 계층적 군집화는 보통 <a href="https://en.wikipedia.org/wiki/Dendrogram">덴드로그램(dendrogram)</a>이라고 하는 그래프를 이용해 시각화 할 수 있으며, 일정 높이에서 덴드로그램을 자르는 방식으로 군집의 개수를 정한다. 계층적 군집화는 <code>합병에 의한 방법(agglomerative)</code>과 <code>분할에 의한 방법(divise)</code>으로 나눌 수 있다.
<img src="https://velog.velcdn.com/images/eunseo_thatsme/post/173f9e02-5fb6-4a43-b6c0-5dc8c98a7eeb/image.png" alt=""></p>
<p><a href="https://medium.com/h-document/%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EB%A7%81-%EA%B5%B0%EC%A7%91-%EB%B6%84%EC%84%9D-%EA%B3%84%EC%B8%B5%EC%A0%81-%EA%B5%B0%EC%A7%91-a7cac74beb6c">그림참고</a></p>
<h3 id="✅-합병에-의한-방법agglomerative-methods">✅ 합병에 의한 방법(Agglomerative methods)</h3>
<blockquote>
<p>💡 <strong>계층적 군집화 프로세스 - 합병</strong></p>
</blockquote>
<p><strong>1.</strong> 1개의 개체를 가지는 n개의 군집을 생성한다.
<strong>2.</strong> 군집 간의 거리를 계산하여 가장 유사한 군집 쌍을 찾아 하나의 군집으로 묶는다.
<strong>3.</strong> <code>2</code>를 반복하여 모든 데이터가 하나의 군집이 될 때까지 군집 합병을 수행한다.</p>
<p>합병에 의한 계층적 군집화 방법에서, <code>군집 간 거리</code>를 계산하기 위해 사용되는 여러 가지 방법이 존재하며, 데이터에 따라 군집화가 잘 되는 방법을 선택하면 된다. 종류는 다음과 같다.</p>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>최단 연결법 (Single Linkage method)</td>
<td>군집 내의 데이터 간 거리 중 가장 작은 값을 군집 간 거리로 설정하는 방법</td>
</tr>
<tr>
<td>최장 연결법 (Complete Linkage method)</td>
<td>군집 내의 데이터 간 거리 중 가장 큰 값을 군집 간 거리로 설정하는 방법</td>
</tr>
<tr>
<td>평균 연결법 (Average Linkage method)</td>
<td>군집 내의 데이터 간 거리에서 평균한 값을 군집 간 거리로 설정하는 방법</td>
</tr>
<tr>
<td>중심 연결법 (Centroid Linkage method)</td>
<td>군집의 중심(centroid) 간의 거리를 군집 간 거리로 설정하는 방법</td>
</tr>
<tr>
<td>Ward 연결법 (Ward Linkage method)</td>
<td>ward: (두 군집 간 제곱합 - (군집 내 제곱합의 합))를 군집 간 거리로 설정하는 방법</td>
</tr>
</tbody></table>
<p><a href="https://scipy.org/">Scipy</a> 라이브러리를 사용하여 <code>Python3</code>으로 구현하면 다음과 같다.</p>
<blockquote>
<p>아래 예제는 <a href="https://archive.ics.uci.edu/ml/machine-learning-databases/00292/Wholesale%20customers%20data.csv">Wholesale customers data</a> 데이터를 사용하여 구현한 예제이므로, 사전에 다운로드가 필요합니다.</p>
</blockquote>
<pre><code class="language-python">from scipy.cluster.hierarchy import linkage, dendrogram
from sklearn.preprocessing import normalize
import matplotlib.pyplot as plt
import pandas as pd

# dataset URL
# https://archive.ics.uci.edu/ml/machine-learning-databases/00292/Wholesale%20customers%20data.csv
data = pd.read_csv(&#39;Wholesale customers data.csv&#39;)

data_scaled = normalize(data)
data_scaled = pd.DataFrame(data_scaled, columns=data.columns)

linkage_list = [&#39;single&#39;, &#39;complete&#39;, &#39;average&#39;, &#39;centroid&#39;, &#39;ward&#39;]

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(15, 10))
for i in range(len(linkage_list)):
    hierarchical = linkage(data_scaled, method=linkage_list[i])
    dn = dendrogram(hierarchical , ax=axes[i%3][i%2])
    axes[i%3][i%2].title.set_text(linkage_list[i])

plt.tight_layout()
plt.show()</code></pre>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/bb6b3ff8-3dd8-4b6e-93d6-1ce74c293dc5/image.png>

<p>  <i>합병에 의한 계층적 군집화 결과, 군집 거리 계산 방법 별 덴드로그램</i></p>
</p>


<h3 id="✅-분할에-의한-방법divisive-methods">✅ 분할에 의한 방법(Divisive methods)</h3>
<p><code>합병에 의한 계층적 군집화</code> 방법과는 반대로 하나의 군집에서 시작하며, 1개의 개체가 하나의 군집이 되는 순간까지 군집을 분할해 나가는 방식으로 군집화를 수행하는 방법이다.</p>
<blockquote>
<p>💡 <strong>계층적 군집화 프로세스 - 분할</strong></p>
</blockquote>
<p><strong>1.</strong> 모든 데이터가 속해 있는 하나의 군집에서 시작한다.
<strong>2.</strong> 군집을 가장 유사하지 않은 두 개의 군집으로 나눈다.
<strong>3.</strong> 2를 반복하여 각 데이터가 하나의 군집이 될 때까지 군집 분할을 수행한다.</p>
<p><del><em>(코드 구현 예정)</em></del></p>
<h2 id="3️⃣-밀도-기반-군집화-dbscan">3️⃣ 밀도 기반 군집화 (DBSCAN)</h2>
<p><strong>밀도 기반 군집화(Density-based spatial clustering of application with noise)</strong>는 주어진 데이터의 밀도를 기반으로 군집화를 수행하는 알고리즘으로, 데이터 점 간 거리를 이용하여 클러스터링 하는 K-평균 군집화, 계층적 군집화와는 다르게, 데이터가 모여있는, 즉 밀도가 높은 부분을 찾아 군집화하는 방법이다.</p>
<p>밀도 기반 군집화의 과정에 앞서 학습 과정 이해를 위해 필요한 <code>기본 하이퍼 파라미터</code>와 <code>주요 개념</code>을 소개한다.</p>
<h3 id="✅-밀도-기반-군집화dbscan-기본-하이퍼-파라미터">✅ 밀도 기반 군집화(DBSCAN) 기본 하이퍼 파라미터</h3>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>엡실론 (epsilon)</strong></td>
<td>어떤 한 점으로 부터의 거리</td>
</tr>
<tr>
<td><strong>점 최소 개수 (min_samples)</strong></td>
<td>어떤 점을 핵심 점(중심점)으로 간주하는 점들의 최소 개수</td>
</tr>
<tr>
<td><p align="center" style="font-size:0.8em; color:gray"></td>
<td></td>
</tr>
<tr>
<td><img src=https://velog.velcdn.com/images/eunseo_thatsme/post/ff6469ca-25da-4b7b-8edc-1cb67c13c690/image.png></td>
<td></td>
</tr>
</tbody></table>
<p>  <i>점 최소 개수(minpts)가 4개인 경우</i></p>
</p>

<h3 id="✅-밀도-기반-군집화dbscan-주요-개념">✅ 밀도 기반 군집화(DBSCAN) 주요 개념</h3>
<table>
<thead>
<tr>
<th>이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Core Point</strong></td>
<td>epsilon 반경 내에 min_samples 이상의 데이터를 포함하는 군집의 중심점</td>
</tr>
<tr>
<td><strong>Border Point</strong></td>
<td>epsilon 반경 내에 min_samples 이상의 데이터를 포함하지 않지만, 해당 반경에 core point를 가지고 있는 점</td>
</tr>
<tr>
<td><strong>Noise Point</strong></td>
<td>epsilon 반경 내에 min_samples 이상의 데이터를 포함하지 않고, 해당 반경에 core point도 없는 점</td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/405e3c87-9625-4bbd-aaff-6fe9b1c8fd7a/image.png" alt=""></td>
<td></td>
</tr>
</tbody></table>
<p><a href="https://medium.com/@agarwalvibhor84/lets-cluster-data-points-using-dbscan-278c5459bee5">그림참고</a></p>
<p>밀도 기반 군집화는 다음의 단계로 수행된다.</p>
<blockquote>
<p>💡 <strong>밀도 기반 군집화 프로세스</strong></p>
</blockquote>
<ol>
<li>공간에서 임의의 점을 선택한다.</li>
<li>앞 단계에서 선택한 임의의 점을 기준으로, <code>epsilon</code> 거리 범위 내에 있는 점의 개수를 센다.<ul>
<li>여기서 만약 점의 개수가 <code>minpts-1</code> 이상이면 해당 점을 <code>core point</code>로, 그렇지 않으면 <code>noise point</code>로 분류한다.</li>
</ul>
</li>
<li>앞 단계의 점이 <code>core point</code>가 되었다면, 해당 점을 기준으로 <code>epsilon</code> 범위 내에 있는 모든 데이터를 하나의 군집으로 지정한다.<ul>
<li>여기서 묶은 군집에 또 다른 <code>core point</code>가 속해 있다면, 그 점의 이웃 점들을 <code>border point</code>에 도달할 때까지 차례로 방문하여 군집에 포함시킨다.</li>
</ul>
</li>
<li>모든 데이터가 어떤 군집에 속하거나 혹은 <code>noise point</code>가 될 때까지 <code>1</code>-<code>3</code>을 반복한다.</li>
</ol>
<p><a href="https://scikit-learn.org/stable/">scikit-learn</a> 라이브러리를 사용하여 <code>Python3</code>으로 구현하면 다음과 같다.</p>
<pre><code class="language-python">from sklearn.datasets import make_blobs
from sklearn.cluster import DBSCAN
from pandas import DataFrame
import mglearn
import numpy as np

# Generate data samples
np.random.seed(1)
data, _ = make_blobs(n_samples=15, centers=3, cluster_std=0.8)

# Fit kmeans model
DBSCAN = DBSCAN(eps=3, min_samples=2)
DBSCAN.fit(data)

# Labels of each point
labels = DBSCAN.labels_ 
print(&#39;labels: &#39;, labels) 

# Visualize clustering result
mglearn.plots.plot_dbscan()</code></pre>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/0df5f0ba-80c4-4de0-a7b5-e35b518e820f/image.png>

<p>  <i>DBSCAN 예제 결과</i></p>
</p>

<blockquote>
<p>✅ <strong>DBSCAN 예제 결과 설명</strong>
<code>core point</code>는 크게, <code>border point</code>는 작게 표시되었으며, <code>noise point</code>는 흰색, 그 외에 군집에 속한 데이터 들은 색상이 칠해졌다. 해당 예시에서는 <code>min_sample: 3, eps: 1.5</code>가 데이터를 가장 잘 나눈 것으로 보인다.</p>
</blockquote>
<h2 id="4️⃣-스펙트럼-군집화-spectral-clustering">4️⃣ 스펙트럼 군집화 (Spectral Clustering)</h2>
<p><strong>스펙트럼 군집화(Spectral Clustering)</strong>는 그래프 기반(graph-based) 군집화 기법으로, 데이터 간 절대적 거리를 기반으로 군집화를 수행하는 K-평균 군집화(K-means clustering), 계층적 군집화(Hierarchical clustering)와는 다르게, <u>데이터 간의 상대적 관계나 유사성을 고려</u>하여 군집화를 수행한다. </p>
<blockquote>
<p>💡 <strong>스펙트럼 군집화 프로세스</strong></p>
</blockquote>
<ol>
<li>주어진 데이터셋에서 데이터 점들 간의 관계를 정의한다.<ul>
<li>여기서 가중치는 데이터 간의 거리가 멀수록 커지고, 가까울수록 작아지도록 한다.</li>
<li>보통 <a href="https://en.wikipedia.org/wiki/Gaussian_filter">가우시안 커널</a>을 사용하여 가중치를 정의한다.  </li>
</ul>
</li>
<li>앞 단계에서 정의한(혹은 계산한) 값들을 바탕으로 <a href="https://en.wikipedia.org/wiki/Adjacency_matrix">인접 행렬(Adjacency matrix)</a> 형태의 유사도 행렬(Affinity matrix)을 만든다.<ul>
<li><a href="https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)#:~:text=are%20special%20cases.-,Weighted%20graph,-%5Bedit%5D">무방향 가중치 그래프(Undirected Weighted Graph)</a> 형태로 볼 수 있다.</li>
<li><code>fully connected graph</code>, <code>k-nearest neighbor graph</code>, <code>ε-neighborhood graph</code> 등 그래프를 만드는 여러 가지 방법들이 존재하며, 각 방법들이 가진 장단점이 다르므로 상황에 맞게 사용하도록 하자.</li>
</ul>
</li>
<li>만들어진 그래프에서 군집을 잘 나눌 수 있는 간선(edge)들을 선택하여 잘라낸다. <em>(Fig C 참고)</em><ul>
<li>그래프를 자르는 방법으로 <a href="https://en.wikipedia.org/wiki/Minimum_cut">Minimum Cut</a>이 보편적이며, 이 밖에도 <code>Ratio cut</code>, <code>Normalized cut</code> 등이 존재한다.</li>
<li>그래프 컷은 각 방법에서 정의된 목적 함수(objective function)의 출력 값을 최소화하는 고유 벡터(eigenvector)를 찾아 이에 해당하는 가중치를 가진 간선들을 자르는 방식으로 수행된다.</li>
</ul>
</li>
</ol>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/1a63b69c-b103-4c28-9995-45f4920df3cd/image.png
>

<p>  <i>Fig C. 그래프 컷 예시</i></p>
</p>

<p><a href="https://scikit-learn.org/stable/">scikit-learn</a> 라이브러리를 사용하여 <code>Python3</code>으로 구현하면 다음과 같다.</p>
<pre><code class="language-python">from sklearn.datasets import make_circles
from sklearn.cluster import SpectralClustering, AgglomerativeClustering
import matplotlib.pyplot as plt

X, y = make_circles(n_samples=(250,500)
                                , random_state=3
                                , noise=0.04
                                , factor = 0.3)

fig, ax = plt.subplots(1,3, figsize=(15,4))

ax[0].scatter(X[:, 0], X[:, 1], alpha=0.7)
ax[0].set_title(&quot;Raw Data&quot;)

# Spectral clustering
pred = SpectralClustering(n_clusters=2
                            , eigen_solver=&quot;arpack&quot;
                            , affinity=&quot;nearest_neighbors&quot;,).fit_predict(X)

ax[1].scatter(X[:, 0], X[:, 1], c=pred, cmap=plt.cm.rainbow, alpha=0.7)
ax[1].set_title(&quot;Spectral Clustering&quot;)

# Agglomerative clustering
pred = AgglomerativeClustering(n_clusters=2
                                  , linkage=&quot;ward&quot;).fit_predict(X)
ax[2].scatter(X[:, 0], X[:, 1], c=pred, cmap=plt.cm.rainbow, alpha=0.7)
ax[2].set_title(&quot;Agglomerative Clustering&quot;)</code></pre>
<p align="center" style="font-size:0.8em; color:gray">
  <img src=https://velog.velcdn.com/images/eunseo_thatsme/post/affc8154-dd22-4e9c-9360-46ed433e2d76/image.png>

<p>  <i>Spectral Clustering과 합병에 의한 계층적 군집화(Agglomerative Clustering) 비교 예제 결과 </i></p>
</p>

<hr>
<h3 id="references">References</h3>
<pre><code>고민삼,『Machine Learning』, 강의 교안.</code></pre><pre><code>ratsgo, &quot;K-Nearest Neighbor Algorithm&quot;, Github.io, 2017.4.17
, https://ratsgo.github.io/machine%20learning/2017/04/17/KNN/</code></pre><pre><code>Tobigs, &quot;클러스터링 실습 (1) (EDA,Sklearn)&quot;, Gitbook
, https://tobigs.gitbook.io/tobigs/data-analysis/undefined-3/python-2-1</code></pre><pre><code>조대협, &quot;클러스터링 #3 - DBSCAN (밀도 기반 클러스터링)&quot;, Tistory, 2017.10.13 
, https://bcho.tistory.com/m/1205</code></pre><pre><code>Vibhor Agarwal, &quot;Let’s cluster data points using DBSCAN&quot;, Medium, 2019.6.28 
, https://medium.com/@agarwalvibhor84/lets-cluster-data-points-using-dbscan-278c5459bee5</code></pre><pre><code>Satyam Kumar, &quot;Hierarchical Clustering: Agglomerative and Divisive — Explained&quot;, Medium, 2020.8.3 
, https://towardsdatascience.com/hierarchical-clustering-agglomerative-and-divisive-explained-342e6b20d710</code></pre><pre><code>Zaid.Ryu, &quot;[ML with Python] 3.비지도 학습 알고리즘 (3-3) DBSCAN&quot;, Github.io, 2020.12.26 
, https://jhryu1208.github.io/data/2020/12/26/ML_DBSCAN/</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[베스트앨범]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EB%B2%A0%EC%8A%A4%ED%8A%B8%EC%95%A8%EB%B2%94</link>
            <guid>https://velog.io/@eunseo_thatsme/%EB%B2%A0%EC%8A%A4%ED%8A%B8%EC%95%A8%EB%B2%94</guid>
            <pubDate>Thu, 21 Jul 2022 06:12:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/42579">https://school.programmers.co.kr/learn/courses/30/lessons/42579</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>노래 장르(<code>genres</code>)와 재생 횟수(<code>plays</code>)가 주어질 때, 장르별 가장 많이 재생된 노래 <code>2</code>개씩 모아 해당 노래의 인덱스(index)를 다음 조건에 맞추어 <strong>순서대로</strong> 출력하는 문제</p>
<ol>
<li>속한 노래가 많이 재생된 장르를 먼저 수록한다.</li>
<li>장르 내에서 많이 재생된 노래를 먼저 수록한다.</li>
<li>장르 내에서 재생 횟수가 같은 노래 중에서는 고유 번호(인덱스, index)가 낮은 노래를 먼저 수록한다.</li>
</ol>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li><p>모든 테스트 케이스 통과</p>
<pre><code class="language-python">def solution(genres, plays):
  streamingInfo = {}
  for i, (g, p) in enumerate(zip(genres, plays)):
      value = streamingInfo.get(g, [0, []])
      value[0] += p
      value[1].append((p, i))
      streamingInfo[g] = value

  answer = []
  for songs in sorted(streamingInfo.values(), reverse=True):
      songs[1].sort(key=lambda x:x[0], reverse=True)
      answer += [s[1] for s in songs[1][:2]]
  return answer</code></pre>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
</li>
<li><p>딕셔너리(dictionary) 자료형을 사용하여 주어진 곡들을 장르별로 (재생횟수 <code>p</code>, 인덱스 <code>i</code>) 형태로 저장한다.</p>
<ul>
<li>장르 별 노래들의 재생 횟수의 총합도 같이 저장하여 이후 <code>속한 노래가 많이 재생된 장르</code>를 찾는데 사용</li>
</ul>
</li>
<li><p>곡 정보가 저장된 딕셔너리가 만들어졌다면, 문제 에서 주어진 조건에 따라 값들을 정렬한다.</p>
<ul>
<li><code>속한 노래가 많이 재생된 장르를 먼저 수록</code>하기 위해 우선 장르별 곡들의 총 재생 횟수를 기준으로 내림차순 정렬</li>
<li>다음으로는 <code>장르 내에서 많이 재생된 노래를 먼저 수록</code>해야 하므로 노래 별 재생 횟수를 기준으로 내림차순 정렬 <em>(이 과정에서 3번 조건은 자동으로 정렬됨)</em></li>
</ul>
</li>
<li><p>정렬된 리스트 <code>songs</code>에서 해당 곡의 인덱스를 순서대로 <code>answer</code>에 저장하여 답을 구한다.</p>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[단어 변환]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EB%8B%A8%EC%96%B4-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@eunseo_thatsme/%EB%8B%A8%EC%96%B4-%EB%B3%80%ED%99%98</guid>
            <pubDate>Wed, 20 Jul 2022 08:32:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://school.programmers.co.kr/learn/courses/30/lessons/43163">https://school.programmers.co.kr/learn/courses/30/lessons/43163</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>두 개의 단어 <code>begin</code>, <code>target</code>과 단어의 집합 <code>words</code>가 주어질 때, <code>begin</code>을 <code>target</code>으로 변환하는 최소 단계 수를 구하는 문제</p>
<ul>
<li>한 번에 한 개의 알파벳만 바꿀 수 있음</li>
<li>words에 있는 단어로만 변환할 수 있음</li>
<li>모든 단어의 길이는 같으며, 중복되는 단어는 없음</li>
<li>변환할 수 없는 경우에는 0를 <code>return</code></li>
</ul>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 너비 우선 탐색 (Breadth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li><p>모든 테스트 케이스 통과</p>
<pre><code class="language-python">from collections import deque
def solution(begin, target, words):
  if target not in words:
      return 0

  queue = deque([(0, begin)])
  while queue:
      depth, tempWord = queue.popleft()
      if tempWord == target:
          return depth

      for w in words:
          diffChar = 0
          for i in range(len(w)):
              if tempWord[i] != w[i]:
                  diffChar += 1
          if diffChar == 1:
              queue.append((depth + 1, w))
  return 0</code></pre>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
</li>
<li><p>현재 단어 <code>tempWord</code>가 <code>target</code> 단어와 같은지 확인한다.</p>
</li>
<li><p>만약 <code>tempWord</code>가 <code>target</code> 단어와 같지 않다면 <code>word</code>에서 바꿀 수 있는 문자를 탐색한다.</p>
<ul>
<li>한 번에 한 문자만 바꿀 수 있기 때문에, <code>tempWord</code>에서 하나의 문자만 다른 문자를 찾는다.</li>
</ul>
</li>
<li><p><code>tempWord</code>가 <code>target</code>와 같아질 때 까지 반복하며, 만약 바꿀 수 없다면 <code>0</code>을 <code>return</code> 한다.</p>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Prim’s (MST): Special Subtree]]></title>
            <link>https://velog.io/@eunseo_thatsme/Prims-MST-Special-Subtree</link>
            <guid>https://velog.io/@eunseo_thatsme/Prims-MST-Special-Subtree</guid>
            <pubDate>Thu, 07 Jul 2022 07:07:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/primsmstsub/problem?isFullScreen=true">https://www.hackerrank.com/challenges/primsmstsub/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p><a href="https://en.wikipedia.org/wiki/Prim%27s_algorithm">프림 알고리즘(Prim&#39;s algorithm)</a>을 이용한  <a href="https://en.wikipedia.org/wiki/Minimum_spanning_tree">최소 신장 트리(Minimum spanning tree)</a> 탐색 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 프림 알고리즘(Prim&#39;s algorithm)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/PrimAlgDemo.gif/200px-PrimAlgDemo.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Prim%27s_algorithm">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">from collections import defaultdict
import heapq
</code></pre>
</li>
</ul>
<p>def prims(n, edges, start):
    visited = [0] * (n + 1)
    graph = defaultdict(list)
    for u, v, w in edges:
        graph[u].append([w, u, v])
        graph[v].append([w, v, u])</p>
<pre><code>heapq.heapify(graph[start])
visited[start] = 1
total_weight = 0

while graph[start]:
    w, u, v = heapq.heappop(graph[start])
    if visited[v] == 0:
        visited[v] = 1
        total_weight += w
        for edge in graph[v]:
            if visited[edge[2]] == 0:
                heapq.heappush(graph[start], edge)

return total_weight</code></pre><p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>간선 정보가 저장된 <code>edges</code>를 이용하여 <code>graph</code>를 만든다.</li>
<li><code>heapq</code> 라이브러리를 사용하여 <code>graph</code>를 <a href="https://en.wikipedia.org/wiki/Binary_heap">최소 힙(Min heap)</a> 형태로 만든다.<ul>
<li>가중치가 가장 작은 간선(edge)을 우선적으로 선택해야 하므로 최소 힙을 사용한다.</li>
</ul>
</li>
<li>모든 노드를 방문할 때 까지 <code>graph</code>를 탐색한다.<ul>
<li><code>visited</code>에 방문 여부를 기록하여 재방문하지 않도록 한다.</li>
<li>노드를 방문할 때 마다 <code>total_weight</code>에 가중치를 더하고, 반복문이 종료되면 <code>return</code>하여 정답을 구한다.</li>
</ul>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[네트워크]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</link>
            <guid>https://velog.io/@eunseo_thatsme/%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC</guid>
            <pubDate>Thu, 07 Jul 2022 06:52:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://programmers.co.kr/learn/courses/30/lessons/43162">https://programmers.co.kr/learn/courses/30/lessons/43162</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>주어진 노드(node) 연결 데이터에서 그래프의 개수를 세는 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 깊이 우선 탐색(Depth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Depth-first_search">그림출처:Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def dfs(computers, i, visited):
  visited[i] = True
  for j in range(len(computers)):
      if computers[i][j] == 1 and visited[j] == 0:
          dfs(computers, j, visited)
</code></pre>
</li>
</ul>
<p>def solution(n, computers):
    answer = 0
    visited = [0] * n
    for i in range(n):
        if visited[i] != True:
            dfs(computers, i, visited)
            answer += 1
    return answer</p>
<p>``` </p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Even Tree]]></title>
            <link>https://velog.io/@eunseo_thatsme/Even-Tree</link>
            <guid>https://velog.io/@eunseo_thatsme/Even-Tree</guid>
            <pubDate>Wed, 06 Jul 2022 10:22:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/even-tree/problem?isFullScreen=true">https://www.hackerrank.com/challenges/even-tree/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>주어진 트리(tree)의 간선(edge)을 끊었을 때 만들어지는 서브 트리(subtree)의 노드 갯수가 반드시 짝수여야 할 때, 끊을 수 있는 간선의 최대 갯수를 구하는 문제</p>
<ul>
<li>주어지는 트리의 노드 개수는 양수</li>
</ul>
<hr>
<h3 id="💡-idea">💡 Idea</h3>
<p>최대한 많은 간선, 즉 최대한 많은 서브 트리를 만들기 위해서는 서브 트리를 구성하는 노드가 최대한 적어야 한다. 이를 구현하기 위해서 리프 노드(leaf node)부터 탐색하여 잘라나가는 방식을 채택했다.</p>
<p>우선, 각 노드별 자식 노드의 갯수를 센다.
<img src="https://velog.velcdn.com/images/eunseo_thatsme/post/1864d34d-c20d-482a-830e-e81cbbe08366/image.png" alt=""></p>
<p>모든 노드의 자식 노드의 갯수를 세었으면, 리프 노드부터 탐색하여 자신을 포함한 자식 노드의 갯수가 양수인 노드를 찾는다. 찾은 노드의 부모와 연결되어 있는 간선을 끊어보면, 양수 개의 노드로 구성된 서브 트리를 만들 수 있게 된다. <em>(여기서 리프 노드부터 탐색하는 이유는 주어진 트리를 최대한 조각내기 위함이다.)</em>
<img src="https://velog.velcdn.com/images/eunseo_thatsme/post/488a51be-0658-44e7-8fd9-23f242ef9cb6/image.png" alt=""></p>
<p>위의 과정을 더 이상 끊을 수 있는 간선이 없을 때까지 반복한다. 이 방법이 가능한 이유는, 문제에서 <strong>주어지는 그래프의 노드 수가 무조건 양수</strong>라는 조건이 있기 때문이다. <code>짝수 - 짝수 = 짝수</code>이므로 조각난 서브 트리들의 노드 개수가 무조건 양수가 될 수 밖에 없다.
<img src="https://velog.velcdn.com/images/eunseo_thatsme/post/88cb2bfc-3257-47f3-b82f-947a7bcca278/image.png" alt=""></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li><p>모든 테스트 케이스 통과</p>
<pre><code class="language-python">def evenForest(t_nodes, t_edges, t_from, t_to):
  tree = [[] for _ in range(t_nodes + 1)]
  for f, t in zip(t_from, t_to):
      tree[t].append(f)

  cumulativeSums = [1] * (t_nodes + 1)
  for pa in range(t_nodes, 1, -1):
      if tree[pa] != []:
          for ch in tree[pa]:
              cumulativeSums[pa] += cumulativeSums[ch]

  edges = 0
  for sums in cumulativeSums:
      if sums % 2 == 0:
          edges += 1
  return edges</code></pre>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
</li>
<li><p>각 노드별 자식 노드 정보가 저장된 <code>tree</code>를 만든다.</p>
<ul>
<li>문제에서는 <code>t_from</code>의 부모 노드로 <code>t_to</code>가 주어지므로, <code>tree[t_to].append(t_from)</code>을 수행하여 트리를 만든다.</li>
</ul>
</li>
<li><p>만들어진 <code>tree</code>의 노드를 노드 번호 역순으로 탐색하여 해당 노드의 자식 노드 갯수를 저장한 <code>cumulativeSums</code> 리스트를 만든다.</p>
<ul>
<li>노드 번호를 인덱스로 하여 자식 노드 갯수를 저장하자.</li>
</ul>
</li>
<li><p>위의 단계가 끝나면 <code>cumulativeSums</code> 안의 양수 갯수를 세어 정답을 구한다.</p>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kruskal (MST): Really Special Subtree]]></title>
            <link>https://velog.io/@eunseo_thatsme/Kruskal-MST-Really-Special-Subtree</link>
            <guid>https://velog.io/@eunseo_thatsme/Kruskal-MST-Really-Special-Subtree</guid>
            <pubDate>Mon, 27 Jun 2022 04:50:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/kruskalmstrsub/problem?isFullScreen=true">https://www.hackerrank.com/challenges/kruskalmstrsub/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p><a href="https://en.wikipedia.org/wiki/Kruskal%27s_algorithm">크루스칼 알고리즘(Kruskal&#39;s algorithm)</a>를 이용한 <a href="https://en.wikipedia.org/wiki/Minimum_spanning_tree">최소 신장 트리(Minimum spanning tree)</a> 탐색 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 크루스칼 알고리즘(Kruskal&#39;s algorithm)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/b/bb/KruskalDemo.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Kruskal%27s_algorithm">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과</li>
<li><a href="https://techblog-history-younghunjo1.tistory.com/262">참고 코드</a><pre><code class="language-python">def graph_info(g_nodes, g_from, g_to, g_weight):
  parents = [i for i in range(g_nodes+1)]
  edges_info = [(g_weight[i], g_from[i], g_to[i]) for i in range(len(g_from))]
  edges_info.sort()
  return parents, edges_info
</code></pre>
</li>
</ul>
<p>def find_parent(parents, n):
    if parents[n] != n:
        parents[n] = find_parent(parents, parents[n])
    return parents[n]</p>
<p>def union_parent(parents, up, vp):
    if up &lt; vp:
        parents[vp] = up
    else:
        parents[up] = vp
    return parents</p>
<p>def kruskal(g_nodes, g_from, g_to, g_weight):
    parents, edges_info = graph_info(g_nodes, g_from, g_to, g_weight)
    total_weights = 0
    for w, u, v in edges_info:
        u_parent, v_parent = find_parent(parents, u), find_parent(parents, v)
        if u_parent != v_parent:
            parents = union_parent(parents, u_parent, v_parent)
            total_weights += w
    return total_weights</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li><code>graph_info</code> 함수에서 간선(edge) 정보를 저장한 <code>edge_info</code>와 서브 트리(subtree)의 루트 노드 정보를 저장할 <code>parents</code> 리스트를 생성한다.<ul>
<li>가중치가 작은 간선을 선택해나가는 방법인 크루스칼 알고리즘을 구현하기 위해 <code>edge_info</code> 생성후, 간선 가중치(weight)를 기준으로 오름차순으로 정렬</li>
<li><code>parents</code>는 우선 노드 본인으로 초기화 (<em>이후 서브 트리를 만들며 갱신</em>)</li>
</ul>
</li>
<li>가중치를 기준으로 오름차순 정렬된 <code>edge_info</code>의 간선들을 탐색하며 서브 트리를 만들어감<ul>
<li>트리의 사이클 여부를 확인하기 위해, 간선을 선택할 때마다 <code>parents</code>에서 해당 노드(node)의 루트(root) 노드를 확인</li>
<li>루트 노드가 같지 않다면, <code>parents</code>에 저장된 각각의 루트 노드 번호 중 작은 값으로 갱신</li>
</ul>
</li>
<li>모든 간선 탐색 후 종료</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[1012번: 유기농 배추]]></title>
            <link>https://velog.io/@eunseo_thatsme/1012%EB%B2%88-%EC%9C%A0%EA%B8%B0%EB%86%8D-%EB%B0%B0%EC%B6%94</link>
            <guid>https://velog.io/@eunseo_thatsme/1012%EB%B2%88-%EC%9C%A0%EA%B8%B0%EB%86%8D-%EB%B0%B0%EC%B6%94</guid>
            <pubDate>Sun, 19 Jun 2022 05:58:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.acmicpc.net/problem/1012">https://www.acmicpc.net/problem/1012</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>주어진 행렬에서 수평(horizontally), 수직(vertically)으로 이어진 구역의 갯수를 세는 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 깊이 우선 탐색(Depth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Depth-first_search">그림출처:Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>메모리 : 33220KB</li>
<li>시간 : 396ms<pre><code class="language-python">import sys
sys.setrecursionlimit(10**6)
</code></pre>
</li>
</ul>
<p>def dfs(matrix, matsize, pos):
    dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
    matrix[pos[0]][pos[1]] = 0
    for i in range(4):
        ni, nj = pos[0]+dy[i], pos[1]+dx[i]
        if 0&lt;=ni&lt;matsize[0] and 0&lt;=nj&lt;matsize[1] and matrix[ni][nj] == 1:
            matrix = dfs(matrix, matsize=matsize, pos=(ni, nj))
    return matrix</p>
<p>def main():
    T = int(input())
    result = []
    for _ in range(T):
        M, N, K = map(int, input().split())
        matrix, cnt = [[0]<em>M for _ in range(N)], 0
        for _ in range(K):
            X, Y = map(int, input().split())
            matrix[Y][X] = 1
        for i in range(N):
            for j in range(M):
                if matrix[i][j] == 1:
                    matrix = dfs(matrix, matsize=(N, M), pos=(i, j))
                    cnt += 1
        result.append(cnt)
    print(</em>result, sep=&#39;\n&#39;)</p>
<p>main()</p>
<p>```</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[2667번: 단지번호붙이기]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EB%8B%A8%EC%A7%80%EB%B2%88%ED%98%B8%EB%B6%99%EC%9D%B4%EA%B8%B0</link>
            <guid>https://velog.io/@eunseo_thatsme/%EB%8B%A8%EC%A7%80%EB%B2%88%ED%98%B8%EB%B6%99%EC%9D%B4%EA%B8%B0</guid>
            <pubDate>Sun, 19 Jun 2022 05:02:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.acmicpc.net/problem/2667">https://www.acmicpc.net/problem/2667</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>주어진 행렬에서 수평(horizontally), 수직(vertically)으로 이어진 구역(1로 표시, 문제에서는 단지로 표현)들의 크기(문제에서는 집의 갯수로 표현)를 구하는 문제</p>
<ul>
<li>출력 형태
  : 첫 번째 줄에는 총 단지 수, 그리고 각 단지내 집의 수를 오름차순으로 정렬하여 한 줄에 하나씩 출력</li>
</ul>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/5602e361-90d9-4dd7-97eb-462be5d5a69e/image.png" alt="">
<a href="https://www.acmicpc.net/problem/2667">이미지 출처: 백준 2667번</a></p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 깊이 우선 탐색(Depth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Depth-first_search">그림출처:Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>메모리 : 31084KB</li>
<li>시간 : 68ms<pre><code class="language-python">def dfs(matrix, n, pos, cnt):
  dx, dy = [1, -1, 0, 0], [0, 0, 1, -1]
  matrix[pos[0]][pos[1]] = &#39;0&#39;
  for i in range(4):
      ni, nj = pos[0]+dx[i], pos[1]+dy[i]
      if 0&lt;=ni&lt;n and 0&lt;=nj&lt;n and matrix[ni][nj] == &#39;1&#39;:
          cnt = dfs(matrix, n, pos=(ni, nj), cnt=cnt+1)
  return cnt
</code></pre>
</li>
</ul>
<p>def main():
    n = int(input())
    matrix = []
    for _ in range(n):
        matrix.append(list(input()))
    result = []
    for i in range(n):
        for j in range(n):
            if matrix[i][j] == &#39;1&#39;:
                cnt = dfs(matrix, n, pos=(i,j), cnt=1)
                result.append(cnt)
    result.sort()
    print(len(result))
    print(*result, sep=&#39;\n&#39;)</p>
<p>main()</p>
<p>```</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Breadth First Search: Shortest Path]]></title>
            <link>https://velog.io/@eunseo_thatsme/Breadth-First-Search-Shortest-Path</link>
            <guid>https://velog.io/@eunseo_thatsme/Breadth-First-Search-Shortest-Path</guid>
            <pubDate>Sat, 18 Jun 2022 08:47:31 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/bfsshortreach/problem?isFullScreen=true">https://www.hackerrank.com/challenges/bfsshortreach/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p><a href="https://en.wikipedia.org/wiki/Breadth-first_search">너비 우선 탐색(Breadth First Search)</a>을 이용하여 특정 노드와 다른 노드와의 최단 거리를 구하는 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 너비 우선 탐색 (Breadth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">from collections import deque
</code></pre>
</li>
</ul>
<p>def bfs(n, m, edges, s):
    graph = [set() for _ in range(n)]
    for u, v in edges:
        graph[u - 1].add(v - 1)
        graph[v - 1].add(u - 1)</p>
<pre><code>end_nodes = [i for i in range(n)]
end_nodes.remove(s - 1)
queue, result = deque([(s - 1, 0)]), [-1] * n
while queue:
    temp, depth = queue.popleft()
    if temp in end_nodes:
        result[temp] = depth * 6
        end_nodes.remove(temp)
    for n in list(graph[temp]):
        queue.append((n, depth + 1))
        graph[temp].remove(n)
        graph[n].remove(temp)
result.pop(s - 1)
return result</code></pre><p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>정점 연결 정보가 저장되어 있는 <code>edges</code>를 가지고 <code>graph</code>를 만든다.</li>
<li>거리를 계산할 노드 번호를 저장할 <code>end_nodes</code>를 만든다.<ul>
<li>시작 노드는 제외시켜야 하므로 리스트 내장함수인 <code>remove</code>를 사용하여 시작 노드 번호를 리스트에서 제거한다.</li>
</ul>
</li>
<li>탐색 결과를 저장할 <code>queue</code>와, 노드 간 최단 거리를 저장할 <code>result</code>를 선언한다.</li>
<li>반복문을 사용하여 그래프 내의 노드들을 BFS 방법으로 탐색한다.<ul>
<li>만나는 노드(<code>temp</code>)가 <code>end_nodes</code>에 있으면 <code>result[temp]</code>에 최단 거리인 <code>depth</code>를 저장하고(문제에서는 간선(edge) 하나의 길이가 <code>6</code>이라고 했으므로 <code>6</code>을 곱하여 저장), 해당 노드의 최단 거리를 구했으므로 <code>end_nodes</code>에서 지운다.</li>
<li>노드 <code>temp</code>의 자식 노드들을 <code>depth</code> 정보와 함께<code>queue</code>에 저장하고, 해당 노드에 다시 방문하지 못하도록 <code>graph</code>에서 간선 정보를 지운다.</li>
<li><code>queue</code>에 방문 정보가 더 이상 없을 때(조건에 맞는 모든 노드를 탐색한 상태)까지 반복한다.</li>
</ul>
</li>
<li>탐색이 끝나면, <code>result</code>에서 시작 노드 <code>s</code>에 저장되어 있는 값을 지우고(시작 노드는 제외하고 출력해야함) <code>return</code> 한다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[소수 판별]]></title>
            <link>https://velog.io/@eunseo_thatsme/%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</link>
            <guid>https://velog.io/@eunseo_thatsme/%EC%86%8C%EC%88%98-%ED%8C%90%EB%B3%84</guid>
            <pubDate>Thu, 16 Jun 2022 05:30:16 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-소수-판별">✅ 소수 판별</h2>
<h3 id="1️⃣-on으로-구현하기">1️⃣ O(n)으로 구현하기</h3>
<p>소수(prime number)란, 1과 자기 자신 외의 수로 나누어지지 않는 수를 말한다. 즉, 약수(divisor)가 1과 자기 자신밖에 없는 수가 소수인 것이다. 이러한 소수의 특성을 이용해서 입력한 숫자가 소수인지 판별하는 <em>Python3</em> 코드를 작성해보면 다음과 같다.</p>
<pre><code class="language-python">def checkPrimeNumber(n):
    for i in range(2, n):
        if n % i == 0:
            return False
    return True</code></pre>
<p>위의 코드는 입력 숫자 <code>n</code> 을 <code>2</code> 부터 <code>n-1</code> 까지 차례대로 나누어서 만약 나누어진다면 <code>False</code>(소수가 아님), 나누어지면  <code>True</code>(소수임)를 반환하여 소수인지 아닌지 판별한다. 시간 복잡도 <code>O(n)</code> 으로, 가장 기초적인 방법임에도 나쁘지 않은 효율을 갖고 있다고 말할 수 있겠다. 하지만, <code>n</code>이 커지면 연산량이 그만큼 많아지게 된다.</p>
<h3 id="2️⃣-osqrtn으로-구현하기">2️⃣ O($\sqrt{n}$)으로 구현하기</h3>
<p>연산량을 좀 더 줄여보자. 약수의 특성을 활용하면 어떨까? 어떤 수의 약수, 예를 들어 16의 약수들을 나열한다고 생각해보자. 나열하면 다음과 같다.</p>
<blockquote>
<p><strong>16의 약수</strong>
1, 2, 4, 8, 16</p>
</blockquote>
<p>나열한 수들을 자세히 보면 중앙값을 기준으로 대칭을 이루고 있는 것을 알 수 있다. 또한 <code>중앙값 이후</code>의 약수들은 <code>중앙값 이전</code>의 약수들로 나눌 수 있다. 그렇다면 중앙값 즉, <strong>소수인지 판별하고 싶은 어떤 수의 제곱근 보다 작은 수들로 나누어진다면, 제곱근 보다 큰 수들로 나누어 보지 않아도 소수가 아님을 알 수 있다.</strong> 이 방식으로 코드를 설계하게 되면 시간 복잡도 O($\sqrt{n}$) 으로, 이전 보다 효율성을 좀 더 챙길 수 있게 된다. <em>Python3</em>로 작성해보면 다음과 같다.</p>
<pre><code class="language-python">def checkPrimeNumber(n):
    for i in range(2, int(n**0.5)+1):
        if n % i == 0:
            return False
    return True</code></pre>
<h2 id="✅-특정-구간에서-소수-찾기">✅ 특정 구간에서 소수 찾기</h2>
<p>지금까지는 어떤 수가 입력되었을 때, 해당 수가 소수인지 아닌지 판별하는 문제를 다루어보았다. 위의 코드를 이용해서 특정 구간에서의 모든 소수를 찾는 코드를 작성해보면 다음과 같다. </p>
<pre><code class="language-python">def checkPrimeNumber(n):
    for i in range(2, int(n**0.5)+1):
        if n % i == 0:
            return False
    return True

def findPrimeNumbers(start, end):
    result = []
    for i in range(start, end+1):
        if checkPrimeNumber(i):
            result.append(i)
    return result

findPrimeNumbers(10, 100)</code></pre>
<h3 id="에라토스테네스의-체">에라토스테네스의 체</h3>
<p>위의 코드는 시간복잡도 O($n^2$) 에 가까운 연산을 수행한다. 조금 더 빠른 코드를 작성해보고 싶다면 <strong>에라토스테네스의 체</strong>를 적용하면 된다. 에라토스테네스의 체 알고리즘은 다음과 같이 작동한다.</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/b/b9/Sieve_of_Eratosthenes_animation.gif" alt="출처: 위키백과">
<a href="https://ko.wikipedia.org/wiki/%EC%97%90%EB%9D%BC%ED%86%A0%EC%8A%A4%ED%85%8C%EB%84%A4%EC%8A%A4%EC%9D%98_%EC%B2%B4">그림 출처: 위키백과</a></p>
<blockquote>
<p>💡 <strong>에라토스테네스의 체 규칙</strong>
1️⃣ 2부터 소수를 구하고자 하는 구간의 모든 수를 나열한다.
2️⃣ 2부터 시작하여 소수의 배수들을 차례로 지워 나간다. 이 때 자기 자신은 지우지 않으며, 이미 지워진 수는 넘어간다.
3️⃣ 더이상 지울 숫자가 없을 때까지 2번을 반복한다. 
4️⃣ 3번 이후 남아있는 숫자가 소수가 된다.</p>
</blockquote>
<p>에라토스테네스의 체를 사용하면 소수 찾는 문제를 시간 복잡도 O($NloglogN$) 으로 구현할 수 있다. 굉장히 빠른 속도로 연산할 수 있지만, 입력 수만큼의 데이터 공간을 만들어야 하기 때문에 너무 큰 수에 대해서는 메모리를 많이 쓴다는 단점이 있다. 하지만 컴퓨터 메모리가 부족할 정도로 큰 범위의 소수 찾기 문제는 흔치 않으므로 편하게 쓰도록 하자. 해당 알고리즘을 적용한 코드는 다음과 같다.</p>
<pre><code class="language-python">def SieveOfEratosthenes(n):
    isPrime = [True] * (n+1)
    for i in range(2, int(n**0.5)+1):
        j = 2
        while i*j &lt;= n:
            isPrime[i*j] = False
            j += 1
    return isPrime

def findPrimeNumbers(start, end):
    isPrime = SieveOfEratosthenes(end)
    result = []
    for i in range(start, end):
        if isPrime[i]:
            result.append(i)
    return result

findPrimeNumbers(10, 100)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Minimum Loss]]></title>
            <link>https://velog.io/@eunseo_thatsme/Minimum-Loss</link>
            <guid>https://velog.io/@eunseo_thatsme/Minimum-Loss</guid>
            <pubDate>Tue, 14 Jun 2022 03:15:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/minimum-loss/problem?isFullScreen=true">https://www.hackerrank.com/challenges/minimum-loss/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>구매한 집을 되팔았을 때 날 수 있는 손해 금액 중 최솟값을 구하는 문제</p>
<ul>
<li>$post$: 집 값이 저장되어 있는 리스트(혹은 배열)</li>
<li>$post$의 인덱스(index)는 연도를 뜻하며, 해당 인덱스에 저장되어 있는 값은 그 해의 집 값임</li>
<li>집은 1년 이후 부터 팔 수 있으며, 무조건 손해가 나야함</li>
</ul>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">import os
import sys
</code></pre>
</li>
</ul>
<p>def minimumLoss(price):
    idxWprice = [(i, v) for i, v in enumerate(price)]
    idxWprice.sort(key = lambda x:x[1])
    minVal = sys.maxsize
    for i in range(1, len(price)):
        if idxWprice[i][0] &lt; idxWprice[i-1][0]:
            diff = idxWprice[i][1] - idxWprice[i-1][1]
            minVal = min(diff, minVal)
    return minVal</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li><code>(연도, 집값)</code> 정보를 저장한 <code>idxWprice</code> 리스트 생성</li>
<li>집값을 기준으로 <code>idxWprice</code> 정렬</li>
<li>정렬한 <code>idxWprice</code>를 앞에서부터 순차적으로 탐색하며 최소 손해 금액을 계산<ul>
<li>인덱스 <code>i</code>번째의 연도가 <code>i-1</code>의 연도보다 작은 경우에만 계산</li>
<li>집값을 기준으로 정렬하였기 때문에, 해당 연도 끼리(<code>i</code>번째의 연도와 <code>i-1</code>의 연도)의 최소 손해 금액을 <code>idxWprice[i][1] - idxWprice[i-1][1]</code> 로 계산할 수 있음</li>
</ul>
</li>
<li>반복문이 한번 수행될 때마다 이전 최솟값과 비교하여 정답을 구함</li>
</ul>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li>원인이 무엇인지 모르겠으나(메모리..?), 코딩 테스트 플랫폼에 따라서 처리할 수 있는 값의 최대치가 존재하는 것 같다. 따라서 최솟값을 비교하기 위해 초깃값을 시스템의 최댓값인 <code>sys.maxsize</code>로 설정하였다. (마냥 큰 값으로 설정하면 코드를 잘 짜도 오류가 나는 경우가 있었다.)</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cut the Tree]]></title>
            <link>https://velog.io/@eunseo_thatsme/Cut-the-Tree</link>
            <guid>https://velog.io/@eunseo_thatsme/Cut-the-Tree</guid>
            <pubDate>Wed, 08 Jun 2022 06:21:03 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/cut-the-tree/problem?isFullScreen=true">https://www.hackerrank.com/challenges/cut-the-tree/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>각 노드에 키 값이 들어있는 이진 트리(Binary Tree)에서 엣지(edge) 하나를 끊어 두개의 트리를 만들고, 각 트리의 노드 값 총합의 차이 중 최솟값을 구하는 문제</p>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/e6320745-afd5-4b00-8949-719fc9310953/image.png" alt="fig1"></p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 너비 우선 탐색 (Breadth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" alt="fig2">
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="💡-idea">💡 Idea</h3>
<p>문제 풀이를 위해 노드 값을 리프 노드 부터 역순으로 <strong>누적합</strong>을 구하여 계산하는 방법을 채택하였다.</p>
<p>우선 예시로 주어진 트리에서 리프 노드(leaf node)부터 루트 노드(root node)까지 누적합을 구하면 다음과 같다.
<img src="https://velog.velcdn.com/images/eunseo_thatsme/post/ca9e585c-2517-4bac-a617-68d4310ced6c/image.png" alt="">
여기서 하나의 엣지(edge)를 끊었을 때 만들어지는 두 트리의 각 노드 총합을 구하려면 다음과 같이 계산하면 된다. 나누어진 두 트리에서 원래 루트 노드인, <code>Vertex 1</code>이 포함되지 않은 트리(tree1)의 총합은 루트 노드에 할당되어 있었던 누적합으로 계산할 수 있다. 그리고 나머지 트리(tree2)의 총합은 <code>Vertex 1</code>의 누적합, 즉 모든 노드의 총합에서 tree1의 루트 노드에 해당하는 누적합을 빼주면 구할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/cecc7bc6-7b1e-42d5-a20a-f7dc9c5a60e1/image.png" alt="fig3">
위의 방식을 따라 계산하면, 나누어진 두 트리의 총합의 차이는 다음 공식으로 구할 수 있게 된다.</p>
<pre><code>두 트리 총합 차이 = (노드 들의 총합) - 2*(Vertex1이 포함되지 않은 트리의 총합)</code></pre><p>이 아이디어를 적용하기 위해서는 노드 들의 깊이(depth) 정보가 있어야 한다(리프 노드 부터 루트 노드 방향으로 누적합을 계산하기 때문에). 이를 설정하기 위해 너비 우선 탐색(Breadth-First Search) 알고리즘을 사용하여 각 노드들의 깊이를 추적하였다.</p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과</li>
<li>참고 코드: <a href="https://www.hackerrank.com/challenges/cut-the-tree/forum">qayum_khan_usa 님의 글</a><pre><code class="language-python">def bfs(tree):
  parent_nodes, visited_nodes = {1}, []
  while parent_nodes:
      pn = parent_nodes.pop()
      # Save visited nodes in descending order along depth level
      visited_nodes.insert(0, pn)
      parent_nodes.update(set(tree[pn]))
      # Prevent node revisiting
      for cn in tree[pn]:
          tree[cn].remove(pn)
  return visited_nodes

</code></pre>
</li>
</ul>
<p>def cutTheTree(data, edges):
    tree = {}
    for n1, n2 in edges:
        tree[n1] = tree.get(n1, []) + [n2]
        tree[n2] = tree.get(n2, []) + [n1]
    # bfs for calculating node&#39;s depth
    visited_nodes = bfs(tree)
    for node in visited_nodes:
        data[node - 1] += sum(data[cn - 1] for cn in tree[node])
    return min(abs(data[0] - 2 * data[n]) for n in range(1, len(data)))</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>주어진 노드에 저장된 값(<code>data</code>)과 엣지(<code>edge</code>) 정보를 이용하여 트리 내 노드 간 연결 관계를 딕셔너리 형태로 만듦<ul>
<li>딕셔너리 키(key): 노드 번호 / 값(value): 키 노드와 이어져 있는 노드 번호 집합(<code>set</code>)</li>
</ul>
</li>
<li>너비 우선 탐색(bfs) 알고리즘을 사용하여 각 노드 들의 깊이 정보를 추적한다. (방문순서 -&gt; 노드 깊이로 생각)<ul>
<li>노드를 번호의 오름차순으로 탐색하기 때문에 방문한 노드 번호를 저장할 때 역순으로 저장하여, 이후 누적합 계산 시 앞에서부터 차례로 계산할 수 있도록 만든다.</li>
</ul>
</li>
<li>💡 <strong>Idea</strong> 에서 언급한 공식으로 누적합 차이를 구하고, 그들 중 최솟값을 <code>return</code> 한다.</li>
</ul>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li><p>트리를 이용한 이런 형태의 문제에서 누적합을 활용하는 방법을 알게 되었다.</p>
</li>
<li><p>BFS를 단순 노드 값 탐색이 아닌 트리의 깊이를 탐색하는데 사용할 수 있다는 것을 깨닫게 되었다.</p>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pairs]]></title>
            <link>https://velog.io/@eunseo_thatsme/Pairs</link>
            <guid>https://velog.io/@eunseo_thatsme/Pairs</guid>
            <pubDate>Wed, 08 Jun 2022 02:37:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/pairs/problem?isFullScreen=true">https://www.hackerrank.com/challenges/pairs/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>양수 $k$와 양수로 이루어진 배열($arr$)이 주어졌을 때, 배열 내 두 수의 차가 $k$인 짝의 갯수를 구하는 문제</p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def pairs(k, arr):
  arr.sort()
  cnt = 0
  for i in range(len(arr)-1):
      for j in range(i+1, len(arr)):
          diff = arr[j]-arr[i]
          if k == diff:
              cnt+=1
          elif k &lt; diff:
              break
  return cnt</code></pre>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
</li>
<li>입력된 배열($arr$)을 오름차순으로 정렬</li>
<li>정렬된 배열을 반복문으로 탐색하며 두 수의 차가 $k$인 경우 계산<ul>
<li>반복문 종료 조건은 배열($arr$) 내의 같은 숫자가 존재하지 않는다는 점을 이용</li>
<li>만약 두 수의 차가 $k$보다 큰 경우, 더 이상 $k$와 같아질 수 없기 때문에 반복문 종료</li>
</ul>
</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Red Knight's Shortest Path]]></title>
            <link>https://velog.io/@eunseo_thatsme/Red-Knights-Shortest-Path</link>
            <guid>https://velog.io/@eunseo_thatsme/Red-Knights-Shortest-Path</guid>
            <pubDate>Fri, 03 Jun 2022 04:58:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/red-knights-shortest-path/problem?isFullScreen=true">https://www.hackerrank.com/challenges/red-knights-shortest-path/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>아래 그림과 같이 여섯 가지의 이동을 할 수 있는 특수 체스 말을 주어진 위치 $(i_{start},j_{start})$ 에서 목적지 $(i_{end},j_{end})$ 까지의 최소 이동 횟수와, 해당 이동 횟수에 해당하는 체스 말의 움직임을 출력하는 문제</p>
<ul>
<li>경로가 겹치는 경우, <code>UL &gt; UR &gt; R &gt; LR &gt; LL &gt; L</code> 를 우선 순위로 하여 경로 출력</li>
<li>만약 불가능하다면 <code>Impossible</code> 출력</li>
</ul>
<p><img src="https://velog.velcdn.com/images/eunseo_thatsme/post/b93e1162-8727-4b19-b566-7b183ae22461/image.png" alt="">
<a href="https://www.hackerrank.com/challenges/red-knights-shortest-path/problem?isFullScreen=true">그림 출처: HackerRank Red Knight&#39;s Shortest Path</a></p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 너비 우선 탐색 (Breadth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def bfs(n, pos, dtn):
  matrix = [[0]*n for _ in range(n)]
  dx = [-2, -2, 0, 2, 2, 0]
  dy = [-1, 1, 2, 1, -1, -2]
  moveName = [&#39;UL&#39;, &#39;UR&#39;, &#39;R&#39;, &#39;LR&#39;, &#39;LL&#39;, &#39;L&#39;]
  queue = [(pos[0], pos[1], [])]
  while queue:
      tx, ty, path = queue.pop(0)
      if (tx, ty) == dtn:
          return (matrix[tx][ty], path)
      for i in range(6):
          nx, ny = tx + dx[i], ty + dy[i]
          if 0&lt;=nx&lt;n and 0&lt;=ny&lt;n and matrix[nx][ny] == 0:
              matrix[nx][ny] = matrix[tx][ty] + 1
              queue.append((nx, ny, path+[moveName[i]]))
  return -1
</code></pre>
</li>
</ul>
<p>def printShortestPath(n, i_start, j_start, i_end, j_end):
    result = bfs(n, pos=(i_start, j_start), dtn=(i_end, j_end))
    if result == -1:
        print(&quot;Impossible&quot;)
    else:
        print(result[0])
        print(*result[1], end=&#39; &#39;)</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>최소 이동 횟수를 구하기 위해 가능한 모든 경우의 수를 탐색해야 한다는 점과, 우선 순위 경로가 있다는 점_(queue를 활용하기 때문에)_에서 <code>BFS</code> 알고리즘 사용</li>
<li>0으로 표시된 방문하지 않은 위치(노드)로만 이동이 가능하며, 목적지에 도착할 때 까지 <code>UL &gt; UR &gt; R &gt; LR &gt; LL &gt; L</code> 순서 대로 경로를 탐색<ul>
<li>현재 방문한 위치 <code>matrix[nx][ny]</code>에는 이전 위치까지의 이동 횟수 <code>matrix[tx][ty]</code>에 <code>1</code>을 더하여 저장</li>
</ul>
</li>
<li><code>bfs</code> 함수를 통해 목적지에 무사히 도착하면 해당 결과를, 아니라면 <code>-1</code>을 <code>return</code>하여 답 출력</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Count Luck]]></title>
            <link>https://velog.io/@eunseo_thatsme/Count-Luck</link>
            <guid>https://velog.io/@eunseo_thatsme/Count-Luck</guid>
            <pubDate>Fri, 03 Jun 2022 04:21:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/count-luck/problem?isFullScreen=true">https://www.hackerrank.com/challenges/count-luck/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p><code>.</code>, <code>X</code>, <code>M</code>, <code>*</code> 로 이루어진 행렬에서 시작점인 <code>M</code>에서 목적지인 <code>*</code> 까지 이동하면서 만날 수 있는 교차점의 갯수를 세는 문제</p>
<ul>
<li><code>상하좌우</code>로 움직일 수 있으며, 값이 <code>.</code> 인 위치로만 움직일 수 있음</li>
<li><code>M</code>에서 <code>*</code> 까지는 오직 한 개의 길만 존재</li>
<li>입력된 <code>k</code>값과  실제로 계산한 교차로 갯수가 일치하면 <code>Impressed</code>,  일치하지 않으면 <code>Oops!</code>를 출력</li>
</ul>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 깊이 우선 탐색(Depth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Depth-first_search">그림출처:Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def findMPosition(matrix):
  for i in range(len(matrix)):
      for j in range(len(matrix[0])):
          if matrix[i][j] == &#39;M&#39;:
              return [i, j]
</code></pre>
</li>
</ul>
<p>def checkIntersection(pos):
    dx = [1, -1, 0, 0]
    dy = [0, 0, 1, -1]
    res = []</p>
<pre><code>for i in range(4):
    nx, ny = pos[0] + dx[i], pos[1] + dy[i]
    if 0 &lt;= nx &lt; n and 0 &lt;= ny &lt; m and matrix[nx][ny] != &#39;X&#39;:
        res.append([nx, ny])
return res</code></pre><p>def dfs(matrix, wave_cnt, pos):
    if matrix[pos[0]][pos[1]] == &#39;*&#39;:
        return wave_cnt</p>
<pre><code>matrix[pos[0]][pos[1]] = &#39;X&#39;
moves = checkIntersection(pos)
if len(moves) &gt; 1:
    wave_cnt += 1
for nx, ny in moves:
    result = dfs(matrix, wave_cnt, pos=[nx, ny])
    if result != -1:
        return result
return -1</code></pre><p>def countLuck(matrix, k):
    temp_pos = findMPosition(matrix)
    wave_cnt = dfs(matrix, 0, pos=temp_pos)
    return &#39;Oops!&#39; if wave_cnt != k else &#39;Impressed&#39;</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li><code>M</code>부터 <code>*</code>까지 한 개의 길만 존재한다는 점에서,<code>DFS</code> 알고리즘 활용</li>
<li>우선 <code>findMPosition</code> 함수로 <code>M</code>의 위치를 탐색</li>
<li><code>M</code>의 위치를 찾으면, <code>dfs</code> 함수를 이용해 <code>*</code>까지의 길을 탐색<ul>
<li>방문 위치(노드)는 <code>X</code>로 표시하여 다시 방문하지 못하게 함</li>
<li>매 탐색 마다 해당 위치가 교차점에 해당하는지 확인 (<code>checkIntersection</code> 함수)</li>
<li>해당 위치에서 갈 수 있는 길이 2개 이상이면 교차점으로 판별하여 <code>교차점 갯수 + 1</code> 을 수행</li>
</ul>
</li>
<li><code>*</code>에 도착하면 입력 <code>k</code>와 <code>실제 교차점 갯수</code>를 비교하여 결과 출력</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[KnightL on a Chessboard]]></title>
            <link>https://velog.io/@eunseo_thatsme/KnightL-on-a-Chessboard</link>
            <guid>https://velog.io/@eunseo_thatsme/KnightL-on-a-Chessboard</guid>
            <pubDate>Fri, 03 Jun 2022 03:51:07 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/knightl-on-chessboard/problem?isFullScreen=true">https://www.hackerrank.com/challenges/knightl-on-chessboard/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>직각 형태로 움직일 수 있는 체스 말(knight)을 이용하여, $n\times n$ 크기의 행렬에서 $(0, 0)$ 부터 $(n-1, n-1)$ 까지 말의 최소 이동 횟수를 구하는 문제</p>
<ul>
<li>아래에 해당하는 모든 경우의 수를 구하여 행렬 형태로 출력해야 함 ($KnightL(i,j)$에 해당하는 값을 행렬 $(i,j)$에 넣어서 출력)</li>
</ul>
<p>$$
KnightL(i,j) \qquad  0\le i &lt; n ,  \quad 0\le j &lt; n 
$$</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 너비 우선 탐색 (Breadth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/4/46/Animated_BFS.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Breadth-first_search">그림 출처: Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def bfs(n, i, j):
  checkVisit = [[0] * n for _ in range(n)]
  queue = [[0, 0]]
  dx, dy = [i, i, -i, -i, j, -j, j, -j], [j, -j, j, -j, i, i, -i, -i]
  while queue:
      px, py = queue.pop(0)
      for p in range(8):
          nx, ny = px + dx[p], py + dy[p]
          if 0 &lt;= nx &lt; n and 0 &lt;= ny &lt; n and checkVisit[nx][ny] == 0:
              queue.append([nx, ny])
              checkVisit[nx][ny] = checkVisit[px][py] + 1
  return -1 if checkVisit[n - 1][n - 1] == 0 else checkVisit[n - 1][n - 1]

</code></pre>
</li>
</ul>
<p>def knightlOnAChessboard(n):
    result = [[0] * (n - 1) for _ in range(n - 1)]
    for i in range(1, n):
        for j in range(i, n):
            val = bfs(n, i, j)
            result[i - 1][j - 1], result[j - 1][i - 1] = val, val
    return result</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>반복문을 사용하여 발생할 수 있는 모든 경우를 탐색함 (<code>BFS</code> 알고리즘 이용)<ul>
<li>$KnightL(i,j) = KnightL(j,i)$ 이므로, 절반만 탐색</li>
</ul>
</li>
<li><code>BFS</code> 함수에서 $Knight$의 방문 노드 확인 및 이동 횟수 계산을 위해 <code>checkVisit</code> 이라는 행렬을 생성<ul>
<li>$Knight$는 다음 위치인 <code>checkVisit[i][j]</code> 의 값이 0일 때만 이동하며, 이동한 이후에는 <code>checkVisit[i][j]</code>에 이전 단계까지의 $Knight$ 이동 횟수에 <code>1</code>을 더하여 저장</li>
</ul>
</li>
<li>모든 경우를 탐색한 후, 최종적으로 <code>checkVisit[n-1][n-1]</code>의 값이 <code>0</code>이 아니라면 <code>checkVisit[n-1][n-1]</code> 에 저장된 값을 <code>return</code>, <code>0</code>이라면 <code>-1</code>을 <code>return</code></li>
</ul>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li>BFS 문제 유형에 대해 감을 잡게 되었다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Connected Cells in a Grid]]></title>
            <link>https://velog.io/@eunseo_thatsme/Connected-Cells-in-a-Grid</link>
            <guid>https://velog.io/@eunseo_thatsme/Connected-Cells-in-a-Grid</guid>
            <pubDate>Fri, 03 Jun 2022 01:57:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/connected-cell-in-a-grid/problem?isFullScreen=true">https://www.hackerrank.com/challenges/connected-cell-in-a-grid/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>주어진 행렬에서 수평(horizontally), 수직(vertically), 대각(diagonally)으로 이어진 구역(1로 표시) 중 가장 넓은 구역의 크기를 구하는 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. 깊이 우선 탐색(Depth-First Search)</strong></span>
<img src="https://upload.wikimedia.org/wikipedia/commons/7/7f/Depth-First-Search.gif" alt="">
<a href="https://en.wikipedia.org/wiki/Depth-first_search">그림출처:Wikipedia</a></p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li>모든 테스트 케이스 통과<pre><code class="language-python">def dfs(matrix, tempCnt, matsize, pos):
  matrix[pos[0]][pos[1]] = 0
  dx = [0, 0, 1, 1, 1, -1, -1, -1]
  dy = [1, -1, 0, 1, -1, 0, 1, -1]
  for i in range(8):
      ni, nj = pos[0] + dx[i], pos[1] + dy[i]
      if 0 &lt;= ni &lt; matsize[0] and 0 &lt;= nj &lt; matsize[1] and matrix[ni][nj] == 1:
          tempCnt = dfs(matrix, tempCnt + 1, matsize=matsize, pos=(ni, nj))
  return tempCnt
</code></pre>
</li>
</ul>
<p>def connectedCell(n, m, matrix):
    maxCnt = 0
    for i in range(n):
        for j in range(m):
            if matrix[i][j] == 1:
                tempCnt = dfs(matrix, 1, matsize=(n, m), pos=(i, j))
                maxCnt = max(tempCnt, maxCnt)
    return maxCnt</p>
<p>```</p>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
<ul>
<li>여러 경우의 수가 아닌 특정 위치에서 하나의 구역을 찾는 문제이기 때문에 DFS를 적용</li>
<li>행렬을 탐색하면서 <code>1</code>을 만날 때 마다 DFS 알고리즘을 사용하여 구역의 크기를 계산함 <em>(DFS 함수를 만들어 재귀의 형태로 구현)</em></li>
<li>DFS 함수에서, 방문한 위치에는 <code>0</code>을 넣어 다시 방문하지 못하게 만듦</li>
<li>DFS 함수를 통해 <code>return</code>된 값을 매 행렬 탐색마다 비교하여 최댓값을 계산</li>
</ul>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li>DFS 문제 유형에 대해 감을 잡을 수 있었다.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[The Full Counting Sort]]></title>
            <link>https://velog.io/@eunseo_thatsme/The-Full-Counting-Sort</link>
            <guid>https://velog.io/@eunseo_thatsme/The-Full-Counting-Sort</guid>
            <pubDate>Thu, 19 May 2022 03:24:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/countingsort4/problem?isFullScreen=true">https://www.hackerrank.com/challenges/countingsort4/problem?isFullScreen=true</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p>문자와 함께 주어진 인덱스(index)를 가지고 분류하여 순서대로 나열하는 문제 (전체 문자의 반절은 <code>-</code>로 변환하여 출력)</p>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li><p>모든 테스트 케이스 통과</p>
<pre><code class="language-python">def countSort(arr):
  maxVal = max([int(i) for i, _ in arr])

  result = [[] for _ in range(maxVal+1)]
  for idx, (i, s) in enumerate(arr):
      if idx &lt; len(arr) // 2:
          result[int(i)].append(&#39;-&#39;)
      else:
          result[int(i)].append(s)
  print(&#39; &#39;.join([y for x in result for y in x]))</code></pre>
<blockquote>
<p>📌 *<em>코드 구현 설명 *</em></p>
</blockquote>
</li>
<li><p>주어진 인덱스(index)값 중 최댓값을 찾은 후, 최댓값 만큼의 빈 리스트가 들어있는 이중 리스트 생성</p>
</li>
<li><p>입력 문자가 들어 있는 배열을 차례대로 탐색하여 처음 반절은 <code>-</code>로 치환, 나머지는 문자열 그대로 사용</p>
</li>
<li><p>처음에 생성해둔 이중 리스트에서, 문자열 인덱스(index)에 해당하는 리스트에 앞 단계에서 만든 문자열을 넣어줌</p>
</li>
<li><p>탐색이 끝나면 이중 리스트를 분해하여 문자들을 출력</p>
</li>
</ul>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li>인덱스 혹은 클래스가 포함된 데이터를 계수 정렬(counting sort)의 개념을 적용해서 정렬할 수 있음을 배울 수 있었음</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Quicksort In-Place]]></title>
            <link>https://velog.io/@eunseo_thatsme/Quicksort-In-Place</link>
            <guid>https://velog.io/@eunseo_thatsme/Quicksort-In-Place</guid>
            <pubDate>Thu, 19 May 2022 02:47:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <strong>Problem Link</strong>
<a href="https://www.hackerrank.com/challenges/quicksort3/problem?/isFullScreen=True">https://www.hackerrank.com/challenges/quicksort3/problem?/isFullScreen=True</a></p>
</blockquote>
<h3 id="✅-problem-summary"><strong>✅ Problem Summary</strong></h3>
<p><a href="http://en.wikipedia.org/wiki/Quicksort#Algorithm">Lumoto Partitioning</a> 알고리즘을 이용한 퀵소트(quicksort) 구현 문제</p>
<hr>
<h3 id="🧮-applied-theory--algorithm">🧮 Applied Theory &amp; Algorithm</h3>
<p><span style="background-color:#F0F7DF"><strong>1. Lumoto Partition Scheme</strong></span></p>
<ul>
<li><code>pivot</code> 값을 정한 후, 배열 앞에서 부터 순차적으로 탐색</li>
<li><code>pivot</code>보다 큰 수가 나타나면 직전의 <code>pivot</code>보다 작은 수와 위치를 바꾸는 방식으로 정렬
<img src="http://upload.wikimedia.org/wikipedia/commons/8/84/Lomuto_animated.gif" alt="">
<a href="https://www.hackerrank.com/challenges/quicksort3/problem?/isFullScreen=True">그림출처:hackerrank quicksort in-place</a></li>
</ul>
<hr>
<h3 id="📑-my-answer">📑 My Answer</h3>
<ul>
<li><p>모든 테스트 케이스 통과</p>
<pre><code class="language-python"># Lomuto Partitioning
def quickSort(arr, l , r):
  if l &gt;= r or l &lt; 0:
      return

  arr, p = partition(arr, l, r)
  quickSort(arr, l, p-1)
  quickSort(arr, p+1, r)
</code></pre>
</li>
</ul>
<p>def partition(arr, l, r):
    i, pivot = l-1, arr[r]
    for j in range(l, r):
        if arr[j] &lt;= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    i += 1
    arr[i], arr[r] = arr[r], arr[i]
    print(*arr)
    return arr, i</p>
<p>```</p>
<hr>
<h3 id="💼-takeaway">💼 Takeaway</h3>
<ul>
<li>퀵소트 구현 방법이 다양하다는 것을 알게됨 </li>
<li>또 다른 퀵소트 알고리즘으로 <a href="https://en.wikipedia.org/wiki/Quicksort#:~:text=to%20(pivot).-,The%20original,-partition%20scheme%20described">Hoare partition scheme</a>이 있음</li>
</ul>
<hr>
]]></description>
        </item>
    </channel>
</rss>