<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yyy_sang.log</title>
        <link>https://velog.io/</link>
        <description>내 꿈은 고등어</description>
        <lastBuildDate>Wed, 13 Sep 2023 00:47:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yyy_sang.log</title>
            <url>https://velog.velcdn.com/images/yyy_sang/profile/461b1ed8-20c4-46a3-90ff-401b1ca28f5f/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yyy_sang.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yyy_sang" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[카카오 로그인하기 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%98%EA%B8%B0-swift</link>
            <guid>https://velog.io/@yyy_sang/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8%ED%95%98%EA%B8%B0-swift</guid>
            <pubDate>Wed, 13 Sep 2023 00:47:49 GMT</pubDate>
            <description><![CDATA[<h1 id="카카오-로그인하기">카카오 로그인하기</h1>
<p>일반적으로 어플을 실행하여 로그인을 할 경우 카카오톡이나 네이버와 같은 플랫폼을 통해 로그인하는 경우를 볼 수 있다
이번 문서는 ios에서 카카오톡 계정을 통해 로그인을 하는 방법이다</p>
<h2 id="1-kakao-developers">1. Kakao Developers</h2>
<p>우선 해당 링크를 통해 카카오톡 api를 이용할 수 있도록 해야한다
카카오톡 계정을 통해 해당 사이트에서 개발자를 등록해야 한다</p>
<blockquote>
<p><strong><a href="https://developers.kakao.com/">https://developers.kakao.com/</a></strong></p>
</blockquote>
<h3 id="a-로그인-및-내-애플리케이션-등록">a. 로그인 및 내 애플리케이션 등록</h3>
<blockquote>
<p>사진에서 빨간색 표시에 들어가 내 어플리케이션을 등록해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/f45d1c9b-6949-401b-9659-36b1c88fdf0c/image.png" alt="">
<img src="https://velog.velcdn.com/images/yyy_sang/post/c1563003-48e2-4bf4-913b-0fdbf61dd2f9/image.png" alt="">
앱 이름은 임의로 지정하여 입력하면 된다
사업자가 있다면 사업자명을 입력하면 된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/db94edb7-07c9-450a-853b-e5e812631bdb/image.png" alt=""></p>
</blockquote>
<h3 id="b-네이티브-키-확인하기">b. 네이티브 키 확인하기</h3>
<blockquote>
<p>등록한 내 앱에 들어가면 사진과 같이 필요한 키값이 존재한다. 앞으로 네이티브 값을 사용할 것이므로 이를 기억하자
<img src="https://velog.velcdn.com/images/yyy_sang/post/f2b0791a-805e-4065-8a3f-ed7808d5e57f/image.png" alt=""></p>
</blockquote>
<h3 id="c-ios-플랫폼-등록">c. ios 플랫폼 등록</h3>
<blockquote>
<p>등록된 앱 화면에서 빨간색으로 표시한 플랫폼에 들어가 내가 등록하고자 하는 프로젝트를 등록해줘야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/c2ba3d5c-a53f-4f0d-b153-1de48913e827/image.png" alt=""></p>
</blockquote>
<blockquote>
<p>ios를 선택하고 
<img src="https://velog.velcdn.com/images/yyy_sang/post/38b45114-ce41-43d7-ae75-b05061fd936b/image.png" alt="">
해당 위치에 프로젝트 번들을 입력해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/b70101e4-59b8-475a-a645-aff57d386c90/image.png" alt="">
프로젝트 번들의 경우에는 xcode 프로젝트에서 확인이 가능하다
프로젝트 파일 -&gt; general -&gt; identity -&gt; Budle identifier
빨간색으로 표시된 부분에 있는 번들키를 입력하면 된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/9bfe88ae-6691-42f4-a6cb-26caa3110de3/image.png" alt=""></p>
</blockquote>
<hr>
<h1 id="2-카카오톡-ios-sdk-설치하기">2. 카카오톡 ios SDK 설치하기</h1>
<p>아래 링크되어 있는 문서를 따라 카카오톡 api을 사용할 수 있도록 해야한다</p>
<blockquote>
<p><a href="https://developers.kakao.com/docs/latest/ko/getting-started/sdk-ios">https://developers.kakao.com/docs/latest/ko/getting-started/sdk-ios</a> </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/yyy_sang/post/dc0a3055-9051-430e-a774-be39dee8e007/image.png" alt=""></p>
<h2 id="1-카카오톡-ios-sdk-설치하기">1. 카카오톡 ios SDK 설치하기</h2>
<p>방법은 2가지로 CocoaPods를 이용하거나 SPM(Swift Package Manager)를 사용하는 방법이 있다
또한 이용하기 위한 방식을 swift와 Rxswift가 있는다</p>
<p>해당 문서에서는 SPM을 사용하며 swift를 이용하였다</p>
<p>아래에서 작성된 그대로 따라한다</p>
<blockquote>
<p><a href="https://developers.kakao.com/docs/latest/ko/getting-started/sdk-ios#apply-sdk-spm">https://developers.kakao.com/docs/latest/ko/getting-started/sdk-ios#apply-sdk-spm</a></p>
</blockquote>
<blockquote>
<p>우선 아래 사진과 같이 순서대로 클릭한다
프로젝트 파일 -&gt; project -&gt; Package Dependencies -&gt; + 버튼
<img src="https://velog.velcdn.com/images/yyy_sang/post/2f3735fa-434d-4fbf-8140-62617c51111c/image.png" alt="">
아래 사진과 같이 제공되는 레포지토리 URL을 복사하여 
<img src="https://velog.velcdn.com/images/yyy_sang/post/18d0a105-c398-467d-ae7a-8d7cc586bfa4/image.png" alt=""></p>
</blockquote>
<ul>
<li>출처: kakao development ios 문서<br>
아래 사진에서 표시한 부분에 언어에 맞는 Repository URL을 붙여넣고 설치한다
Dependancy Rule 부분을 Branch로 변경하고 브런치 명은 master로 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/b2fe2597-b5f0-4fa7-a509-4785f84357cf/image.png" alt="">
설치 할 모듈은 필요에 의해 선택하면 되지만 현재 문서에서는 모든 모듈을 설치한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/62fd4e11-30fd-4c8d-8921-53ea7ccc28e0/image.png" alt=""></li>
<li>출처: kakao development ios 문서<br>
모든 설치과정을 마치면 아래 사진과 같이 새로운 패키지가 추가된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/51589ade-77e9-468e-9ece-09b640e23930/image.png" alt=""></li>
</ul>
<hr>
<h1 id="3-앱-실행-허용-목록-설정">3. 앱 실행 허용 목록 설정</h1>
<h2 id="1-infolist-수정하기">1. info.list 수정하기</h2>
<blockquote>
<p>info.list를 수정하는 방법은 2가지가 존재한다
이 2가지 방식 중 하나를 선택하면 된다</p>
</blockquote>
<ul>
<li>info.list 딕셔너리에 목록을 추가하여 수정하기</li>
<li>info.list를 코드를 이용해 직접 수정하기</li>
</ul>
<h3 id="a-딕셔너리에-목록을-추가하여-수정하기">a. 딕셔너리에 목록을 추가하여 수정하기</h3>
<blockquote>
<p>[Info] &gt; [Custom iOS Target Properties]에 Array 타입 키(Key)인 LSApplicationQueriesSchemes를 추가하고, 해당 키의 [Item]으로 커스텀 URL 스킴에 사용할 값인 &quot;kakaokompassauth&quot;, &quot;kakaolink&quot;를 추가.
<img src="https://velog.velcdn.com/images/yyy_sang/post/d61ef919-a90a-472a-ad25-bc4105951caa/image.png" alt=""></p>
</blockquote>
<ul>
<li>출처: kakao development ios 문서<br></li>
</ul>
<h3 id="b-코드로-직접-수정하기">b. 코드로 직접 수정하기</h3>
<blockquote>
<p>우선 info파일에 접근을 해야한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/287bfb34-2011-4ea8-bbc1-f74c240851b3/image.png" alt="">
그러면 info파일이 navigator pane에 생성된 것을 확인할 수 있다<br>
[info 파일 마우스 우클릭] -&gt; [Open As] -&gt; [Source Code]
순서로 실행
<img src="https://velog.velcdn.com/images/yyy_sang/post/56af48bc-08a7-4887-908c-012c94f8e547/image.png" alt="">
아래 코드를 info.list의 딕셔너리에 삽입한다.(딕셔너리 내 삽입 위치 주의!) </p>
</blockquote>
<pre><code class="language-swift"> &lt;key&gt;LSApplicationQueriesSchemes&lt;/key&gt;
  &lt;array&gt;
      &lt;!-- 카카오톡으로 로그인 --&gt;
      &lt;string&gt;kakaokompassauth&lt;/string&gt;
      &lt;!-- 카카오톡 공유 --&gt;
      &lt;string&gt;kakaolink&lt;/string&gt;
  &lt;/array&gt;</code></pre>
<hr>
<h1 id="4-url-schemes-등록하기">4. URL Schemes 등록하기</h1>
<h2 id="1-네이티브-앱-키-입력하기">1. 네이티브 앱 키 입력하기</h2>
<blockquote>
<p>URL Schemes이란 
본래 파란색으로 표시한 부분에 네이티브 앱 키를 입력해도 실행이 되지만 네이티브 키는 중요한 정보이므로 해당 내용을 소스코드에서 노출되는 점을 해결해야한다. 따라서 config 파일을 이용하여 네이티브 앱 키를 저장하고 변수에 네이티브 앱 키를 저장해 변수로 사용이 가능하다
<img src="https://velog.velcdn.com/images/yyy_sang/post/402077b3-05e8-4108-a2d4-545fdcd2b26f/image.png" alt="">
아래 사진처럼 새로운 파일을 생성한다
검색창에 config를 검색하고 해당 파일을 만들면 된다 - 확장자는 xconfig다
<img src="https://velog.velcdn.com/images/yyy_sang/post/f12f6d5a-e3c2-4640-96c4-7b5f163d3f42/image.png" alt="">
아래 사진과 같이 변수를 지정하고 개인별로 지급된 네이티브 앱 키를 저장한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/3da35db1-ba25-4bc4-b6fb-56aa88f42b65/image.png" alt="">
config에 있는 네이티브 앱 키를 사용하기 위해서 info.plist에 카카오에서 표기한 <strong>kakao${NATIVE_APP_KEY}</strong> 형식의 문자열을 저장해야 한다
url 스킴을 위해 사용하는 ${KAKAO_NATIVE_APP_KEY}에 config파일에 저장한 KAKAO_NATIVE_APP_KEY변수에 저장된 네이티브 앱 키로 설정하고 사용한다는 의미가 된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/efa0d9c6-9228-4c7a-8f97-65b881202ab5/image.png" alt="">
그리고 아래 url스킴을 만드는 자리에 지정해놓은 토큰인 ${KAKAO_NATIVE_APP_KEY}를 넣으면 된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/9dafafae-171a-4b5b-a3e2-e13f7fbe9296/image.png" alt="">
그리고 아래와 같이 config파일을 사용하겠다는 의미로 3번 부분의 요소를 none에서 config로 바꿔줘야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/458ccc8e-cb1f-4cc1-98db-bcbdc2cd614b/image.png" alt="">
인터넷 api통신에서의 안정성을 위해 아래 사진과 같이 info.plist에 파란색으로 표기된 요소를 삽입한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/29dfe7d8-b3b2-42af-908e-7eaacd904d31/image.png" alt=""></p>
</blockquote>
<hr>
<h1 id="5-카카오-로그인-활성상태-on">5. 카카오 로그인 활성상태 on</h1>
<blockquote>
<p>kakao Development에서 내 애플리케이션 -&gt; 등록된 어플 -&gt; 카카오 로그인 -&gt; 활성상태 변경을 해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/b112f709-5bf6-4667-96a2-2c9bfbf32ae0/image.png" alt=""></p>
</blockquote>
<ul>
<li>출처: kakao development ios 문서<br></li>
</ul>
<hr>
<h1 id="6-redirect-uri-설정">6. Redirect URI 설정</h1>
<blockquote>
<p>아래 사진에 위치한 Redirect URI을 추가한다
해당 위치에 들어가는 주소는 회사 사이트 주소로 지정하면 된다
<img src="https://velog.velcdn.com/images/yyy_sang/post/846f322c-232c-4f16-9897-2ae14f0876ce/image.png" alt=""></p>
</blockquote>
<ul>
<li>출처: kakao development ios 문서<br></li>
</ul>
<hr>
<h1 id="7-delegate에-코드-넣기">7. Delegate에 코드 넣기</h1>
<blockquote>
<p>카카오 문서를 보면 아래 사진과 같이 AppDelegate에 라이브러리 import와 코드 삽입에 관한 정보를 볼 수 있다.
<img src="https://velog.velcdn.com/images/yyy_sang/post/9307e0cd-17b1-4d64-9aa9-b34807b1e430/image.png" alt=""></p>
</blockquote>
<ul>
<li>출처: kakao development ios 문서<br>
해당 코드에 대하여 AppDelegate에 붙여 넣는 과정에서 약간의 변형이 필요하다
&quot;${NATIVE_APP_KEY}&quot;는 단순히 문자열 상수이므로. 실제 앱키 값을 의미하지 않는다
반면에 Bundle.main.infoDictionary?[&quot;KAKAO_NATIVE_APP_KEY&quot;]는 info.plist 파일에서 실제 앱키 값을 동적으로 읽어오는 코드다.
그러므로 현재 프로젝트의 주 번들인 info.plist에 에서 키값을 추출해 오는 과정이 필요하다 따라서 아래와 같이 appDelegate에 코드를 삽입하면 된다<pre><code class="language-swift">import KakaoSDKCommon
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {
      let nativeAppKey = Bundle.main.infoDictionary?[&quot;KAKAO_NATIVE_APP_KEY&quot;] ?? &quot;&quot;
      KakaoSDK.initSDK(appKey: nativeAppKey as! String) 
      return true
  }
...</code></pre>
이후로 아래 사진과 같이 AppDelegate에 작성해야할 카카오톡으로 로그인을 위한 필수적인 요소가 존재하는데 해당 코드를 작성해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/bb8a4802-cc4b-4edd-a589-bd0bcdc1d007/image.png" alt="">
그리고 SceneDelegate에도 작성해야 할 코드가 있다
해당 코드를 복사해 SceneDelegate에 삽입해준다
<img src="https://velog.velcdn.com/images/yyy_sang/post/a07e459f-72ef-49fa-82f4-24a9726ced7b/image.png" alt=""></li>
</ul>
<hr>
<h1 id="7-카카오톡-로그인-코드-작성하기">7. 카카오톡 로그인 코드 작성하기</h1>
<blockquote>
<p><a href="https://developers.kakao.com/docs/latest/ko/kakaologin/ios">https://developers.kakao.com/docs/latest/ko/kakaologin/ios</a></p>
</blockquote>
<blockquote>
<p>아래 사진과 같이 해당 코드를 작성하라고 한다</p>
</blockquote>
<ul>
<li><img src="https://velog.velcdn.com/images/yyy_sang/post/c68ea491-ff39-4554-b051-a8d52c140b5e/image.png" alt=""></li>
<li>출처: kakao development ios 문서<br>
구동 방식에 관하여 아래에서 작성된 대로 우선 카카오톡이 해당 디바이스에 존재하는지 여부를 먼저 판단한다
만약 카카오톡이 디바이스에 존재하지 않는 경우에는 웹뷰를 통해 카카오톡을 인증하도록 할 수 있다
<img src="blob:https://velog.io/ce9b5a92-540e-4261-a9ca-d9b0ebca6e7a" alt="업로드중.."></li>
<li>출처: kakao development ios 문서<br></li>
</ul>
<blockquote>
<p>새로운 swift파일을 생성하여 Model로 사용하면 된다
아래 코드처럼 작성하면 된다
이후 원하는 방식에 따라 비동기로 처리가 가능하다</p>
</blockquote>
<pre><code class="language-swift">import Foundation
import Combine
import KakaoSDKAuth
import KakaoSDKUser

class KakaoAuthVM: ObservableObject {

    var subscriptions = Set&lt;AnyCancellable&gt;()

    func handleKakaLogin() {
        print(&quot;KakaoAuthVM - handleKakaoLogin() called&quot;)

        // 카카오톡 실행 가능 여부 확인
        if (UserApi.isKakaoTalkLoginAvailable()) {
            // 카카오 앱으로 로그인
            UserApi.shared.loginWithKakaoTalk {(oauthToken, error) in
                if let error = error {
                    print(error)
                }
                else {
                    // 카카오 계정으로 로그인
                    print(&quot;loginWithKakaoTalk() success.&quot;)

                    //do something
                    _ = oauthToken
                }
            }
        }else {// 카카오톡 미설치 상태 -&gt; 웹으로 이동해 로그인
            UserApi.shared.loginWithKakaoAccount {(oauthToken, error) in
                    if let error = error {
                        print(error)
                    }
                    else {
                        print(&quot;loginWithKakaoAccount() success.&quot;)

                        //do something
                        _ = oauthToken
                    }
                }
        }
    }

    func kakaoLogOut() {
        UserApi.shared.logout {(error) in
            if let error = error {
                print(error)
            }
            else {
                print(&quot;logout() success.&quot;)
            }
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백트래킹(Back Tracking) swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9Back-Tracking-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%ED%8A%B8%EB%9E%98%ED%82%B9Back-Tracking-swift</guid>
            <pubDate>Fri, 01 Sep 2023 09:02:55 GMT</pubDate>
            <description><![CDATA[<h1 id="백트래킹back-tracking">백트래킹(Back Tracking)</h1>
<p>백트래킹(Backtracking) 알고리즘은 해결책에 대한 후보를 구축해 나가다가, 어느 시점에서든 해당 후보가 문제의 해결책이 될 수 없다고 판단되면, 부분 해결책을 버리고 이전 단계로 돌아가는 (즉, &quot;되돌림&quot;을 수행하는) 알고리즘이다. </p>
<p>설명만으로는 어떤 알고리즘인지 알기 어려우니 밑에 설명을 이어가겠다</p>
<p>백트래킹은 주로 재귀함수(Recursive function)를 통해 만들어지며, 트리를 통해 표현이 가능하다</p>
<hr>
<h2 id="백트래킹을-이용하면-좋은-문제">백트래킹을 이용하면 좋은 문제</h2>
<blockquote>
</blockquote>
<ol>
<li>모든 가능한 해를 나열해야 하는 문제 (예: 순열, 조합)</li>
<li>최적의 해를 찾아야 하는 문제 (예: 미로 찾기, 스도쿠)</li>
<li>제약 조건이 있는 상황에서 가능한 해를 찾아야 하는 문제 (예: N-Queens 문제)</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/yyy_sang/post/24006800-2aad-4c5c-860b-8a686b234746/image.jpeg" alt=""></p>
<p>위 사진과 같이 트리 형태의 자료로 표현이 가능한 상황에 각 노드를 거쳐 모든 경우의 수를 확인할 경우 경로에 따른 해결책의 확인이 가능하며 해결책을 찾아내는데 불가능한 경로라고 한다면 해당 경로를 해결책에서 제외시키고 지나온 길을 다시 지나 다음 노드부터 탐색한다</p>
<p>예시로 아래와 같은 과정을 거칠 수 있다. 임의로 몇개의 경로는 해결책이 될 수 없음으로 정한 경우를 예시로 포함한다</p>
<blockquote>
<p>1Aa -&gt; 해결책이 될 수 없는 경로 
1Ab
1Ac
1Ba -&gt; 해결책이 될 수 없는 경로 
1Bb
1Bc
1Ca -&gt; 해결책이 될 수 없는 경로 
1Cb
1Cc
... ...</p>
</blockquote>
<p>이후 특정 조건이 만족되는 모든 경우의 수를 기록한다</p>
<hr>
<h1 id="코드">코드</h1>
<h3 id="구성요소">구성요소</h3>
<blockquote>
<p>함수의 파라미터  </p>
</blockquote>
<ul>
<li>Index -&gt; 탐색이 시작되는 인덱스</li>
<li>letter -&gt; 재귀를 통해 호출하며 각 경로를 통한 결과를 저장하는 버퍼</li>
</ul>
<h3 id="함수-내-요소">함수 내 요소</h3>
<blockquote>
<ul>
<li>함수를 멈추기위한 조건을 정의</li>
</ul>
</blockquote>
<ul>
<li>경로를 통해 탐색해야하는 노드의 배열</li>
<li>배열을 순회하며 백트래킹 함수를 다시 호출</li>
</ul>
<pre><code class="language-swift">// index -&gt; 현재 처리할 numbers 배열의 인덱스를 나타냅니다
// letter -&gt; 특정 조건을 만족시키는지 확인하기위해 사용하는 버퍼
// 해당 경우의 경우 letter에 추가적으로 문자열을 더해 조건을 만족시키면 다른 배열에 저장하는 조건
func backTracking(_ index: Int, _ letter: String) {
        // 조건이 들어가는 자리
        if index == numbers.count {
            result.append(letter)
            return
        }

        //현재 대상에서 탐색되는 위치
        let num = numbers[index]
        // 현재 대상에서 경로를 통해 이어지는 모든 노드가 저장되는 배열
        guard let c = nums[num] else {return}

        // 배열 순회를 통해 각 루트를 따라 이동하여 백트래킹 함수를 재귀적으로 실행
        for i in c {
            backTracking(index + 1, letter + i) 
            //-&gt; 재귀를 통해 데이터를 축적하여 조건을 만족시키기 위해 문자열 i를 추가
        }
    }</code></pre>
<hr>
<h1 id="문제-예시에-대한-코드">문제 예시에 대한 코드</h1>
<p>Leet code 
<strong>17. Letter Combinations of a Phone Number</strong></p>
<blockquote>
<p><a href="https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/">https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/</a></p>
</blockquote>
<pre><code class="language-swift">class Solution {
    let nums: [Character: [String]] = [
            &quot;2&quot;:[&quot;a&quot;,&quot;b&quot;,&quot;c&quot;],
            &quot;3&quot;:[&quot;d&quot;,&quot;e&quot;,&quot;f&quot;],
            &quot;4&quot;:[&quot;g&quot;,&quot;h&quot;,&quot;i&quot;],
            &quot;5&quot;:[&quot;j&quot;,&quot;k&quot;,&quot;l&quot;],
            &quot;6&quot;:[&quot;m&quot;,&quot;n&quot;,&quot;o&quot;],
            &quot;7&quot;:[&quot;p&quot;,&quot;q&quot;,&quot;r&quot;,&quot;s&quot;],
            &quot;8&quot;:[&quot;t&quot;,&quot;u&quot;,&quot;v&quot;],
            &quot;9&quot;:[&quot;w&quot;,&quot;x&quot;,&quot;y&quot;,&quot;z&quot;]
            ]

    var result = [String]()
    var numbers: [Character] = []

    func letterCombinations(_ digits: String) -&gt; [String] {
        if digits.isEmpty {
            return []
        }

        numbers = Array(digits)

        backTracking(0, &quot;&quot;)

        return result
    }

    func backTracking(_ index: Int, _ letter: String) {
        if index == numbers.count {
            result.append(letter)
            return
        }

        let num = numbers[index]

        guard let c = nums[num] else {return}


        for i in c {
            backTracking(index + 1, letter + i)
        }
    }
}
</code></pre>
<hr>
<p>재귀함수가 제일 어렵다;;</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[BFS - 너비우선탐색 swift]]></title>
            <link>https://velog.io/@yyy_sang/BFS-%EB%84%88%EB%B9%84%EC%9A%B0%EC%84%A0%ED%83%90%EC%83%89-swift</link>
            <guid>https://velog.io/@yyy_sang/BFS-%EB%84%88%EB%B9%84%EC%9A%B0%EC%84%A0%ED%83%90%EC%83%89-swift</guid>
            <pubDate>Wed, 23 Aug 2023 12:48:50 GMT</pubDate>
            <description><![CDATA[<h1 id="bfs---너비우선탐색">BFS - 너비우선탐색</h1>
<p>특정 노드로부터 인접한 모든 노드를 순회하는 순서로 그래프를 탐색하는 방법
BFS는 너비 우선 탐색으로 큐를 이용하여 구현된다
(DFS와 다르게 재귀로는 구현되지 않는다)</p>
<p><strong>순서의 예시는 아래와 같다</strong></p>
<p><img src="https://velog.velcdn.com/images/yyy_sang/post/ce52d063-22d8-4ea3-adc7-2b81a021af34/image.jpeg" alt=""></p>
<hr>
<h1 id="구현">구현</h1>
<h3 id="1-그래프의-구현">1. 그래프의 구현</h3>
<pre><code class="language-swift">let graph: [String:[String]] = [
    &quot;A&quot; : [&quot;B&quot;, &quot;C&quot;, &quot;D&quot;],
    &quot;B&quot; : [&quot;A&quot;, &quot;E&quot;],
    &quot;C&quot; : [&quot;A&quot;, &quot;F&quot;],
    &quot;D&quot; : [&quot;A&quot;],
    &quot;E&quot; : [&quot;B&quot;],
    &quot;F&quot; : [&quot;C&quot;],
]</code></pre>
<h3 id="2-dfs-함수-구현">2. DFS 함수 구현</h3>
<p><strong>기본적인 구조는 다음과 같다</strong></p>
<blockquote>
<ul>
<li>탐색 시작 노드를 queue에 넣고 방문처리를 한다</li>
<li>스택의 fron에 존재하는 노드는 모든 인접한 노드의 방문을 끝내지 않은 노드를 의미한다
그러므로 노드를 queue에 넣고 방문처리 한다</li>
<li>만약 해당 노드에 대해 모든 인접한 노드를 방문한 경우 에서 최상단 노드를 꺼낸다</li>
</ul>
</blockquote>
<p>아래 코드의 경우에는 이미 방문한 스택을 확인하는데 있어 배열을 사용하면 최악의 경우 O(N)의 시간복잡도를 가지기 때문에 별도의 Set를 이용해 탐색 시간을 줄이는 방법을 채택한다</p>
<pre><code class="language-swift">func bfs(_ graph: [String: [String]], _ start: String) -&gt; [String] {

    // 방문한 지점을 탐색하기 위해 사용될 Set -&gt; contains를 통해 특정 요소를 탐색하는데 O(1)의 시간복잡도 때문
    // 배열은 탐색에서 최악의 경우 O(N)
    var visitedRecord: Set&lt;String&gt; = []

    // 방문이 필요한 요소들을 저장하기위한 큐
    var needVisitQueue: [String] = [start]

    // 탐색한 노드를 순서대로 기록하기 위한 배열(큐)
    var result: [String] = []

    while !queue.isEmpty {
        // 큐에서 가장 앞에 위치한 노드를 꺼낸다
        let node = queue.removeFirst()

        // 이미 방문한 노드가 아니라면 아래 문장을 실행
        if !visitedRecord.contains(node) {
            //Set에 방문 지점을 저장
            visitedRecord.insert(node)
            탐색한 노드를 순서대로 저장하는 result 배열(큐)에 방문 노드를 저장
            result.append(node)
            방문한 노드에서 인접한 모든 노드를 저장. 단, 이미 거친 노드의 경우 저장하지 않는다
            queue += graph[node]?.filter { !visitedRecord.contains($0) } ?? []
        }
    }
    return result
}</code></pre>
<hr>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-swift">let graph: [String:[String]] = [
    &quot;A&quot; : [&quot;B&quot;, &quot;C&quot;, &quot;D&quot;],
    &quot;B&quot; : [&quot;A&quot;, &quot;E&quot;],
    &quot;C&quot; : [&quot;A&quot;, &quot;F&quot;],
    &quot;D&quot; : [&quot;A&quot;],
    &quot;E&quot; : [&quot;B&quot;],
    &quot;F&quot; : [&quot;C&quot;],
]
func bfs(_ graph: [String: [String]], _ start: String) -&gt; [String] {
    var visitedRecord: Set&lt;String&gt; = []
    var queue: [String] = [start]
    var result: [String] = []

    while !queue.isEmpty {
        let node = queue.removeFirst()
        if !visitedRecord.contains(node) {
            visitedRecord.insert(node)
            result.append(node)
            queue += graph[node]?.filter { !visitedRecord.contains($0) } ?? []
        }
    }
    return result
}



print(bfs(graph, &quot;A&quot;))
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[DFS 탐색 - 깊이우선탐색 swift]]></title>
            <link>https://velog.io/@yyy_sang/DFS-%ED%83%90%EC%83%89-%EB%84%88%EB%B9%84%EC%9A%B0%EC%84%A0%ED%83%90%EC%83%89-swift</link>
            <guid>https://velog.io/@yyy_sang/DFS-%ED%83%90%EC%83%89-%EB%84%88%EB%B9%84%EC%9A%B0%EC%84%A0%ED%83%90%EC%83%89-swift</guid>
            <pubDate>Tue, 22 Aug 2023 06:43:32 GMT</pubDate>
            <description><![CDATA[<h1 id="dfs---깊이우선탐색">DFS - 깊이우선탐색</h1>
<p>특정 노드에서부터 더이상 간선이 존재하지 않는 부분까지 탐색하는 방식</p>
<p>DFS는 깊이 우선 탐색 알고리즘이며, 스택이나 재귀 함수를 사용하여 구현된다 
DFS는 그래프의 구성 요소, 사이클, 위상 정렬 등을 찾는 데 유용하다</p>
<p><img src="https://velog.velcdn.com/images/yyy_sang/post/196843b0-9fd9-4c43-aa18-4231deaafea6/image.jpeg" alt=""></p>
<p><strong>순서의 예시는 아래와 같다</strong>
<img src="https://velog.velcdn.com/images/yyy_sang/post/7162a4ab-2408-4ac5-9a6c-3f066c968652/image.jpeg" alt=""></p>
<hr>
<h1 id="구현">구현</h1>
<h3 id="1-그래프의-구현">1. 그래프의 구현</h3>
<p>우선 그래프는 인접리스트, 인접행렬의 방식으로 구현된다
해당 문서에서는 인접리스트 방식을 이용하였다</p>
<pre><code class="language-swift">let graph: [String:[String]] = [
    &quot;A&quot; : [&quot;B&quot;, &quot;C&quot;, &quot;D&quot;],
    &quot;B&quot; : [&quot;A&quot;, &quot;E&quot;],
    &quot;C&quot; : [&quot;A&quot;, &quot;F&quot;],
    &quot;D&quot; : [&quot;A&quot;],
    &quot;E&quot; : [&quot;B&quot;],
    &quot;F&quot; : [&quot;C&quot;],
]</code></pre>
<h3 id="2-dfs-함수-구현">2. DFS 함수 구현</h3>
<p><strong>기본적인 구조는 다음과 같다</strong></p>
<blockquote>
<ul>
<li>탐색 시작 노드를 stack에 넣고 방문처리를 한다</li>
<li>스택의 top에 존재하는 노드는 모든 인접한 노드의 방문을 끝내지 않은 노드를 의미한다
그러므로 노드를 stack에 넣고 방문처리 한다</li>
<li>만약 해당 노드에 대해 모든 인접한 노드를 방문한 경우 스택에서 최상단 노드를 꺼낸다</li>
</ul>
</blockquote>
<p>아래 코드의 경우에는 이미 방문한 스택을 확인하는데 있어 배열을 사용하면 최악의 경우 O(N)의 시간복잡도를 가지기 때문에 별도의 Set를 이용해 탐색 시간을 줄이는 방법을 채택한다</p>
<pre><code class="language-swift">
func dfs(_ graph: [String:[String]], _ start: String) -&gt; [String] {
    var visitedRecord: Set&lt;String&gt; = []
    var needVisit: [String] = [start]
    var result:[String] = []

    while !needVisit.isEmpty {
        let node = needVisit.removeLast()
        if !visitedRecord.contains(node) {
            visitedRecord.insert(node)
            result.append(node)
            needVisit += graph[node] ?? []
        }
    }
    return result
}</code></pre>
<h1 id="전체-코드">전체 코드</h1>
<pre><code class="language-swift">let graph: [String:[String]] = [
    &quot;A&quot; : [&quot;B&quot;, &quot;C&quot;, &quot;D&quot;],
    &quot;B&quot; : [&quot;A&quot;, &quot;E&quot;],
    &quot;C&quot; : [&quot;A&quot;, &quot;F&quot;],
    &quot;D&quot; : [&quot;A&quot;],
    &quot;E&quot; : [&quot;B&quot;],
    &quot;F&quot; : [&quot;C&quot;],
]

func dfs(_ graph: [String:[String]], _ start: String) -&gt; [String] {
    // 방문을 완료한 노드 저장
    // -&gt; 방문 여부의 확인 시간을 줄이기 위한 set
    var visitedRecord: Set&lt;String&gt; = []

    // 인접 노드의 방문이 완료되지 않는 노드를 기록
    // top에 위치한 노드에서 인접 노드를 탐색해야 한다
    var needVisit: [String] = [start]

    // 방문 순서를 저장하기 위한 result
    var result:[String] = []

    // 방문해야할 노드가 사라질때까지 반복
    while !needVisit.isEmpty {
        // 방문의 필요가 있는 노드를 top에서(in stack) 꺼내 변수에 저장
        let node = needVisit.removeLast()
        // 해당 노드를 방문한적이 없으면 인접한 노드를 탐색
        if !visitedRecord.contains(node) {
            // 해당 노드를 방문처리
            visitedRecord.insert(node)
            result.append(node)

            // 탐색이 필요한 노드들을 그대로 더한다
            // 만약 모든 인접 노드를 방문한 경우 빈 배열을 더한다
            needVisit += graph[node]?.reversed() ?? []
        }
    }

    return result
}


print(dfs(graph, &quot;A&quot;)) // 결과 : [&quot;A&quot;, &quot;B&quot;, &quot;E&quot;, &quot;C&quot;, &quot;F&quot;, &quot;D&quot;]
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[사진 클릭시 캘린더 사진 지정하기 ]]></title>
            <link>https://velog.io/@yyy_sang/%EC%82%AC%EC%A7%84-%ED%81%B4%EB%A6%AD%EC%8B%9C-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%82%AC%EC%A7%84-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yyy_sang/%EC%82%AC%EC%A7%84-%ED%81%B4%EB%A6%AD%EC%8B%9C-%EC%BA%98%EB%A6%B0%EB%8D%94-%EC%82%AC%EC%A7%84-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 21 Aug 2023 09:56:07 GMT</pubDate>
            <description><![CDATA[<h1 id="phpickerviewcontroller">PHPickerViewController</h1>
<p><strong><a href="https://developer.apple.com/documentation/photokit/phpickerviewcontroller">https://developer.apple.com/documentation/photokit/phpickerviewcontroller</a></strong></p>
<h2 id="정의">정의</h2>
<p>사진첩으로부터 사진들을 선택할 수 있도록 유저 인터페이스를 제공하는 뷰 컨트롤러</p>
<hr>
<h2 id="예시">예시</h2>
<p><strong>아래 화면과 같이 이미지 클릭시 사진첩을 열어 사진을 선택할 수 있도록 한다</strong></p>
<p><img src="https://velog.velcdn.com/images/yyy_sang/post/8c056fd1-bd89-41da-b64a-36be381edbbf/image.gif" alt=""></p>
<hr>
<h2 id="구현">구현</h2>
<p>구현된 프로젝트 링크 -&gt;
<strong><a href="https://github.com/sang9984/PHPickerViewController-test">https://github.com/sang9984/PHPickerViewController-test</a></strong></p>
<h3 id="1-import-photosui">1. import PhotosUI</h3>
<p><strong>PHPickerViewController</strong>를 사용하기 위해서는 <strong>PhotosUI</strong>를 import 해야 한다</p>
<pre><code class="language-swift">import PhotosUI</code></pre>
<h3 id="2-제스쳐-설정하기">2. 제스쳐 설정하기</h3>
<p>UITapGestureRecognizer는 제스쳐를 인식할 수 있도록 도와주는 도구다
해당 객체를 가져와 대상을 지정하고 화면 내에 존재하는 객체(image 등)에 대한 제스쳐 인식을 활성화 해야 한다</p>
<pre><code class="language-swift">func setupTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(touchUpImageView))
        image.isUserInteractionEnabled = true
        image.addGestureRecognizer(tapGesture)
    }</code></pre>
<p>또한 #selector()를 통해 제스쳐를 인식할 경우 수행할 기능에 대해 함수로 정의해야 한다
제스쳐가 인식되면 아래 @objc 함수를 실행시켜 함수 내 기능을 수행한다
setupImagePicker()에 대해서는 3번에서 설명</p>
<pre><code class="language-swift">@objc func touchUpImageView() {
        setupImagePicker()
    }</code></pre>
<h3 id="3-pickerview-설정하기">3. pickerView 설정하기</h3>
<p>여기서 필요한 객체가 2가지가 있다</p>
<ul>
<li>PHPickerConfiguration() -&gt; PHPickerViewController의 동작 등을 구성하는데 사용되는 객체</li>
<li>PHPickerViewController() -&gt; 피커뷰 컨트롤러를 실행시키기 위한 객체</li>
</ul>
<pre><code class="language-swift">func setupImagePicker() {
        // PHPickerConfiguration() -&gt; PHPickerViewController의 동작 등을 구성하는데 사용되는 객체
        // 해당 객체를 통해 선택되는 사진의 개수나 파일 종류를 지정할 수 있다
        var configuration = PHPickerConfiguration()
        // 사진 선택 개수 제한: 0이면 제한없음, 0이외의 수 -&gt; 해당 개수만큼 제한
        configuration.selectionLimit = 0
        // 선택할 수 있는 파일의 종류
        configuration.filter = .any(of: [.images, .videos])

        // 기본설정을 바탕으로, 피커뷰컨트롤러 생성
        let picker = PHPickerViewController(configuration: configuration)
        // 피커뷰 컨트롤러의 대리자 설정
        picker.delegate = self
        // 피커뷰 띄우기
        self.present(picker, animated: true, completion: nil)
    }</code></pre>
<h3 id="4-delegate-설정하기">4. Delegate 설정하기</h3>
<p>피커뷰에 대한 기본적인 설정이 완료된 경우 delegate를 통해 실질적인 기능을 수행할 수 있도록 해야 한다</p>
<pre><code class="language-swift">extension ViewController: PHPickerViewControllerDelegate {

    // 사진이 선택이 된 후에 호출되는 메서드
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        // 피커뷰 dismiss
        picker.dismiss(animated: true)

        let itemProvider = results.first?.itemProvider

        if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
            itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
                DispatchQueue.main.async {
                    // 이미지뷰에 표시
                    self.image.image = image as? UIImage
                }
            }
        } else {
            print(&quot;이미지 불러오기 실패&quot;)
        }
    }
}</code></pre>
<hr>
<h2 id="전체-예시-코드">전체 예시 코드</h2>
<pre><code class="language-swift">import UIKit
import PhotosUI

class ViewController: UIViewController {

    // 이미지 생성
    let image: UIImageView = {
        let image = UIImageView()
        image.image = UIImage(named: &quot;swift-og&quot;)
        image.layer.cornerRadius = 100
        image.clipsToBounds = true

        return image
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupTapGesture()
        setupAutolayout()
    }

    func setupAutolayout() {
        image.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(image)

        NSLayoutConstraint.activate([

            image.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            image.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            image.heightAnchor.constraint(equalToConstant: 200),
            image.widthAnchor.constraint(equalToConstant: 200)
        ])
    }

    //MARK: - pickerViewController 기본 설정

    func setupTapGesture() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(touchUpImageView))
        image.isUserInteractionEnabled = true
        image.addGestureRecognizer(tapGesture)
    }

    // 제스쳐에서 addTarget을 통해 지정한 함수
    // 제스쳐 발생시 어떤 기능을 수행해야 하는지 정의하는 함수
    @objc func touchUpImageView() {
        setupImagePicker()
    }

    func setupImagePicker() {
        // PHPickerConfiguration() -&gt; PHPickerViewController의 동작 등을 구성하는데 사용되는 객체
        // 해당 객체를 통해 선택되는 사진의 개수나 파일 종류를 지정할 수 있다
        var configuration = PHPickerConfiguration()
        // 사진 선택 개수 제한: 0이면 제한없음, 0이외의 수 -&gt; 해당 개수만큼 제한
        configuration.selectionLimit = 0
        // 선택할 수 있는 파일의 종류
        configuration.filter = .any(of: [.images, .videos])

        // 기본설정을 바탕으로, 피커뷰컨트롤러 생성
        let picker = PHPickerViewController(configuration: configuration)
        // 피커뷰 컨트롤러의 대리자 설정
        picker.delegate = self
        // 피커뷰 띄우기
        self.present(picker, animated: true, completion: nil)
    }

}
//MARK: - 피커뷰 델리게이트 설정

extension ViewController: PHPickerViewControllerDelegate {

    // 사진이 선택이 된 후에 호출되는 메서드
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        // 피커뷰 dismiss
        picker.dismiss(animated: true)

        let itemProvider = results.first?.itemProvider

        if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
            itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
                DispatchQueue.main.async {
                    // 이미지뷰에 표시
                    self.image.image = image as? UIImage
                }
            }
        } else {
            print(&quot;이미지 불러오기 실패&quot;)
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 5397번 - 키로거 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-5397%EB%B2%88-%ED%82%A4%EB%A1%9C%EA%B1%B0-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-5397%EB%B2%88-%ED%82%A4%EB%A1%9C%EA%B1%B0-swift</guid>
            <pubDate>Tue, 15 Aug 2023 06:07:31 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-예시">문제 예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">2
&lt;&lt;BP&lt;A&gt;&gt;Cd-
ThIsIsS3Cr3t</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">BAPC
ThIsIsS3Cr3t</code></pre>
<hr>
<h1 id="규칙">규칙</h1>
<ol>
<li>&quot;&lt;&quot; 와 &quot;&gt;&quot;는 키보드의 커서를 이동시키는 역할을 한다</li>
<li>&quot;-&quot;는 바로 앞 문자를 없애는 역할을 한다</li>
<li>1과 2외의 문자를 제외한 나머지 문자와 숫자는 입력받으면 그대로 키로그에 기록된다</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>배열 내에서 <strong>insert(newElement:, at:)</strong>를 이용해 풀이를 하려했으나 시간초과가 발생</li>
</ol>
<p>-&gt; 따라서 문자열 처리를 위한 스택 2개를 이용하기로 결정</p>
<ol start="2">
<li><p>스택 2개는 각각 키로그를 기록할 공간, &quot;&lt;&quot;, &quot;&gt;&quot;, &quot;-&quot;를 확인한 경우 해당 역할에 맞게 문자를 이동시키기 위한 예비 버퍼의 역할을 한다</p>
<p> a. 스택1에는 키로그를 기록한다
 b. 만약 &quot;&lt;&quot;를 확인한 경우 커서를 왼쪽으로 이동시키게 되므로 스택1의 키로그 중 마지막 문자를 스택 2로 이동시킨다 
 c. 만약 &quot;&gt;&quot;를 확인한 경우 커서를 오른쪽으로 이동시키게 되므로 스택 2의 문자를 스택 1로 이동시킨다
 d. 만약 &quot;-&quot;를 확인한 경우 키로그의 문자를 하나 없애고 커서를 왼쪽으로 하나 이동시킨다</p>
</li>
<li><p>스택2에 남아있는 문자가 있으면 전부 스택1로 옮기고 마지막에 joined()를 하여 출력한다</p>
</li>
</ol>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">import Foundation

let input = Int(readLine()!)!

for _ in 0..&lt;input {
    let a = readLine()!

    var stack1 = [String]()
    var stack2 = [String]()

    for i in a {
        if i == &quot;&lt;&quot;, !stack1.isEmpty {
            stack2.append(stack1.removeLast())
        } else if i == &quot;&gt;&quot;, !stack2.isEmpty {
            stack1.append(stack2.removeLast())
        } else if i == &quot;-&quot;, !stack1.isEmpty {
            stack1.removeLast()
        } else if i != &quot;&lt;&quot; &amp;&amp; i != &quot;&gt;&quot; &amp;&amp; i != &quot;-&quot; {
            stack1.append(String(i))
        }
    }
    while !stack2.isEmpty {
        stack1.append(stack2.removeLast())
    }

    print(stack1.joined())
}
</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 1620 - 나는야 포켓몬 마스터 이다솜 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-1620-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-1620-%EB%82%98%EB%8A%94%EC%95%BC-%ED%8F%AC%EC%BC%93%EB%AA%AC-%EB%A7%88%EC%8A%A4%ED%84%B0-%EC%9D%B4%EB%8B%A4%EC%86%9C-swift</guid>
            <pubDate>Sat, 12 Aug 2023 05:09:19 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-예시">문제 예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">26 5
Bulbasaur
Ivysaur
Venusaur
Charmander
Charmeleon
Charizard
Squirtle
Wartortle
Blastoise
Caterpie
Metapod
Butterfree
Weedle
Kakuna
Beedrill
Pidgey
Pidgeotto
Pidgeot
Rattata
Raticate
Spearow
Fearow
Ekans
Arbok
Pikachu
Raichu
25
Raichu
3
Pidgey
Kakuna</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">Pikachu
26
Venusaur
16
14</code></pre>
<hr>
<h1 id="규칙">규칙</h1>
<ol>
<li>포켓몬의 이름을 입력하며, 입력한 순서대로 도감에서 순서가 부여된다</li>
<li>제시되는 문제의 경우 도감의 번호와 포켓몬의 이름이 제시된다</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>해시를 생성하여 도감의 번호(key)와 포켓몬 이름(value)을 저장해야 한다</li>
<li>제시된 문제는 다음과 같다<ul>
<li>포켓몬 이름이 입력되면 도감 번호를 출력해야 한다</li>
<li>도감 번호이 입력되면 포켓몬 이름을 출력해야 한다</li>
</ul>
</li>
<li>두 종류의 입력에 대해 빠른 답을 위해서는 2개의 해시가 필요하다</li>
</ol>
<p>-&gt; 하나의 해시를 이용할 경우 두 종류의 문제중 하나의 답은 시간복잡도가 O(1) 이지만
    다른 문제의 답을 구하기 위해서는 해시의 순회로 인해 시간복잡도가 최악의 경우 O(N)이 걸릴 수 있다</p>
<ol start="4">
<li><p>2개의 해시의 형태를 [포켓몬 이름(String): 도감 번호(String)], [도감 번호(String), 포켓몬 이름(String)]</p>
</li>
<li><p>도감과 포켓몬 이름은 중복되는 데이터가 제공되지 않기 때문에 하나의 해시에서 nil이 나온다면 다른 해시에 데이터가 들어 있는 것이므로 이를 판별하면 된다</p>
</li>
</ol>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let input = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}
var hash = [String:String]()
var hash2 = [String:String]()

for i in 1...input[0] {
    let a = readLine()!
    hash[String(i)] = a
    hash2[a] = String(i)
}

for _ in 0..&lt;input[1] {
    let b = readLine()!
    hash[b] == nil ? print(hash2[b]!) : print(hash[b]!)
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 24347번 - 저울 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-24347%EB%B2%88-%EC%A0%80%EC%9A%B8-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-24347%EB%B2%88-%EC%A0%80%EC%9A%B8-swift</guid>
            <pubDate>Fri, 11 Aug 2023 12:06:10 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-예시">문제 예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">7
3 1 6 2 7 30 1</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">21</code></pre>
<hr>
<h1 id="규칙">규칙</h1>
<ol>
<li>추의 개수와 각 추의 무게를 제시한다</li>
<li>각 추를 더해 만들 수 있는 추의 무게 중 가장 낮은 무게를 찾아낸다</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li><p>만약 내가 가지고 있는 저울추들로 1~K무게를 모두 만들 수 있다고 가정</p>
</li>
<li><p>저울추(L)가 K무게 보다 같거나 작다면, 다음 저울추로 (1+L)<del>(K+L)무게를 모두 만들 수 있다. 
그렇다면 다음 저울추까지 포함했을때 1</del>(K+L)무게를 모두 만들 수 있다.</p>
</li>
</ol>
<h3 id="예시">예시</h3>
<p>저울추를 오름차순으로 정렬하면:
[1, 1, 2, 3, 6, 7, 30]</p>
<ul>
<li><p>첫 번째와 두 번째 저울추인 1, 1을 사용하면 무게 1, 2를 만들 수 있다</p>
</li>
<li><p>세 번째 저울추인 2를 사용하면, 현재까지의 무게(1, 2)에 각각 2를 더하여 무게 3, 4를 만들 수 있다</p>
</li>
<li><p>네 번째 저울추인 3을 사용하면, 현재까지의 무게(1, 2, 3, 4)에 각각 3을 더하여 무게 4, 5, 6, 7을 만들 수 있다</p>
</li>
<li><p>다섯 번째 저울추인 6을 사용하면, 현재까지의 무게(1~7)에 각각 6을 더하여 무게 7, 8, 9, 10, 11, 12, 13을 만들 수 있다</p>
</li>
<li><p>여섯 번째 저울추인 7을 사용하면, 현재까지의 무게(1~13)에 각각 7을 더하여 무게 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20을 만들 수 있다</p>
</li>
<li><p>이렇게 주어진 저울추를 조합하여 1부터 20까지의 무게를 모두 만들 수 있다</p>
</li>
</ul>
<p>만약 (1 ~ 20)까지 만들 수 있는 무게에서 30을 더할 경우 30을 포함하여 (30 ~ 50)까지 만들 수 있기 때문에 (20 ~ 29) 사이의 값은 만들 수 없다</p>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let input = Int(readLine()!)!
var weight = readLine()!.split(separator:&quot; &quot;).map{Int($0)!}
weight.sort()

var x = 1
for i in weight {
    if x &gt;= i {
        x += i
    }else {
        break
    }
}
print(x)
</code></pre>
<hr>
<p>** 시간 줄이기 참 어렵다..**</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 13164번 - 행복 유치원 swift ]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-13164%EB%B2%88-%ED%96%89%EB%B3%B5-%EC%9C%A0%EC%B9%98%EC%9B%90-swift-w9twxe8v</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-13164%EB%B2%88-%ED%96%89%EB%B3%B5-%EC%9C%A0%EC%B9%98%EC%9B%90-swift-w9twxe8v</guid>
            <pubDate>Fri, 11 Aug 2023 09:22:14 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-예시">문제 예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">5 3
1 3 5 6 10</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">3</code></pre>
<hr>
<h1 id="규칙">규칙</h1>
<ol>
<li>그룹별 가장 키가 큰 사람과 가장 키가 작은 사람 사이의 키 차이가 티셔츠 제조 비용이 된다</li>
<li>전체 티셔츠 제조 비용에 대해 최소로 만들기 위해서는 각 그룹별 키 차이 또한 최소가 되어야 한다</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<p>문제 예시를 통해 풀이</p>
<blockquote>
<p>[1, 3, 5, 6, 10]</p>
</blockquote>
<p>3개의 그룹을 만든다고 가정하며 해당 유치원생들의 키가 주어졌을때 그룹별 가장 큰 키와 작은 키 차이를 최소화 해야 한다
우선 각 유치원생들의 키 차이를 배열에 저장</p>
<blockquote>
<p>[2, 2, 1, 4]</p>
</blockquote>
<p>여기서 그룹을 </p>
<blockquote>
<p>[1,3,5],  [6],  [10]</p>
</blockquote>
<p>그룹 내 요소가 1개라면 비용이 0으로 되지만 
2개 이상인 그룹에서의 티셔츠 비용은 해당 그룹에 포함된 요소들의 각 키 차이에 대한 누적합이 된다</p>
<p>그러므로 필요에 의해 각 그룹별 2명 이상으로 인원수를 구성하되 가장 적은 키차이로 만들어내야 하며 
가장 큰 키차이를 나타내는 요소에 대해서는 1인만 들어가는 그룹내에 넣어야 한다</p>
<blockquote>
<p>[2, 2, 1, 4]</p>
</blockquote>
<p>사이에서 가장 적은 차를 가진 이들끼리 묶을 경우 키차이가 2인 그룹 하나,  1 차이인 그룹 하나, 차가 0인 그룹 하나가 생성된다
이때 티셔츠 구매 제작 비용에 대한 결과에 실질적으로 영향을 주는 그룹은 2개가 되며 (전체 인원수 - 그룹의 개수)를 하면 해당 결과가 나오게 된다
그럼 (전체 인원수 - 그룹의 개수) 만큼 가장 차이가 작은 그룹들의 합을 구하면 결과가 나온다</p>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let input = readLine()!.split(separator:&quot; &quot;).map{Int($0)!}
let talls = readLine()!.split(separator:&quot; &quot;).map{Int($0)!}.sorted()
var subTalls = [Int]()
var result = 0

for i in 0..&lt;talls.count - 1 {
    subTalls.append(talls[i+1] - talls[i])
}
subTalls.sort()

for i in 0..&lt;(input[0] - input[1]) {
    result += subTalls[i]
}
print(result)</code></pre>
<hr>
<p>머리가 아프다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 12904번 - A와 B swift  ]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-12904%EB%B2%88-A%EC%99%80-B-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-12904%EB%B2%88-A%EC%99%80-B-swift</guid>
            <pubDate>Fri, 11 Aug 2023 07:21:49 GMT</pubDate>
            <description><![CDATA[<p><strong><a href="https://www.acmicpc.net/problem/12904">https://www.acmicpc.net/problem/12904</a></strong></p>
<h1 id="문제예시">문제예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">B
ABBA</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">1</code></pre>
<hr>
<h3 id="1-입력-1">1. 입력</h3>
<pre><code class="language-swift">AB
ABB</code></pre>
<h3 id="2-출력-1">2. 출력</h3>
<pre><code class="language-swift">0</code></pre>
<hr>
<h1 id="규칙">규칙</h1>
<ol>
<li>A를 맨 뒷자리에 추가한다</li>
<li>기존 문자열을 거꾸로 뒤집고 B를 추가한다</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<p><strong>규칙</strong>에서 제시된 내용을 반대로 시행한다
*<em>문자열의 길이가 같아질때까지 아래 작업을 반복한다    *</em></p>
<ul>
<li><ol>
<li>문자열의 마지막 문자가 &quot;A&quot;인 경우 -&gt; A를 제거한다</li>
</ol>
</li>
<li><ol start="2">
<li>문자열의 마지막 문자가 &quot;B&quot;인 경우 -&gt; B를 제거하고 B가 제거된 문자를 뒤집는다</li>
</ol>
</li>
</ul>
<p>문자열의 길이가 같아지면 두 문자열이 같은지 비교한 후 결과를 출력한다</p>
<h2 id="예시">예시</h2>
<h3 id="문제-예시-1번을-참조한-예시">문제 예시 1번을 참조한 예시</h3>
<h3 id="1-입력-2">1. 입력</h3>
<pre><code class="language-swift">B
ABBA</code></pre>
<h3 id="2-출력-2">2. 출력</h3>
<pre><code class="language-swift">1</code></pre>
<hr>
<ol>
<li>ABBA -&gt; 마지막 문자는 A -&gt; A 제거</li>
<li>ABB -&gt; 마지막 문자는 B -&gt; B 제거 후 문자열 뒤집기</li>
<li>BA -&gt; 마지막 문자가 A -&gt; A 제거</li>
<li>B == B 이므로 결과로 1을 출력</li>
</ol>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let a = readLine()!
var b = readLine()!.map{String($0)}

while a.count != b.count {
   if b[b.count - 1] == &quot;A&quot; {
       b.removeLast()
   }else {
       b.removeLast()
       b = b.reversed()
   }
}

print(b.joined() == a ? 1 : 0)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 : 1213 - 팰린드롬 만들기 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-1213-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-1213-%ED%8C%B0%EB%A6%B0%EB%93%9C%EB%A1%AC-%EB%A7%8C%EB%93%A4%EA%B8%B0-swift</guid>
            <pubDate>Fri, 11 Aug 2023 03:52:52 GMT</pubDate>
            <description><![CDATA[<p><strong><a href="https://www.acmicpc.net/problem/1213">https://www.acmicpc.net/problem/1213</a></strong></p>
<h1 id="문제예시">문제예시</h1>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">AABB</code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code>ABBA</code></pre><hr>
<h3 id="1-입력-1">1. 입력</h3>
<pre><code class="language-swift">AAABB</code></pre>
<h3 id="2-출력-1">2. 출력</h3>
<pre><code>ABABA</code></pre><hr>
<h3 id="1-입력-2">1. 입력</h3>
<pre><code class="language-swift">ABACABA</code></pre>
<h3 id="2-출력-2">2. 출력</h3>
<pre><code>AABCBAA</code></pre><hr>
<h3 id="1-입력-3">1. 입력</h3>
<pre><code class="language-swift">ABCD</code></pre>
<h3 id="2-출력-3">2. 출력</h3>
<pre><code>I&#39;m Sorry Hansoo</code></pre><hr>
<h1 id="규칙">규칙</h1>
<ol>
<li><p>팰린드롬은 대칭의 형태를 가진 문자열로 거꾸로 읽어도 원래 순서대로 읽은 문자와 동일한 형태를 가진다</p>
</li>
<li><p>문자열은 가운데를 기준으로 양쪽 문자열이 대칭을 이룬다</p>
</li>
<li><p>만약 동일한 문자의 개수에 대하여 홀수인 개수가 여러개일 경우 해당 문자는 팰린드롬을 만들 수 없다</p>
</li>
<li><p>대칭 문자에 관해 문자열 전체 길이가 홀수인 경우를 제외하면 가운데를 기준으로 모든 각 문자의 전체 갯수는 왼쪽, 혹은 오른쪽의 문자열에 포함된 각 문자의 개수의 * 2다</p>
</li>
</ol>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>해쉬를 이용하여 각 문제에 대한 개수를 파악한다 -&gt; 여기서 팰린드롬을 만들 수 있는지 여부를 확인한다
a. 만약 A, B, C 문자를 저장했을때 각 문자의 개수가 홀수가 되는 경우는 단 하나만 존재해야 한다</li>
</ol>
<p>-&gt; 즉, 3개의 문자중 홀수개를 가진 문자는 하나만 있어야 하며, 2개 이상의 문자가 홀수개를 가진 경우 팰린드롬을 만들 수 없다</p>
<ol start="2">
<li><p>팰린드롬을 만들 수 있는 경우 해쉬를 &quot;문자 오름차순&quot; 으로 정렬한다</p>
</li>
<li><p>정렬된 해쉬를 순회하면서 각 문자의 개수를 2로 나눈다
a. 3의 결과로 나온 몫은 문자의 개수는 중심을 기준으로 한쪽 방향(왼쪽 또는 오른쪽)에 들어가는 문자의 개수를 의미한다
b. 만약 3의 결과와 함께 문자개수가 홀수(문자갯수 % 2 != 0)인 경우 해당 문자가 전체 길이가 홀수인 팰린드롬의 가운데 문자가 된다
c. 3의 결과로 나온 몫 만큼 stack과 문자열에 문자를 삽입한다</p>
</li>
<li><p>순회가 완료되면 mid를 문자열에 추가한다</p>
</li>
<li><p>이후 stack을 pop하여 모든 문자를 문자열에 삽입한다</p>
</li>
</ol>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let input = readLine()!.map { String($0) }
var hash = [String:Int]()
var stack = [String]()
var result = &quot;&quot;
var mid = &quot;&quot;

for i in input {
    if hash[i] == nil {
        hash[i] = 1
    }else {
        hash[i]! += 1
    }
}

if hash.filter({$0.value % 2 != 0}).count &gt; 1 {
    print(&quot;I&#39;m Sorry Hansoo&quot;)
}else {
    let sortedHash = hash.sorted(by: { $0.key &lt; $1.key })

    for (key, value) in sortedHash {
        let stringCount = value / 2

        if value % 2 != 0 {
            mid = key
        }

        for _ in 0..&lt;stringCount {
            stack.append(key)
            result += key
        }
    }

    result += mid

    while !stack.isEmpty {
        result.append(stack.removeLast())
    }
    print(result)
}</code></pre>
<h1 id="최적화-코드">최적화 코드</h1>
<pre><code class="language-swift">let input = readLine()!.map { String($0) }
var hash = input.reduce(into: [String: Int]()) { $0[$1, default: 0] += 1 }

var result = &quot;&quot;
var mid = &quot;&quot;

if hash.values.filter({ $0 % 2 != 0 }).count &gt; 1 {
    print(&quot;I&#39;m Sorry Hansoo&quot;)
} else {
    for key in hash.keys.sorted() {
        let stringCount = hash[key]! / 2

        if hash[key]! % 2 != 0 {
            mid = key
        }

        result += String(repeating: key, count: stringCount)
    }

    let reversedResult = String(result.reversed())
    result += mid + reversedResult
    print(result)
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TableView 만들기]]></title>
            <link>https://velog.io/@yyy_sang/TableView-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yyy_sang/TableView-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 02 Aug 2023 08:02:38 GMT</pubDate>
            <description><![CDATA[<h1 id="tableview">TableView</h1>
<p><strong><a href="https://developer.apple.com/documentation/uikit/uitableview">https://developer.apple.com/documentation/uikit/uitableview</a></strong></p>
<ul>
<li>가장 많이 사용되는 형태의 인터페이스</li>
<li>리스트 형태의 데이터를 스크롤을 통해 확인이 가능하다
<img src="https://velog.velcdn.com/images/yyy_sang/post/a11cdaec-644a-4a08-bcc0-91a42c1d3f65/image.png" alt=""></li>
</ul>
<hr>
<h1 id="구성">구성</h1>
<hr>
<h1 id="만드는-과정">만드는 과정</h1>
<ol>
<li>tableView 선언하기</li>
<li>tableView DataSource 채택</li>
<li>tableView Delegate 채택</li>
<li>tableView cell 생성 및 cell 등록</li>
</ol>
<h2 id="1-tableview-선언하기">1. tableView 선언하기</h2>
<p>우선 tableView를 화면에 나타내기 위해서는 View에 tableView를 삽입하여 표시할 수 있도록 해야한다. 또한 화면에 표시하는 것 뿐만 아니라 tableView의 속성(디자인 등)을 변경하기 위해서는 tableView를 변수에 저장할 필요가 있다.</p>
<pre><code class="language-swift">let tableView = UITableView()</code></pre>
<hr>
<h2 id="2-tableview-datasource-채택">2. tableView DataSource 채택</h2>
<p>tableView를 화면에 표시하기 위해서는 <strong>필수적으로 2가지가 필요</strong>하다
만약 아래 2가지를 구현하지 않는다면 tableView를 구현할 수 없다</p>
<ul>
<li>tableView에서 표시할 행의 수</li>
<li>tableView에서 나타낼 cell</li>
</ul>
<h3 id="예시-코드">예시 코드</h3>
<pre><code class="language-swift">tableView.dataSource = self</code></pre>
<pre><code class="language-swift">// UITableViewDataSource 프로토콜 채택
extension ViewController: UITableViewDataSource {

    // tableView에서 표시할 행의 수를 결정하는 함수
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        return 1
    }

    // tableView에서 사용할 cell을 선언하는 함수
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {

         // cell 변수에 tableView에서 재사용하게 될 cell을 지정
        let cell = tableView.dequeueReusableCell(withIdentifier: &quot;CalendarViewCell&quot;, for: indexPath) as! CalendarViewCell
        return cell
    }
}</code></pre>
<h3 id="a-행의-수">a. 행의 수</h3>
<p>행의 수는 tableView에서 보여줄 데이터의 개수를 의미한다</p>
<p>아래 사진과 같이 요약은 행의 수를 1개로, 루틴은 행의 수를 3개로 설정한 결과다
tableView에서 표시할 행의 수를 결정해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/c6619b03-d257-4d4f-8f97-ac4414c639e9/image.png" alt=""></p>
<h3 id="b-cell">b. cell</h3>
<p>cell은 tableView의 각 행에서 나타낼 데이터의 한 항목을 의미한다
tableView는 cell을 데이터를 표시하는데 사용되는 양식에 비유할 수 있다</p>
<p>tableView는 cell을 재활용 하여 데이터를 표시하게 되는데 cell을
&lt;주제목&gt;, &lt;요일, 날짜&gt;, &lt;메모 내에서 적힌 첫번째 줄&gt; 을 표시하는 형태로 설정하고 셀을 사용하면 아래 사진과 같이 tableView에서 표시할 데이터의 항목을 표시한다 
<img src="https://velog.velcdn.com/images/yyy_sang/post/daae74f0-b957-452a-b1c6-ce9dc0831113/image.jpeg" alt=""></p>
<p>cell에 관한 정보를 구현하기 위해서는 별도의 tableViewCell을 채택한 별도의 파일이 필요하며 해당 파일 내에서 cell을 구현해야 한다</p>
<p>tableViewCell을 구현한 경우 위의 예시코드처럼 cell을 가져와 반환하도록 해야 한다</p>
<hr>
<h1 id="3-tableview-delegate-채택">3. tableView Delegate 채택</h1>
<p>Delegate 패턴을 이용해 tableView에서 사용할 특정 조건에 따른 동작의 추가를 하는 증 과정을 위해 필요하다</p>
<pre><code class="language-swift">tableView.delegate = self</code></pre>
<pre><code class="language-swift">extension ViewController: UITableViewDelegate {

}</code></pre>
<hr>
<h1 id="4-tableview-cell-생성-및-cell-등록">4. tableView cell 생성 및 cell 등록</h1>
<p>별도의 tableViewCell 파일을 생성하여 해당 파일 cell을 선언해야 한다
아래 코드의 경우 cell에 캘린더를 넣는 것을 목적으로 만들어진 코드다</p>
<pre><code class="language-swift">import UIKit

class CalendarViewCell: UITableViewCell {

    // 캘린더 선언
    let calendarVC = UICalendarView()

    // 해당코드는 tableViewCell 파일을 만들때 기본적으로 들어있지 않는 코드다
    // 만약 tableView를 스토리보드로 만든 경우가 아닌 코드로만 작성해 구현한 경우 필요로 하는 생성자다
    // cell을 표시할때 cell을 초기화하는 역할을 한다
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            setupDelegate()
            setupCalendar()
            setupAutoLayout()
        }

    // tableViewCell 파일을 만들때 기본적으로 제공 되는 코드
    required init?(coder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }

    // tableViewCell 파일을 만들때 기본적으로 제공되는 코드
    // 해당 코드는 스토리보드를 사용하는 경우에 이용하는 부분이다
    // 만약 코드를 이용해서만 구현을 하는 경우에는 수정할 필요가 없는 부분이다
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

    }

    //MARK: - 델리게이트 패턴
    func setupDelegate(){
        calendarVC.delegate = self
        calendarVC.selectionBehavior = UICalendarSelectionSingleDate(delegate: self)
    }

    //MARK: - 캘린더 설정
    func setupCalendar(){
        calendarVC.calendar = .current
        calendarVC.locale = .current
        calendarVC.fontDesign = .rounded
        calendarVC.layer.cornerRadius = 8

        self.contentView.addSubview(calendarVC)
    }

    //MARK: - 오토레이이아웃 설정
    func setupAutoLayout() {
        calendarVC.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            calendarVC.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 15),
            calendarVC.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -15),
            calendarVC.topAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.topAnchor, constant: 15),

        ])
    }
}

//MARK: - 캘린더 델리게이트 확장

extension CalendarViewCell: UICalendarViewDelegate {
    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -&gt; UICalendarView.Decoration? {
        return nil
    }

}
extension CalendarViewCell: UICalendarSelectionSingleDateDelegate {
    func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) {
        if let date = dateComponents?.day {
            print(date)
        }else {print(&quot;empty date&quot;)}

    }
}
</code></pre>
<p>cell이 완성된 경우 cell을 표시할 ViewController에서 아래 코드를 통해 cell 등록을 해야 한다</p>
<pre><code class="language-swift">tableView.register(CalendarViewCell.self, forCellReuseIdentifier: &quot;CalendarViewCell&quot;)</code></pre>
<hr>
<h2 id="전체-예시-코드">전체 예시 코드</h2>
<h3 id="viewcontroller">ViewController</h3>
<pre><code class="language-swift">import UIKit

class ViewController: UIViewController {

    let tableView = UITableView()

    override func viewDidLoad() {
        super.viewDidLoad()
        createTableView()
    }

    func createTableView() {

        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(CalendarViewCell.self, forCellReuseIdentifier: &quot;CalendarViewCell&quot;)

        tableView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(tableView)

        NSLayoutConstraint.activate([
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 0),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: 0),
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0),
        ])


    }

}
extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: &quot;CalendarViewCell&quot;, for: indexPath) as! CalendarViewCell
        return cell
    }
}
extension ViewController: UITableViewDelegate {

}
</code></pre>
<h3 id="calendarviewcell">CalendarViewCell</h3>
<pre><code class="language-swift">mport UIKit

class CalendarViewCell: UITableViewCell {

    // 캘린더 선언
    let calendarVC = UICalendarView()

    // 해당코드는 tableViewCell 파일을 만들때 기본적으로 들어있지 않는 코드다
    // 만약 tableView를 스토리보드로 만든 경우가 아닌 코드로만 작성해 구현한 경우 필요로 하는 생성자다
    // cell을 표시할때 cell을 초기화하는 역할을 한다
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
            super.init(style: style, reuseIdentifier: reuseIdentifier)
            setupDelegate()
            setupCalendar()
            setupAutoLayout()
        }

    // tableViewCell 파일을 만들때 기본적으로 제공 되는 코드
    required init?(coder: NSCoder) {
        fatalError(&quot;init(coder:) has not been implemented&quot;)
    }

    // tableViewCell 파일을 만들때 기본적으로 제공되는 코드
    // 해당 코드는 스토리보드를 사용하는 경우에 이용하는 부분이다
    // 만약 코드를 이용해서만 구현을 하는 경우에는 수정할 필요가 없는 부분이다
    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code

    }

    //MARK: - 델리게이트 패턴
    func setupDelegate(){
        calendarVC.delegate = self
        calendarVC.selectionBehavior = UICalendarSelectionSingleDate(delegate: self)
    }

    //MARK: - 캘린더 설정
    func setupCalendar(){
        calendarVC.calendar = .current
        calendarVC.locale = .current
        calendarVC.fontDesign = .rounded
        calendarVC.layer.cornerRadius = 8

        self.contentView.addSubview(calendarVC)
    }

    //MARK: - 오토레이이아웃 설정
    func setupAutoLayout() {
        calendarVC.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            calendarVC.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 15),
            calendarVC.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -15),
            calendarVC.topAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.topAnchor, constant: 15),

        ])
    }
}

//MARK: - 캘린더 델리게이트 확장

extension CalendarViewCell: UICalendarViewDelegate {
    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -&gt; UICalendarView.Decoration? {
        return nil
    }

}
extension CalendarViewCell: UICalendarSelectionSingleDateDelegate {
    func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) {
        if let date = dateComponents?.day {
            print(date)
        }else {print(&quot;empty date&quot;)}

    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[UICalendarView로 캘린더 만들기]]></title>
            <link>https://velog.io/@yyy_sang/UICalendarView%EB%A1%9C-%EC%BA%98%EB%A6%B0%EB%8D%94-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@yyy_sang/UICalendarView%EB%A1%9C-%EC%BA%98%EB%A6%B0%EB%8D%94-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 31 Jul 2023 16:22:46 GMT</pubDate>
            <description><![CDATA[<h1 id="uicalendarview">UICalendarView</h1>
<ul>
<li>캘린더를 표시하기 위한 뷰</li>
<li>ios 16이상의 버전에서만 사용이 가능하다</li>
</ul>
<hr>
<h1 id="uicalendarview-캘린더-표시하기">UICalendarView 캘린더 표시하기</h1>
<ul>
<li><p>캘린더를 표시하기 위한 기본적인 코드로 별도의 오토레이아웃을 잡아줘야하며, 화면의 크기, 기종에 따라 캘린더 크기의 제약이 발생한다</p>
</li>
<li><blockquote>
<p>heightAnchor를 원래 기본적으로 지원하는 캘린더 크기보다 작게 설정할 경우 캘린더가 잘리는 모습이 보인다</p>
</blockquote>
<pre><code class="language-swift">let calendarVC = UICalendarView()
calendarVC.calendar = .current //뷰 컨트롤러의 캘린더를 사용자의 지역의 현재 캘린더로 설정
calendarVC.locale = .current //캘린더 뷰 컨트롤러의 지역을 사용자의 지역의 현재 지역
calendarVC.fontDesign = . rounded //캘린더 뷰 컨트롤러의 글꼴 디자인을 둥근 것으로 설정</code></pre>
</li>
<li><p><strong>해당 코드만 입력했을 경우</strong>(오토레이아웃은 별도로 지정) 결과로는 아래 사진과 같이 캘린더가 만들어진다
<img src="https://velog.velcdn.com/images/yyy_sang/post/b1ec4796-f541-494c-aa1d-83826b5783db/image.png" alt=""></p>
</li>
<li><p>단, 위 코드의 결과로는 단순 캘린더의 표시만 가능할뿐 날짜 선택에 따른 이벤트는 발생하지 않는다.</p>
</li>
<li><p>날짜의 선택과 데이터 변화에 따른 캘린더를 꾸미길 원한다면 Delegate확장을 이용해서 기능을 추가해야한다</p>
</li>
</ul>
<hr>
<h1 id="uicalendarview-캘린더-날짜-선택하기">UICalendarView 캘린더 날짜 선택하기</h1>
<ul>
<li><p>날짜 선택시 이벤트를 발생시키기 위해서는 <strong>UICalendarSelectionSingleDateDelegate</strong>가 필요하다</p>
</li>
<li><p>공식 애플 개발자 문서 주소는 다음과 같다</p>
</li>
<li><p><em><a href="https://developer.apple.com/documentation/uikit/uicalendarview">https://developer.apple.com/documentation/uikit/uicalendarview</a>*</em></p>
</li>
<li><p>아래 코드를 삽입하면 날짜 선택에 대한 이벤트를 발생시키는 기능을 확장할 수 있다 </p>
<pre><code class="language-swift">// 캘린더 날짜 선택시 이벤트를 발생시키는 UICalendarSelectionSingleDate(delegate: self)
calendarVC.selectionBehavior = UICalendarSelectionSingleDate(delegate: self)</code></pre>
</li>
<li><p>확장을 완료하였을 경우 아래 코드를 입력해 delegate 확장을 해야한다</p>
</li>
<li><p>아래 예시는 날짜 선택시 나타나는 정보들 중 day에 해당되는 내용만 출력하도록 설정한 결과이다</p>
</li>
<li><p>기본적으로 dateComponets가 알려주는 정보는 아래와 같이 출력되며 optional type다</p>
</li>
</ul>
<blockquote>
<p>calendar: gregorian (current) era: 1 year: 2023 month: 8 day: 31 isLeapMonth: false </p>
</blockquote>
<pre><code class="language-swift">extension CalenderViewController: UICalendarSelectionSingleDateDelegate {
    func dateSelection(_ selection: UICalendarSelectionSingleDate, didSelectDate dateComponents: DateComponents?) {
        if let date = dateComponents?.day {
            print(date)
        }else {print(&quot;empty date&quot;)}

    }
}
</code></pre>
<ul>
<li>위 코드를 기반으로 날짜를 선택한 결과의 사진은 아래와 같다
<img src="https://velog.velcdn.com/images/yyy_sang/post/ee0f2ea2-8570-4430-b39a-c37553a0568d/image.png" alt=""></li>
</ul>
<hr>
<h1 id="uicalendarview-캘린더-꾸미기---추가예정">UICalendarView 캘린더 꾸미기 - 추가예정</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 2357번 - 최솟값과 최댓값]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2357%EB%B2%88-%EC%B5%9C%EC%86%9F%EA%B0%92%EA%B3%BC-%EC%B5%9C%EB%8C%93%EA%B0%92</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2357%EB%B2%88-%EC%B5%9C%EC%86%9F%EA%B0%92%EA%B3%BC-%EC%B5%9C%EB%8C%93%EA%B0%92</guid>
            <pubDate>Mon, 31 Jul 2023 13:39:33 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2357">https://www.acmicpc.net/problem/2357</a></p>
<h1 id="문제예시">문제예시</h1>
<h2 id="1-입력">1. 입력</h2>
<pre><code class="language-swift">10 4
75
30
100
38
50
51
52
20
81
5
1 10
3 5
6 9
8 10</code></pre>
<h2 id="2-출력">2. 출력</h2>
<pre><code class="language-swift">5 100
38 100
20 81
5 81</code></pre>
<hr>
<h1 id="풀이과정">풀이과정</h1>
<ul>
<li>세그먼트 트리를 이용해서 구간에 대한 최댓값과 최솟값을 구하고 출력하기로 함</li>
</ul>
<hr>
<h1 id="코드">코드</h1>
<ul>
<li>최댓값과 최솟값을 구하기 위한 세그먼트 트리를 각각 구현</li>
<li>각 섹션별로 구간에 대한 최댓값과 최솟값을 저장하도록 한다</li>
<li>입력된 값에 따라 해당 구간 사이에서 존재하는 최대값과 최솟값을 각각 구하여 출력한다</li>
</ul>
<pre><code class="language-swift">class MINSegmentTree {
    var value: Int
    var leftBound: Int
    var rightBound: Int
    var leftChild: MINSegmentTree?
    var rightChild: MINSegmentTree?

    init(array: [Int], leftBound: Int, rightBound: Int) {
        self.leftBound = leftBound
        self.rightBound = rightBound

        if leftBound == rightBound {
            value = array[leftBound]
        } else {
            let middle = (leftBound + rightBound) / 2
            leftChild = MINSegmentTree(array: array, leftBound: leftBound, rightBound: middle)
            rightChild = MINSegmentTree(array: array, leftBound: middle+1, rightBound: rightBound)
            value = min(leftChild!.value, rightChild!.value)
        }
    }

    func query(leftBound: Int, rightBound: Int) -&gt; Int {
        if self.leftBound == leftBound &amp;&amp; self.rightBound == rightBound {
            return self.value
        }

        guard let leftChild = leftChild else { return Int.max }
        guard let rightChild = rightChild else { return Int.max }

        if leftChild.rightBound &lt; leftBound {
            return rightChild.query(leftBound: leftBound, rightBound: rightBound)
        } else if rightChild.leftBound &gt; rightBound {
            return leftChild.query(leftBound: leftBound, rightBound: rightBound)
        } else {
            let leftQuery = leftChild.query(leftBound: leftBound, rightBound: leftChild.rightBound)
            let rightQuery = rightChild.query(leftBound: rightChild.leftBound, rightBound: rightBound)
            return min(leftQuery, rightQuery)
        }
    }
}

class MAXSegmentTree {
    var value: Int
    var leftBound: Int
    var rightBound: Int
    var leftChild: MAXSegmentTree?
    var rightChild: MAXSegmentTree?

    init(array: [Int], leftBound: Int, rightBound: Int) {
        self.leftBound = leftBound
        self.rightBound = rightBound

        if leftBound == rightBound {
            value = array[leftBound]
        } else {
            let middle = (leftBound + rightBound) / 2
            leftChild = MAXSegmentTree(array: array, leftBound: leftBound, rightBound: middle)
            rightChild = MAXSegmentTree(array: array, leftBound: middle+1, rightBound: rightBound)
            value = max(leftChild!.value, rightChild!.value)
        }
    }

    func query(leftBound: Int, rightBound: Int) -&gt; Int {
        if self.leftBound == leftBound &amp;&amp; self.rightBound == rightBound {
            return self.value
        }

        guard let leftChild = leftChild else { return Int.max }
        guard let rightChild = rightChild else { return Int.max }

        if leftChild.rightBound &lt; leftBound {
            return rightChild.query(leftBound: leftBound, rightBound: rightBound)
        } else if rightChild.leftBound &gt; rightBound {
            return leftChild.query(leftBound: leftBound, rightBound: rightBound)
        } else {
            let leftQuery = leftChild.query(leftBound: leftBound, rightBound: leftChild.rightBound)
            let rightQuery = rightChild.query(leftBound: rightChild.leftBound, rightBound: rightBound)
            return max(leftQuery, rightQuery)
        }
    }
}

let input = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}
var arr = [Int]()


for _ in 0..&lt;input[0] {
    arr.append(Int(readLine()!)!)
}

let minSegmentTree = MINSegmentTree(array: arr, leftBound: 0, rightBound: arr.count-1)
let maxSegmentTree = MAXSegmentTree(array: arr, leftBound: 0, rightBound: arr.count-1)

for _ in 0..&lt;input[1] {
    let order = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}

    print(minSegmentTree.query(leftBound: order[0] - 1, rightBound: order[1] - 1),maxSegmentTree.query(leftBound: order[0] - 1, rightBound: order[1] - 1))
}
</code></pre>
<hr>
<p><strong>트리 매우 어렵다...</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CocoaPods 설치하기]]></title>
            <link>https://velog.io/@yyy_sang/CocoaPods-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@yyy_sang/CocoaPods-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 29 Jul 2023 09:57:45 GMT</pubDate>
            <description><![CDATA[<h1 id="cocoapods">CocoaPods</h1>
<ul>
<li>Rubu로 작성된 Swift 및 Objective-C 코코아 프로젝트의 종속성 관리자</li>
<li>서드파티 라이브러리 관리 도구</li>
</ul>
<hr>
<h1 id="설치">설치</h1>
<h2 id="1-cocoapods-설치">1. CocoaPods 설치</h2>
<blockquote>
<p>$ sudo gem install cocoapods</p>
</blockquote>
<h2 id="2-pod-init">2. pod init</h2>
<ul>
<li>터미널에 아래 커멘드 입력</li>
</ul>
<blockquote>
<p>pod init</p>
</blockquote>
<h2 id="3-pod라이브러리-혹은-의존성-도구-설치">3. pod(라이브러리 혹은 의존성 도구) 설치</h2>
<ul>
<li>터미널에서 내가 원하는 프로젝트에 들어간 후 해당 위치에서 아래 커멘드를 입력</li>
</ul>
<blockquote>
<p>pod &#39;&lt;Pod 이름&gt;&#39; </p>
</blockquote>
<h3 id="예시-fscalendar를-예시로-함">예시) FSCalendar를 예시로 함</h3>
<pre><code>&gt;pod &#39;FSCalendar&#39;</code></pre><h2 id="4-podfile-수정">4. Podfile 수정</h2>
<ul>
<li>2번 과정을 진행하면 Podfile이 생성되는데 Podfile에 들어가 아래와 같이 입력</li>
</ul>
<h3 id="예시-fscalendar를-예시로-함-1">예시) FSCalendar를 예시로 함</h3>
<pre><code class="language-swift"># Uncomment the next line to define a global platform for your project
platform :ios, &#39;14.0&#39; &lt;- **ios버전(원본은 주석처리 되어있다)**

target &#39;&lt;프로젝트 이름&gt;&#39; do &lt;- **pod의 대상이 되는 프로젝트**
  # Comment the next line if you don&#39;t want to use dynamic frameworks
  use_frameworks!

  # Pods for &lt;프로젝트 이름&gt; &lt;- **프로젝트 이름**
  pod &#39;FSCalendar&#39;  &lt;- **해당위치**

end</code></pre>
<h2 id="5-pod-install">5. pod install</h2>
<ul>
<li>터미널에 아래 커멘드 입력</li>
</ul>
<blockquote>
<p>pod install</p>
</blockquote>
<h2 id="6-pod가-설치된-파일-실행프로젝트-실행">6. pod가 설치된 파일 실행(프로젝트 실행)</h2>
<ul>
<li>.xcworkspace 확장자를 가진 새로운 프로젝트 파일이 생성됨</li>
<li>해당 파일로 xcode를 실행하면 새로운 pod의 import가 가능하다</li>
</ul>
<blockquote>
<p>프로젝트명.xcworkspace </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[에러해결]: cocoapods 설치 후 오류]]></title>
            <link>https://velog.io/@yyy_sang/%EC%97%90%EB%9F%AC%ED%95%B4%EA%B2%B0-cocoapods-%EC%84%A4%EC%B9%98-%ED%9B%84-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@yyy_sang/%EC%97%90%EB%9F%AC%ED%95%B4%EA%B2%B0-cocoapods-%EC%84%A4%EC%B9%98-%ED%9B%84-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Sat, 29 Jul 2023 06:28:59 GMT</pubDate>
            <description><![CDATA[<h1 id="에러">에러</h1>
<ul>
<li><p>cocoapods 설치 후 밑의 에러메시지와 함께 <strong>build fail</strong> 문제가 발생한 경우를 작성한 경우이며 본인에게 나타난 과정에 대한 해결과정을 서술한 문서다</p>
</li>
<li><p>또한 해당 오류는 FSCalendar을 사용하는 과정에서 발생한 문제이므로 다른 pod에서 발생하는 문제에 대하여 해결이 불가능한 방법일 수도 있다</p>
</li>
</ul>
<blockquote>
<p>File not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a</p>
</blockquote>
<blockquote>
<p>Linker command failed with exit code 1 (use -v to see invocation)</p>
</blockquote>
<hr>
<h1 id="실패했던-시도">실패했던 시도</h1>
<ul>
<li>chatGPT에게 질문한 결과로는 xcode 업데이트 혹은 설치 과정에서 <strong>링커가 libarclite_iphonesimulator.a 파일을 찾지 못해서 발생한 것</strong>이라 알려주었음</li>
<li><blockquote>
<p>그에 따라 xcode를 재설치를 해보았으나 문제가 해결되지 않았음</p>
</blockquote>
</li>
</ul>
<hr>
<h1 id="해결">해결</h1>
<ul>
<li>xcode14는 지원하는 최하위 ios 버전이 <strong>11.0</strong>이다. </li>
<li>만약 pod를 설치한 후에 밑의 사진과 같이 <strong>Navigator Pane</strong>에서 <strong>pod</strong>에 들어간다</li>
<li>Minimum Development에서 설정된 <strong>ios 버전이 11.0 보다 하위의 버전이 아닌지</strong>, <strong>현재 내가 진행하는 프로젝트보다 높은 버전으로 설정되어 있는지 확인</strong>해야 한다
<img src="https://velog.velcdn.com/images/yyy_sang/post/7ec8af18-0029-466e-81eb-17fb013c75fe/image.png" alt=""></li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준: 2042 - 구간 합 구하기 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2042-%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2042-%EA%B5%AC%EA%B0%84-%ED%95%A9-%EA%B5%AC%ED%95%98%EA%B8%B0-swift</guid>
            <pubDate>Thu, 27 Jul 2023 14:44:28 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2042">https://www.acmicpc.net/problem/2042</a></p>
<h2 id="문제예시">문제예시</h2>
<h3 id="1-입력">1. 입력</h3>
<pre><code class="language-swift">5 2 2 // 입력숫자개수, 변경횟수, 출력횟수
1 //숫자입력
2 //숫자입력
3 //숫자입력
4 //숫자입력
5 //숫자입력
1 3 6 // 변경명령, 변경위치, 변경 후 숫자
2 2 5 // 출력명령, 구간 처음 위치, 구간 마지막 위치
1 5 2 // 변경명령, 변경위치, 변경 후 숫자
2 3 5 // 출력명령, 구간 처음 위치, 구간 마지막 위치 </code></pre>
<h3 id="2-출력">2. 출력</h3>
<pre><code class="language-swift">17
12</code></pre>
<h3 id="3-접근">3. 접근</h3>
<ul>
<li><strong>Fenwick Tree를 이용한 구간 합 구하기</strong></li>
</ul>
<ol>
<li>fenwick tree를 구현한다</li>
<li>본래 값을 저장할 배열을 생성</li>
<li>frewick tree의 값을 변경할때 (특정구간 변경후 값 - 특정구간 기존에 존재했던 값)을 특정위치 값이 포함된 모든 인덱스를 순회하며 값을 변경</li>
<li>특정구간에 대한 값을 구하기 위해(M ~ N까지) (N까지 합) - (M - 1 까지의 합)을 구하면 됨</li>
</ol>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">struct FenwickTree {
    var size: Int
    var tree: [Int]

    init(size: Int) {
        self.size = size
        self.tree = Array(repeating: 0, count: size + 1)
    }

    func lsb(_ x: Int) -&gt; Int {
        return x &amp; -x
    }

    mutating func update(index: Int, diff: Int) {
        var idx = index
        while idx &lt;= size {
            tree[idx] += diff
            idx += lsb(idx)
        }
    }

    func getSum(index: Int) -&gt; Int {
        var sum = 0
        var idx = index
        while idx &gt; 0 {
            sum += tree[idx]
            idx -= lsb(idx)
        }
        return sum
    }

    func rangeSum(from: Int, to: Int) -&gt; Int {
        return getSum(index: to) - getSum(index: from - 1)
    }
}

let input = readLine()!.split(separator: &quot; &quot;).map { Int($0)! }
var fenwickTree = FenwickTree(size: input[0])
var array = [Int](repeating: 0, count: input[0] + 1)

for i in 1...input[0] {
    let value = Int(readLine()!)!
    fenwickTree.update(index: i, diff: value)
    array[i] = value
}

for _ in 0..&lt;(input[1] + input[2]) {
    let orders = readLine()!.split(separator: &quot; &quot;).map { Int($0)! }
    if orders[0] == 1 {
        let idx = orders[1]
        let diff = orders[2] - array[idx]
        fenwickTree.update(index: idx, diff: diff)
        array[idx] = orders[2]
    } else {
        let result = fenwickTree.rangeSum(from: orders[1], to: orders[2])
        print(result)
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자료구조: 구간 합 - Prefix Sum]]></title>
            <link>https://velog.io/@yyy_sang/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B5%AC%EA%B0%84%ED%95%A9-Prefix-Sum</link>
            <guid>https://velog.io/@yyy_sang/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EA%B5%AC%EA%B0%84%ED%95%A9-Prefix-Sum</guid>
            <pubDate>Thu, 27 Jul 2023 06:01:34 GMT</pubDate>
            <description><![CDATA[<h1 id="구간-합">구간 합</h1>
<p>배열의 특정 구간의 합을 구하는 자료구조
배열 내 특정 위치의 값이 변하지 않는 경우를 전체로 사용가능한 자료구조다</p>
<hr>
<h1 id="분류">분류</h1>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">값을 변경하지 않는 경우</th>
<th align="center">값을 변경하는 경우</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1 차원</td>
<td align="center">Prefix Sum</td>
<td align="center">Binary Index Tree<br>(Fenwick Tree) <br> (segment tree)<br>(sqrt-decomposition)</td>
</tr>
<tr>
<td align="center">2 차원</td>
<td align="center">sumed-area table <br> (integral image)</td>
<td align="center">Binary Index Tree <br>(Fenwick Tree)<br>(segment tree)<br> (sqrt-decomposition)</td>
</tr>
<tr>
<td align="center">표 출처 : <a href="https://www.youtube.com/watch?v=_2DOKWvGets">https://www.youtube.com/watch?v=_2DOKWvGets</a></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<hr>
<h1 id="구조">구조</h1>
<h2 id=""><img src="https://velog.velcdn.com/images/yyy_sang/post/e1004ddd-3f8e-4bb0-a6e4-a1b82528851c/image.jpg" alt=""></h2>
<ul>
<li>배열의 0번째 인덱스는 0으로 한다</li>
<li>누적 합의 결과는 1번 인덱스부터 저장을 시작한다<h3 id="1-0번부터-n번째-위치까지의-합">1. 0번부터 N번째 위치까지의 합</h3>
</li>
<li>Array[N]<h3 id="2-m번째부터-n번째-위치까지의-합">2. M번째부터 N번째 위치까지의 합</h3>
</li>
<li>Array[N]-[M-1]</li>
</ul>
<hr>
<h1 id="코드">코드</h1>
<pre><code class="language-swift">let arr = [1, 2, 3, 4, 5]
// 배열 크기를 하나 더 크게 만들어 0번 인덱스에 0을 할당
var prefixSum = [Int](repeating: 0, count: arr.count + 1)

for i in 1...arr.count {
    prefixSum[i] = prefixSum[i-1] + arr[i-1]
}

print(prefixSum)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[백준 : 2776번 - 암기왕 swift]]></title>
            <link>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2776%EB%B2%88-%EC%95%94%EA%B8%B0%EC%99%95-swift</link>
            <guid>https://velog.io/@yyy_sang/%EB%B0%B1%EC%A4%80-2776%EB%B2%88-%EC%95%94%EA%B8%B0%EC%99%95-swift</guid>
            <pubDate>Wed, 26 Jul 2023 03:52:19 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2776">https://www.acmicpc.net/problem/2776</a></p>
<h1 id="문제예시">문제예시</h1>
<h2 id="1-입력">1. 입력</h2>
<pre><code class="language-swift">1
5
4 1 5 2 3
5
1 3 7 9 5</code></pre>
<h2 id="2-출력">2. 출력</h2>
<pre><code class="language-swift">1
1
0
0
1</code></pre>
<hr>
<h1 id="실패-코드-1---시간-초과완전탐색">실패 코드 1 -&gt; 시간 초과(완전탐색)</h1>
<pre><code class="language-swift">let input = Int(readLine()!)!
var result = [Int]()

for _ in 0..&lt;input {
    let numberOfNBook1 = Int(readLine()!)!
    let arrOfBook1 = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}
    let numberOfNBook2 = Int(readLine()!)!
    let arrOfBook2 = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}

    for j in arrOfBook2{
        if arrOfBook1.contains(j) {
            print(1)
        }else {
            print(0)
        }
    }
}
print(result)</code></pre>
<h1 id="실패코드-2---시간초과이진탐색">실패코드 2 -&gt; 시간초과(이진탐색)</h1>
<ul>
<li>이진탐색을 이용하였으나 100만이 넘는 정수배열을 정렬하는 과정에서 시간복잡도가 급격히 증가하여 시간초과가 난다고 추정<pre><code class="language-swift">let input = Int(readLine()!)!
</code></pre>
</li>
</ul>
<p>for _ in 0..&lt;input {
    var hash = <a href="">Int : [Int]</a></p>
<pre><code>let _ = Int(readLine()!)!
let arrOfBook1 = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}.sorted()
let _ = Int(readLine()!)!
let arrOfBook2 = readLine()!.split(separator: &quot; &quot;).map{Int($0)!}

for i in arrOfBook2 {
    if binaraySearch(arrOfBook1, i) != -1 {
        print(1)
    }else {
        print(0)
    }
}</code></pre><p>}</p>
<p>func binaraySearch(_ arr: [Int], _ value: Int ) -&gt; Int{
    var left = 0
    var right = arr.count - 1</p>
<pre><code>while left &lt;= right {
    let mid = left + (right - left)/2

    if value == arr[mid] {
        return mid
    }else if value &gt; arr[mid] {
        left = mid + 1
    }else {
        right = mid - 1
    }
}
return -1</code></pre><p>}</p>
<pre><code>* 이진탐색으로도 문제가 해결되지 않는 현상이 나타나 구글링을 해봄 
-&gt; 결과로 입출력 속도에 의해 시간초과가 나타나기도 한다고 함
-&gt; 빠른 입출력 코드를 이용해 문제를 해결하기로 결정

---
# 풀이과정
* 우선 라이노님의 빠른 입출력 클래스를 이용하기로 함
* 이진탐색을 이용하려면 탐색 대상 배열이 **정렬된 상태**로 되어있어야 하므로 시간복잡도가 늘어남. 따라서 해쉬를 이용하기로 함
1. 해쉬를 이용해 해당 값이 들어있는지 Bool 자료형을 이용해 확인
2. 수첩 2에 들어갈 정수를 입력해 해쉬테이블에 해당 값이 포함되어 있는지 확인
-&gt; 들어있으면 **1**을 출력
-&gt; 없으면 **0**을 출력

---
# 코드
* 해당 코드는 **라이노님**의 빠른 입출력 클래스를 이용한 풀이다
* 빠른 입출력 코드의 경우 xcode에서는 정상적으로 실행되지 않는 이슈가 있음을 기억해야 한다
```swift
import Foundation

final class FileIO {
    private let buffer: Data
    private var index: Int = 0

    init(fileHandle: FileHandle = FileHandle.standardInput) {
        self.buffer = try! fileHandle.readToEnd()! // 인덱스 범위 넘어가는 것 방지
    }

    @inline(__always) private func read() -&gt; UInt8 {
        defer {
            index += 1
        }
        guard index &lt; buffer.count else { return 0 }

        return buffer[index]
    }

    @inline(__always) func readInt() -&gt; Int {
        var sum = 0
        var now = read()
        var isPositive = true

        while now == 10
                || now == 32 { now = read() } // 공백과 줄바꿈 무시
        if now == 45 { isPositive.toggle(); now = read() } // 음수 처리
        while now &gt;= 48, now &lt;= 57 {
            sum = sum * 10 + Int(now-48)
            now = read()
        }

        return sum * (isPositive ? 1:-1)
    }


    @inline(__always) func readString() -&gt; String {
            var str = &quot;&quot;
            var now = read()

            while now == 10
                    || now == 32 { now = read() } // 공백과 줄바꿈 무시

            while now != 10
                    &amp;&amp; now != 32 &amp;&amp; now != 0 {
                str += String(bytes: [now], encoding: .ascii)!
                now = read()
            }

            return str
        }
  }


let fIO = FileIO()
let testCases = fIO.readInt()

for _ in 0..&lt;testCases {
    var hash = [Int: Bool]()
    let n = fIO.readInt()
    for _ in 0..&lt;n {
        let number = fIO.readInt()
        hash[number] = true
    }
    let m = fIO.readInt()
    for _ in 0..&lt;m {
        let a = fIO.readInt()
        if hash[a] == true {
            print(1)
        } else {
            print(0)
        }
    }
}</code></pre><hr>
<h1 id="다른-유저의-풀이과정">다른 유저의 풀이과정</h1>
<ul>
<li>Set를 이용한 탐색을 이용한 풀이과정이다</li>
<li>Set는 해쉬를 이용한 자료구조로 키(key)만 저장한 결과로 중복을 허용하지 않는다. 또한 배열과 다르게 특정 대상을 contains를 이용해 탐색하는데 시간복잡도가 O(1)이므로 매우 빠르다</li>
<li>반면에 배열은 탐색 contains를 이용해 탐색할 경우 최악의 경우 시간복잡도가 O(N)이다<pre><code class="language-swift">let n = Int(readLine()!)!
</code></pre>
</li>
</ul>
<p>for _ in 0..&lt;n {
    var answer = <a href="">String</a></p>
<pre><code>let _ = readLine()!
let arr = Set(readLine()!.split(separator: &quot; &quot;).map { Int($0)! })

let _ = readLine()!
readLine()!.split(separator: &quot; &quot;).forEach { char in
    arr.contains(Int(char)!) ? answer.append(&quot;1&quot;) : answer.append(&quot;0&quot;)
}

print(answer.joined(separator: &quot;\n&quot;))</code></pre><p>}</p>
<h2 id="">```</h2>
<p><strong>시간복잡도 무쳤다..!!</strong> 
<strong>어떻게 저렇게 생각할 수 있는지...(부럽다...)</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자료구조: 이진 탐색(Binary Search) swift]]></title>
            <link>https://velog.io/@yyy_sang/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89Binary-Search-swift</link>
            <guid>https://velog.io/@yyy_sang/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89Binary-Search-swift</guid>
            <pubDate>Tue, 25 Jul 2023 06:57:14 GMT</pubDate>
            <description><![CDATA[<h1 id="이진탐색">이진탐색</h1>
<hr>
<h2 id="시간복잡도">시간복잡도</h2>
<ul>
<li><strong>최선</strong> : <strong>O(1)</strong></li>
<li><strong>평균</strong> : <strong>O(N logN)</strong></li>
<li><strong>최악</strong> : <strong>O(N logN)</strong></li>
</ul>
<hr>
<h2 id="과정">과정</h2>
<ul>
<li>배열 -&gt; **[ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] **</li>
</ul>
<ol>
<li><p>배열 내에 찾고자 하는 값을 정한다 -&gt; 7을 찾는다라고 가정</p>
</li>
<li><p>탐색할 배열의 위치를 지정한다 </p>
</li>
</ol>
<p><strong>-&gt;</strong> <strong>left</strong>(왼쪽) : 0번 인덱스부터 탐색으로 지정
<strong>-&gt;</strong> <strong>right</strong>(오른쪽) : 마지막 인덱스까지 탐색으로 지정</p>
<ol start="3">
<li>오른쪽과 왼쪽 인덱스의 중간지점을 찾는다</li>
</ol>
<p><strong>-&gt;</strong> <strong>mid</strong>(가운데)</p>
<ol start="4">
<li>원하는 값이 <strong>mid</strong>(가운데)보다 상대적으로 앞에 있는지 뒤에 있는지 확인</li>
</ol>
<p><strong>-&gt; 앞에있는 경우</strong> -&gt; <strong>right</strong> = <strong>(mid - 1)</strong>
    <strong>-&gt; 뒤에있는 경우</strong> -&gt; <strong>left</strong> = <strong>(mid + 1)</strong>
    <strong>-&gt; 가운데에 있는 경우</strong> -&gt; <strong>가운데에 값이 있으므로 해당 인덱스를 반환</strong></p>
<ol start="5">
<li><strong>right</strong>이나 <strong>left</strong>이 변경된 경우에는 mid의 값도 다시 갱신해야함</li>
</ol>
<hr>
<h2 id="주의사항">주의사항</h2>
<ul>
<li>이진탐색(Binary Search)는 가운데를 기준으로 값의 크기를 비교한 후 탐색하는 방식이기 때문에 탐색하고자 하는 배열이 <strong>정렬된 상태</strong>여야 한다</li>
</ul>
<hr>
<h2 id="코드">코드</h2>
<pre><code class="language-swift">// 탐색의 결과로 해당 값이 위치한 인덱스를 표시한다

func binaraySearch(_ arr: [Int], _ value: Int ) -&gt; Int{
    // 탐색을 시작하는 첫 인덱스를 0으로 설정
    // 만약 탐색을 시작하는 인덱스가 1이상인 경우 원하는 숫자로 변경
    var left = 0
    var right = arr.count - 1

    while left &lt;= right {
        let mid = left + (right - left)/2

        if value == arr[mid] {
            return mid
        }else if value &gt; arr[mid] {
            left = mid + 1
        }else {
            right = mid - 1
        }
    }
    return -1
}
</code></pre>
]]></description>
        </item>
    </channel>
</rss>