<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>vas-y-somi | TIL</title>
        <link>https://velog.io/</link>
        <description>기록의 힘 </description>
        <lastBuildDate>Sun, 20 Mar 2022 05:02:24 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>vas-y-somi | TIL</title>
            <url>https://images.velog.io/images/vas-y-somi/profile/53423246-1521-49c3-9f8d-c96788ae6847/IMG_1643.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. vas-y-somi | TIL. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/vas-y-somi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[시간이 지나고 보이는 것들 | 스프린트 short.ly (부제: Sequelize)]]></title>
            <link>https://velog.io/@vas-y-somi/%EC%8B%9C%EA%B0%84%EC%9D%B4-%EC%A7%80%EB%82%98%EA%B3%A0-%EB%B3%B4%EC%9D%B4%EB%8A%94-%EA%B2%83%EB%93%A4-%EC%8A%A4%ED%94%84%EB%A6%B0%ED%8A%B8-short.ly-%EB%B6%80%EC%A0%9C-Sequelize</link>
            <guid>https://velog.io/@vas-y-somi/%EC%8B%9C%EA%B0%84%EC%9D%B4-%EC%A7%80%EB%82%98%EA%B3%A0-%EB%B3%B4%EC%9D%B4%EB%8A%94-%EA%B2%83%EB%93%A4-%EC%8A%A4%ED%94%84%EB%A6%B0%ED%8A%B8-short.ly-%EB%B6%80%EC%A0%9C-Sequelize</guid>
            <pubDate>Sun, 20 Mar 2022 05:02:24 GMT</pubDate>
            <description><![CDATA[<h3 id="sequelize">Sequelize</h3>
<blockquote>
<p>Sequelize는 Postgres, MySQL, MariaDB 등을 위한 promise기반의 ORM이다. 현재 V7까지 배포되었는데 V5를 한국어로 번역해 놓으신 분이 있어서 블로그 작성에 많이 참고하였다. </p>
</blockquote>
<ul>
<li><a href="https://pjt3591oo.github.io/sequelizejs_translate/build/html/index.html">pjt3591oo님의 깃허브</a> </li>
<li><a href="https://sequelize.org/v7/manual/getting-started.html">Sequelize 공식문서</a></li>
</ul>
<p><br><br></p>
<h4 id="데이터베이스에-연결">데이터베이스에 연결</h4>
<ul>
<li>데이터베이스를 생성하기 위해, Sequelize 인스턴스를 생성해야 합니다. 생성자에게 하나의 연결 URI를 전달하거나 <strong>연결 파라미터 변수를 개별적으로 전달</strong>하여 Sequelize 인스턴스 생성이 가능합니다.</li>
</ul>
<blockquote>
<p><img src="https://images.velog.io/images/vas-y-somi/post/b63e8cd2-83b3-4813-8638-c168b08ac36d/Screen%20Shot%202022-03-18%20at%2010.10.32%20PM.png" alt="Connecting to a database"></p>
</blockquote>
<ul>
<li>models/index.js 에도 공식문서의 &#39;database&#39;, &#39;username&#39;, &#39;password&#39;, { host, dialect }에 해당되는 값이 전달된 것을 볼 수 있다.<pre><code class="language-js">let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(
  config.database, // mysql&gt; create database [db 이름]으로 생성한 것
  config.username, // root
  config.password, // mysql 비번
  config // host와 dialect가 config.json 파일에 있음
);
}</code></pre>
<br><br></li>
</ul>
<h4 id="1-orm-설정">1. ORM 설정</h4>
<blockquote>
<p>cli를 통해 ORM을 잘 사용할 수 있도록 bootstraping(프로젝트 초기 단계를 자동으로 설정할 수 있도록 도와주는 일)을 해줘야 합니다.</p>
</blockquote>
<p>기본적으로 <code>npm i sequelize</code>로 Sequelize를 설치한 뒤 마이그레이션 파트를 봐야 한다. <strong>Migrations-Installing the CLI/Project bootstrapping</strong>를 보고 다음 명령어를 실행한다.</p>
<pre><code>npm install --save-dev sequelize-cli
npx sequelize-cli init</code></pre><p><code>config</code>, <code>models</code>, <code>migrations</code>, <code>seeders</code> 폴더들이 생성될 것이다. </p>
<pre><code class="language-js">// config.js
{
  &quot;development&quot;: {
    &quot;username&quot;: &quot;root&quot;,
    &quot;password&quot;: null,
    &quot;database&quot;: &quot;database_development&quot;,
    &quot;host&quot;:
    &quot;127.0.0.1&quot;,
    &quot;dialect&quot;: &quot;mysql&quot;
  },
 ...
}</code></pre>
<p>위의 파일에서 <code>password</code>를 MySQL 비밀번호로 변경하고 <code>shortlyMvc</code>라는 데이터베이스도 생성한다.</p>
<pre><code>mysql&gt; create database shortlyMvc;</code></pre><p><br><br></p>
<h4 id="2-모델-생성하기">2. 모델 생성하기</h4>
<p>(1-2) 모델 생성을 통과하기 위해서는 공식문서 <strong>Migrations-Creating the first Model (and Migration)</strong>를 참고한다. 박스 부분을 url 과 url:string,title:string,visits:integer 로 바꾸면 된다.</p>
<blockquote>
<p><img src="https://images.velog.io/images/vas-y-somi/post/6e41f9dd-6eaa-4f6c-a27b-dca8e8100c64/Screen%20Shot%202022-03-18%20at%2010.48.58%20PM.png" alt=""></p>
</blockquote>
<p>이 명령문은</p>
<ol>
<li><code>models</code>폴더에 url.js이라는 모델 파일을 생성하고 </li>
<li><code>migrations</code> 폴더에 <code>XXXXXXXXXXXXXX-create-url.js</code>라는 마이그레이션(스키마 변경에 따른 데이터 이주) 파일을 생성할 것이다.</li>
</ol>
<blockquote>
<p>1번 파일을 보자.</p>
</blockquote>
<pre><code class="language-js">&#39;use strict&#39;;
const {
  Model
} = require(&#39;sequelize&#39;);
module.exports = (sequelize, DataTypes) =&gt; {
  class url extends Model {
    /**
     * Helper method for defining associations.
     * This method is not a part of Sequelize lifecycle.
     * The `models/index` file will call this method automatically.
     */
    static associate(models) {
      // define association here
    }
  }
  url.init({
    url: DataTypes.STRING,
    title: DataTypes.STRING,
    visits: DataTypes.INTEGER // visits의 값으로 { type, defaultValue } 객체를 넣어 값에 DataTypes.INTEGER 와 0을 넣어야 한다.
  }, {
    sequelize,
    modelName: &#39;url&#39;,
  });
  // the defined model is the class itself
  console.log(url === sequelize.models.url); // true
  return url;
};</code></pre>
<p>Sequelize에서 모델을 정의하는 방법은 두 가지로 하나는 <code>sequelize.define(modelName, attributes, options)</code>이고 다른 하나는 <code>extends</code>를 써서 <code>init(attributes, options)</code>을 호출하는 것인데 위에 생성된 파일을 보면 두번째 방법으로 정의된 것을 볼 수 있다.</p>
<p><img src="https://images.velog.io/images/vas-y-somi/post/b47986fe-0666-47e4-bd14-4e8d8e6345a2/Screen%20Shot%202022-03-18%20at%2011.12.19%20PM.png" alt=""></p>
<p><strong>Note</strong>
그러나 이 단계까지는 데이터베이스에 아무것도 삽입하지 않았다. 실제로 데이터베이스에 해당 테이블을 <em>생성</em>하려면 다음과 같은 명령을 실행해야 한다.</p>
<pre><code>npx sequelize-cli db:migrate</code></pre><p>이제 urls라는 테이블에 기본적으로 생성되는 id, createdAt, updatedAt과 지정했던 3개의 열이 모두 생성될 것이다.</p>
<p><br><br></p>
<h4 id="3-controller-작성">3. Controller 작성</h4>
<pre><code>2) links controller 파일이 존재해야 합니다
3) links controller에는 get, post 메소드가 각각 존재해야 합니다</code></pre><p>controllers/links/index.js를 만들고 get, post, 그리고 처음보는 redirect 메서드를 작성해야 했다.</p>
<blockquote>
<p><strong>Pseudo code</strong>
0. controllers/links/index.js를 생성한다.</p>
</blockquote>
<ol>
<li>/modules/utils에 url 유효성 검사를 하고 url title만 따오는 함수들을 불러온다.</li>
<li>/models 에서 url 인스턴스를 불러온다.</li>
<li>module.exports = { get: (callback), post: (callback), redirect: (callback) } 의 형태로 틀을 짠다.</li>
<li>공식문서 <a href="https://sequelize.org/v7/manual/model-querying-finders.html"><strong>Model Querying - Finders</strong></a>의    <code>findAll</code>, <code>findByPk</code>,<code>findOne</code>, <code>findOrCreate</code>와 같은 다양한 메서드를 이용해 인스턴스 모델에서 특정 정보를 조회하고 request에 상응하는 response를 리턴한다.</li>
</ol>
<pre><code class="language-js">const { isValidUrl, getUrlTitle } = require(&quot;../../modules/utils&quot;);
const { url: URLModel } = require(&quot;../../models&quot;); 
// 모델을 url로 그냥 불러오면 테이블 안의 url과 헷갈리기 때문에 
// 구조분해할당에서 URLModel로 이름을 바꾼다.

module.exports = {
  // 7) GET /links는 urls 테이블의 목록을 JSON의 형태로 반환합니다
  // findAll 메서드를 통해 SELECT 쿼리를 날리고 데이터 전송이 
  // 완료된 후에 res에 실어보내줘야 하므로 async/await을 잊지 말자.
  get: async (req, res) =&gt; {
    const data = await URLModel.findAll();
    res.status(200).json(data);
  },
  // 6) POST /links은 url을 받아 단축 url로 만듭니다
  post: (req, res) =&gt; {
    const { url } = req.body;
    // url 유효성 검사해서 유효하지 않은 경우 에러코드 전달
    if (!isValidUrl(url)) {
      return res.sendStatus(400);
    }

    // title만 추려내는 코드에는 create 보다는 findOrCreate을 
    // 써서 이미 있는 인스턴스가 테이블에 중복 생성되지 않도록 한다.
    getUrlTitle(url, async (err, title) =&gt; {
      if (err) {
        console.log(err);
        return res.sendStatus(400);
      }

      await URLModel.findOrCreate({
          where: { url: url },
          defaults: { title: title } 
        // defaults는 where로 찾은 것이 아무것도 없을 때 
        // 꼭 생성해야 하는 인스턴스를 정해주는 것
        })
        .then(([result, created]) =&gt; {
        // findOrCreate은 찾았거나 생성한 객체와 boolean을 담은
        // 배열을 리턴한다. 새로 생성된 것이 있으면 true, 없으면 false
          if (!created) { // 생성한 것이 없을때 ---&gt; Found
            return res.status(201).json(result); 
          }
          res.status(201).json(result); // Created
        })
        .catch(error =&gt; {
          console.log(error);
          res.sendStatus(500); // Server error
        });
    });
  },
  // 8) GET /links/:id 을 요청하면 url 필드값으로 리디렉션합니다
  // 9) GET /links/:id 을 요청하면, 해당 레코드의 visit count가 1 증가해야 합니다
  redirect: async (req, res) =&gt; {
    await URLModel
      .findByPk(req.params.id)
      .then(result =&gt; {
        if (result) {
          return result.update({
            visits: result.visits + 1
          }); // result.increment(&quot;visits&quot;, { by: 1 })도 가능
        } else {
          res.sendStatus(204);
        }
      })
      .then(result =&gt; {
        res.redirect(result.url);
      // The res.redirect() function lets you redirect the user 
      //to a different URL by sending an HTTP response with status 302.
      // app.get(&#39;/from&#39;, (req, res) =&gt; {
      //   res.redirect(&#39;/to&#39;);
      // });
      })
      .catch(error =&gt; {
        console.log(error);
        res.sendStatus(500);
      });
  }
}</code></pre>
<p><br><br></p>
<h4 id="4-router-연결">4. Router 연결</h4>
<pre><code class="language-js">const express = require(&quot;express&quot;);
const router = express.Router();
const linksController = require(&quot;../controllers/links&quot;);

router.get(&quot;/&quot;, linksController.get);
router.post(&quot;/&quot;, linksController.post);
router.get(&quot;/:id&quot;, linksController.redirect);

module.exports = router;
</code></pre>
<ul>
<li>res.redirect() 메서드는 302 상태메세지로 HTTP 응답을 보내고 유저가 다른 URL로 접속되도록 한다.<pre><code class="language-js">app.get(&#39;/from&#39;, (req, res) =&gt; {
res.redirect(&#39;/to&#39;); // /from 으로 접속하면 /to 로 보냄
});
app.get(&#39;/to&#39;, (req, res) =&gt; res.send(&#39;Hello, World!&#39;));</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[MySQL 설치 중 만난 ERROR 1045 (28000)]]></title>
            <link>https://velog.io/@vas-y-somi/MySQL-ERROR-1045-28000</link>
            <guid>https://velog.io/@vas-y-somi/MySQL-ERROR-1045-28000</guid>
            <pubDate>Sat, 05 Mar 2022 07:24:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>문제의 발단</p>
</blockquote>
<ol>
<li>MySQL 설치를 완료하고 <code>mysql -u root</code> <code>Enter</code>를 통해 비밀번호가 없는 <code>root</code> 계정을 생성했다. 만약 <code>mysql -u root -p</code>를 입력하면 계정 생성과 동시에 임의의 패스워드를 입력하고 나중에 변경 가능하다.<pre><code>brew install mysql // 설치
brew services start mysql // 서비스 시작
mysql -u root // 비밀번호 없이 로그인
</code></pre></li>
</ol>
<pre><code>&lt;br&gt;

 2. `&#39;root-password&#39;` 자리에 패스워드를 지정했는데 `mysql&gt;`을 한번 `exit`한 이후로 다시 sql 서버에 들어가려고 할 때마다 `root`에 접근이 거부되는 헬이 시작되었다.
```js
ALTER USER &#39;root&#39;@&#39;localhost&#39; IDENTIFIED BY &#39;root-password&#39;; // root-password 대신에 각자의 비밀번호 입력</code></pre><p><img src="https://images.velog.io/images/vas-y-somi/post/f616aa8b-c32a-4baa-8946-2a655ffbff32/Screen%20Shot%202022-03-04%20at%203.38.29%20AM.png" alt="Error 1045"></p>
<ol start="3">
<li><code>ERROR 1045 (28000): Access denied for user &#39;root&#39;@&#39;localhost&#39; (using password: NO)</code> 의 발생 이유는 지금 와서 보니 알 것 같다. 지정한 비밀번호가 존재하면 무조건 <code>mysql -u root -p</code>로 접속해야 하는데 <code>-p</code>를 포함하지 않은 것이다.</li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/7ea7c229-c72a-4bea-8cb5-25b5706af4d3/Screen%20Shot%202022-03-04%20at%205.08.22%20PM.png" alt=""></p>
<ol start="4">
<li>그래서 <code>-p</code>를 넣었는데 <code>Enter password:</code>에서 비밀번호가 잘 지정된 건지 확신이 서지 않아 그냥 엔터를 눌러보았다. 그런데 에러가 떠서 이번엔 지정한 비밀번호를 눌렀는데도 <code>using password:</code> 부분에 <code>No</code> <code>YES</code>만 다른 ERROR 1045 
(28000)가 또 나타났다.</li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/912db3fd-0864-4ebe-ae98-b307467a95fa/Screen%20Shot%202022-03-04%20at%2010.01.00%20PM.png" alt=""></p>
<ol start="5">
<li>에러메세지를 구글링해서 이런저런 냅다 복붙해 가면서 문제를 해결하려고 했는데 그 과정에서 1045 다음으로 많이 만난 사람 미치게 하는 에러메세지는 <code>ERROR 2002 (HY000): Can&#39;t connect to local MySQL server through socket &#39;/tmp/mysql.sock&#39; (2)</code> 였다. </li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/04366c25-95f2-450d-bb02-1e596c54464f/Screen%20Shot%202022-03-04%20at%2010.01.17%20PM.png" alt=""></p>
<ol start="6">
<li>검색해보니 이 경우는 <code>mysql</code>이 설치는 되었는데 작동이 되고 있지 않을 때 나타나는 메세지라고 한다. <code>brew services start mysql</code>나 <code>mysql.server start</code>로 서버를 열어야 한다.</li>
</ol>
<blockquote>
<p>문제 해결</p>
</blockquote>
<ol>
<li><p>결국 문제는 <code>root</code>에 접속할때 비번 없이 엔터를 치든, 맥북 로그인 비번을 치든, 내가 지정한 특수문자가 포함된 엄청 복잡한 비번을 치든 무조건 접근이 거부되는 상황이었다. (아무래도 오타가 있었던 것 같다.) 나는 분명히 비번을 지정하긴 했기 때문에 에러메세지로 구글링 하던 것을 mysql root password lost 쪽으로 노선변경했다.</p>
</li>
<li><p><code>--skip-grant-tables</code>의 발견: 이 옵션을 입력하면 계정 관리에 관련된 SQL 명령들을 실행하지 않는다. 그래서 보통 <code>root</code>의 사용자 비번을 잃어버렸을 때 비번 없이 데이터베이스 서버에 접속하기 위해 사용된다. 그래서 <code>mysql.server start --skip-grant-tables</code>를 입력해 서버를 열었다.</p>
</li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/33a80e2f-f753-491b-b414-7af9abe0fa31/Screen%20Shot%202022-03-04%20at%2010.38.27%20PM.png" alt=""></p>
<ol start="3">
<li><p>그리고 비번없이 계정에 접근하는 <code>mysql -u root</code>를 입력했더니 드디어!!! Welcome to the MySQL monitor라는 문구를 만나게 된다.
<img src="https://images.velog.io/images/vas-y-somi/post/03735507-b936-4b19-8ad0-1f83d459bf92/Screen%20Shot%202022-03-04%20at%2010.39.52%20PM.png" alt=""></p>
</li>
<li><p>그리고 이번에는 엄청 쉬운 비번을 지정하려고 <code>ALTER USER</code> 커맨드를 쳤더니 또 다시 나를 어택하는 <code>ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement</code> ㅠㅠ.. 읽어 보니 현재도 <code>--skip-grant-tables</code>가 걸려있어서 커맨드를 실행할 수 없다는 것이었다.</p>
</li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/d39817aa-f6a1-42d2-a4e0-394a654f547a/Screen%20Shot%202022-03-04%20at%2010.57.49%20PM.png" alt=""></p>
<ol start="5">
<li><p>이 옵션을 해제하는 방법은 <code>FLUSH PRIVILEGES</code>이다. 그리고 나서 다시 비번 변경하는 커맨드 입력했더니 Query OK! <code>exit</code>으로 나가고 재접속을 시도해 봤다.
<img src="https://images.velog.io/images/vas-y-somi/post/2863df43-a906-4969-8222-bc707b607a81/Screen%20Shot%202022-03-04%20at%2011.08.51%20PM.png" alt=""></p>
</li>
<li><p>다시 말하지만 비번이 있다면 <code>mysql -u root</code> 다음에 <code>-p</code>를 잊지 말자. 그럼 <code>ERROR 1045 (28000)</code> 또 뜬다! 이제 비번 입력하면 무사히 MySQL에 잘 접속되고 실행되는 걸 볼 수 있다.</p>
</li>
</ol>
<p><img src="https://images.velog.io/images/vas-y-somi/post/b7fb3e50-8c7b-41ac-b7db-e8f7b0057162/Screen%20Shot%202022-03-04%20at%2011.00.32%20PM.png" alt=""></p>
<blockquote>
<p>마치며...</p>
</blockquote>
<p>처음에는 문제가 어디서부터 시작되는지 감이 안 왔다. 그래서 에러메세지만 구글링해서 답변들이 다 영어니 피곤해서 제대로 읽지도 않고 우선 복붙해서 다 쳐봤다. uninstall/install도 세 번은 한 것 같다. 왜 그렇게 해결하는지를 이해하려고 한 것이 아니라 운좋게 하나만 걸려라 이런 마인드였다. 근데 그렇게 2시간을 고생하고도 안 풀렸을 때, 다시 제일 처음으로 되돌아가 문제가 뭐였지? 되짚어봤다. 패스워드를 안 입력해도, 틀릴 리 없는 맥북 비번을 입력해도, 긴 비번을 입력해도 답이 없을 때는 나의 비번 설정 오류를 의심했어야 했다. 그래서 검색의 방향을 바꾸니 문제가 풀렸다. 앞으로는 무작정 에러메세지만 검색하지 말고 그 전후 상황을 이해하고 검색어를 구체적으로 설정하고 그 설명들도 주의깊게 읽어보면서 가져다 써야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 정복 | 시간 복잡도]]></title>
            <link>https://velog.io/@vas-y-somi/Algorithm-Time-Complexity</link>
            <guid>https://velog.io/@vas-y-somi/Algorithm-Time-Complexity</guid>
            <pubDate>Wed, 02 Mar 2022 14:23:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>시간 복잡도란? 
특정한 크기의 입력 n에 대하여 알고리즘을 위해 필요한 연산의 횟수를 의미하며 빅오(Big-O) 표기법을 사용해 나타냄</p>
</blockquote>
<p>표에서 아래로 내려갈수록 시간 복잡도가 높다.</p>
<table>
<thead>
<tr>
<th>빅오 표기법</th>
<th>명칭</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>O(1)</td>
<td>상수 시간(Constant time)</td>
<td>문제를 해결하는데 오직 한 단계 처리</td>
</tr>
<tr>
<td>O(logN)</td>
<td>로그 시간(Logarithmic time)</td>
<td>문제를 해결하는데 필요한 단계들이 연산마다 특정 요인에 의해 줄어듬</td>
</tr>
<tr>
<td>O(N)</td>
<td>선형 시간(Linear time)</td>
<td>문제를 해결하기 위한 입력 N 만큼 단계가 필요</td>
</tr>
<tr>
<td>O(NlogN)</td>
<td>로그 선형 시간(Linearithmic time)</td>
<td>문제를 해결하기 위한 단계의 수가 N번에 그 하나의 N번당 단계들이 연산마다 특정 요인에 의해 줄어듬</td>
</tr>
<tr>
<td>O(N^2)</td>
<td>이차 시간(Quadric time)</td>
<td>문제를 해결하기 위한 단계의 수는 입력값 N의 제곱</td>
</tr>
<tr>
<td>O(2^N)</td>
<td>지수 시간</td>
<td>문제를 해결하기 위한 단계의 수는 주어진 상수값 2의 N제곱</td>
</tr>
</tbody></table>
<h3 id="예시">예시</h3>
<ol>
<li>O(1)<pre><code class="language-js">// 예1
// n의 숫자가 아무리 커져도 한 번만 대입해 계산하면 끝 
// O(10), O(100) 이런 거 없다. O(1*10), O(1*100) 이고 상수 10과 100은 제거
function sumUp(n) {
return (n / 2) * (n + 1); 
}</code></pre>
<pre><code class="language-js">// 예2
// i&lt;n이 아니고 i&lt;5라서 n이 영향을 미치지 않음
let i = 0;
do {
i = i + 1;
} while (i &lt; 5);</code></pre>
</li>
<li>O(logN): Binary Search<pre><code class="language-js">const binarySearch = function (arr, target) {
let left = 0,
 right = arr.length - 1;
while (left &lt;= right) {
 let middle = parseInt((right + left) / 2);
 if (arr[middle] === target) {
   return middle;
 }
 if (target &lt; arr[middle]) {
   right = middle - 1;
 } else {
   left = middle + 1;
 }
}
return -1;
};</code></pre>
</li>
<li>O(N) <pre><code class="language-js">// 예1
// i&lt;n인 반복문
let i = 0;
do {
i = i + 1; // f(n) = n, O(f(n)) = O(n)
} while (i &lt; n);</code></pre>
<pre><code class="language-js">// 예2
let i = 0;
do {
i = i + 3; // f(n) = 3n, i에 3씩 더하니까 위의 알고리즘보다 3배 일찍 끝나지만 n 앞의 상수는 별 의미 없으므로 똑같이 O(f(n)) = O(n)
} while (i &lt; n);</code></pre>
</li>
<li>O(NlogN): Merge Sort
<img src="https://miro.medium.com/max/1400/1*61Mf0zjVfd1s3_SzUNGxPA.png" alt="Merge Sort"></li>
<li>O(N^2): loop in loop<pre><code class="language-js">// 예1
for(let i = 0; i &lt; n; i++)
 for(let j = 0; j &lt; n; j++)
// f(n) = n*n, O(f(n)) = O(n^2) </code></pre>
<pre><code class="language-js">// 예2
for(let i = 0; i &lt; n; i++)
 for(let j = i; j &lt; n; j++) // 0이 i로 바뀜
   // i=0이면 n work, i=1이면 n-1 work
   // (n) + (n-1) + n(n-2) _ ... + 2 + 1 = n(n+1)/2 = 1/2(n^2+n)
   // O(1/2(n^2+n)) 상수 1/2 날리고 별 의미없는 +n 날리면 O(n^2)</code></pre>
</li>
<li>O(2^N): Recursive Fibonacci<pre><code class="language-js">function fibonacci(num) {
 if(num &lt; 2) {
     return num;
 }
 else {
     return fibonacci(num-1) + fibonacci(num - 2);
 }
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[알고리즘 정복 | Spiral Matrix in JS]]></title>
            <link>https://velog.io/@vas-y-somi/Algorithm-Spiral-Matrix</link>
            <guid>https://velog.io/@vas-y-somi/Algorithm-Spiral-Matrix</guid>
            <pubDate>Mon, 28 Feb 2022 05:51:47 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">문제</h3>
<p>2차원 M x N 배열을 나선형(spiral)으로 순회해야 합니다.</p>
<h3 id="입력">입력</h3>
<p>인자 1 : matrix
세로 길이(matrix.length)가 M, 가로 길이(matrix[i].length)가 N인 2차원 배열
matrix[i]는 string 타입을 요소로 갖는 배열
matrix[i][j].length는 1</p>
<h3 id="출력">출력</h3>
<p>string 타입을 리턴해야 합니다.</p>
<h3 id="주의사항">주의사항</h3>
<p>순회는 좌측 상단 (0,0)에서 시작합니다.
배열의 모든 요소를 순서대로 이어붙인 문자열을 리턴해야 합니다.</p>
<h3 id="입출력-예시">입출력 예시</h3>
<pre><code class="language-js">matrix = [
  [&#39;T&#39;, &#39;y&#39;, &#39;r&#39;, &#39;i&#39;],
  [&#39;i&#39;, &#39;s&#39;, &#39;t&#39;, &#39;o&#39;],
  [&#39;n&#39;, &#39;r&#39;, &#39;e&#39;, &#39;n&#39;],
  [&#39;n&#39;, &#39;a&#39;, &#39;L&#39;, &#39; &#39;],
];
output = spiralTraversal(matrix);
console.log(output); // --&gt; &#39;Tyrion Lannister&#39;
</code></pre>
<h3 id="나의-풀이">나의 풀이</h3>
<pre><code class="language-js">const spiralTraversal = function (matrix) {
  // 시작점은 (top, left) = (0, 0) 반대쪽 끝은 (botom, right) = (arr[0].length -1, arr.length -1) = (3, 3) 을 정한다
  // direction(right, down, left, up)을 정한다
  // matrix[top][left] 에선 to right
  // matrix[top][right] 에선 to down
  // matrix[bottom][right]에선 to left, 
  // matrix[bottom][left]에선 to up

  let result = []
  let top = 0
  let left = 0
  let right = matrix[0].length - 1 // 열의 개수
  let bottom = matrix.length -1 // 행의 개수
  let direction = &#39;right&#39;
  while(left&lt;=right &amp;&amp; top&lt;=bottom) {
    if(direction === &#39;right&#39;){
      for(let i = left; i&lt;=right; i++) {
        result.push(matrix[top][i])
      }
      top += 1 // 기준 top점이 내려가려면 반대로 인덱스 번호를 1 더한다.
      direction = &#39;down&#39;
    }
    else if(direction === &#39;down&#39;){
      for(let i = top; i&lt;=bottom; i++) {
        result.push(matrix[i][right])
      }
      right -= 1
      direction = &#39;left&#39;
    }
    else if(direction === &#39;left&#39;){
      for(let i = right; i&gt;=left; i--) {
        result.push(matrix[bottom][i])
      }
      bottom -= 1 // 기준 bottom이 올라가려면 인덱스 번호를 1 내린다.
      direction = &#39;up&#39;
    }
    else if(direction === &#39;up&#39;){
      for(let i = bottom; i&gt;=top; i--) {
        result.push(matrix[i][left]) 
      }
      left += 1
      direction = &#39;right&#39;
    }
  }

  return result.join(&#39;&#39;);
};</code></pre>
<h3 id="레퍼런스">레퍼런스</h3>
<pre><code class="language-js">const spiralTraversal = function (matrix) {
  // 각 방향마다 [row, column]의 변화를 저장
  const RIGHT = [0, 1];
  const DOWN = [1, 0];
  const LEFT = [0, -1];
  const UP = [-1, 0];
  // 각 방향을 &#39;MOVES&#39;라는 배열에 집어넣고 
  // MOVES[index]에서 index를 direction으로 칭한다. 
  // 반복문에서 direction의 값이 증가하면 
  // 차례로 RIGHT&gt;DOWN&gt;LEFT&gt;UP의 요소에 접근한다.
  const MOVES = [RIGHT, DOWN, LEFT, UP]; //2차 행렬
  const M = matrix.length; // 행
  const N = matrix[0].length; // 열
  // 행렬의 범위를 벗어나지 않는 조건
  const isValid = (row, col) =&gt; row &gt;= 0 &amp;&amp; row &lt; M &amp;&amp; col &gt;= 0 &amp;&amp; col &lt; N;

  let count = 0;
  let row = 0,
    col = -1; // col은 왜 범위 밖에서 시작하나 싶었는데 뒤에 cd(1)가 더해져서 0이 됨
  let direction = 0;
  let result = &#39;&#39;;
  while (count &lt; M * N) { // M*N은 엘리먼트의 총 개수가 되고 나중에 count와 비교하여 함수를 종료할 수 있음
    const move = MOVES[direction]; // MOVES[0] = RIGHT = [0, 1]
    const [rd, cd] = move; // 구조분해할당 rd(row direction) =0, cd(col direction)=1

    row = row + rd; // 0
    col = col + cd; // 0
    while (isValid(row, col) &amp;&amp; matrix[row][col] !== false) {
      result = result + matrix[row][col]; // result = &quot;T&quot;
      // 한 요소에 두 번 접근하지 않게 하기 위해서, 접근된 요소를 false로 변경한다.
      matrix[row][col] = false;
      row = row + rd; // 0+0 행은 그대로
      col = col + cd; // 0+1 열은 1칸 이동
      count++;
    }
    // row, col 이 행렬의 범위를 벗어났기 때문에,
    // 진행된 방향의 반대로 한 칸 이동한다.
    row = row - rd;
    col = col - cd;

    // 각 방향이 순환되기 때문에 방향 개수를 나머지로 하는 모듈러 연산을 사용한다.
    direction = (direction + 1) % 4;
  }
  return result;
};</code></pre>
<blockquote>
<h3 id="소감">소감</h3>
<p>레퍼런스는 항상 놀랍다. [row, col] 배열에 0, 1, -1을 넣어 방향을 직관적으로 표현한 것이 좋았다. 한 요소에 두번 접근하지 않기 위해 접근된 요소를 false로 바꿔놓는 것도 DFS에서 visited 에 [false, ..., false]를 할당하고 접근했을 때마다 true로 바꿔놓았던 방법과 일맥상통하는 것 같다. 방향 개수로 나머지 연산을 해서 방향이 모든 방향으로 다 돌았을 때 다시 0으로 초기화 하는 것도 기억했다가 꼭 다른데서 써야겠다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>