アニメーションで理解する探索アルゴリズム

# はじめに

### 探索アルゴリズムとは
この記事を読んでくださっている方の中には、そもそもアルゴリズムってなんだろう？と思っている方もいると思います。
アルゴリズムとは、問題を解決するための手順・計算方法のことです。
例えば、大学受験を考えてみましょう。
大学に合格するためには勉強をしなければいけません。大学に受かるための勉強のやり方（アルゴリズム）はたくさんあります。そのアルゴリズムを教えてくれるのが学校や塾といった場所になります。

その中でも探索アルゴリズムは基本となるアルゴリズムのひとつで、データ群の中から条件に一致するデータを見つけるアルゴリズムのことを指します。
### 探索アルゴリズムを学ぶ意義
・処理の高速化（効率化）
データの特性に合ったアルゴリズムを実装することによって、処理速度が速くなります。

・読解しやすいコードを書ける
定式化された読解しやすいコードを書くことによって、保守性を向上させることができます。

# リストの探索
### 線形探索
線形探索は先頭から順に、条件と照らし合わせながら調べていくアルゴリズムです。探索アルゴリズムの中で最も基本的なアルゴリズムだと言われています。

#### イメージ
[1,12,34,3,17,9,22]というデータ群から17を探し出す場合で説明します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/54babc48-3243-e5cd-0d29-7a6eed32bfa4.gif">

#### 解説
1\. データの先頭の要素「1」が探している「17」と同じかどうか調べる。違うので次の要素へ
![qiita1.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/0ea3dc73-8e5e-9600-df92-2bd4fb025b2f.png)

2\. 次の要素「12」が探している「17」と同じかどうか調べる。違うので次の要素へ
![qiita1.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/b4463a5a-c336-580d-3916-5f0f39c90216.png)

3\. 次の要素「34」が探している「17」と同じかどうか調べる。違うので次の要素へ
![qiita1.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/049d55f4-df58-cd9a-010a-6f836f702461.png)

4\. 次の要素「3」が探している「17」と同じかどうか調べる。違うので次の要素へ
![qiita1.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/fa361f20-c0aa-22db-b2f1-9ff004ad72f1.png)

5\. 次の要素「17」が探している「17」と同じかどうか調べる。目的のデータと合致したので探索終了。
（目的のデータが見つからない場合はリストの最後まで探索を行う。）
![qiita1.77.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/3f4ce5da-8e62-175d-d455-398f830c2d8d.png)


目的のデータある場所によって処理時間が変わってしまうため、目的のデータが最後にある場合には処理に時間がかかってしまいます。
計算量はO(N)です。

:::note info
**計算量**
計算量(時間計算量)とは計算にかかる処理時間を表しています。アルゴリズムの性能を評価する指標の一つです。
計算量を記述するときにはオーダー記法（O記法）を使用します。
例えば、O(N)の場合は、処理時間がデータの数（N）に比例することを表します。
:::

### 二分探索
整列されたデータ群を二つに分け、どちらに目的のデータがあるかを繰り返し判断していくことで探索を行うアルゴリズムです。

#### イメージ
[0,1,3,9,12,17,22,28,34,46]というデータ群から22を探し出す場合で説明します。
![qiita2.5アニメ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/5fb6ad6e-3d72-34df-0d1d-bca08830e2e7.gif)

#### 解説
（もしも、リスト内の要素がソートされていない場合はソートする）
1\. まず、真ん中の要素（今回は真ん中がないため1つ左の要素）「12」と探している「22」を比較する。
（データを二つに分けて、真ん中がなかった場合は、1つ左の要素 or 1つ右の要素と目的のデータを比べる。）
![qiita2.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/231acbd4-2fae-bde0-a726-8a05d4f88157.png)

2\. 「22」よりも「12」の方が小さかったため「12」から左側には「22」が存在しないことがわかる。
![qiita2.3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/b89b8db6-877a-42af-317e-619abd4fc7fc.png)

3\. 次に、右側の5つのデータの真ん中の要素「28」と探している「22」を比較する。
![qiita2.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/2478b078-14f7-134e-1fd0-283847448afb.png)

4\. 「22」よりも「28」の方が大きかったため、「28」から右側には「22」が存在しないことがわかる。
![qiita2.5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/25a5202d-2ee5-193a-abb3-e5b543ef25fe.png)

5\. 次に、残ったデータの真ん中（真ん中がないため1つ左の要素）「17」と探している「22」を比較する。目的のデータと違うの次へ
![qiita2.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/4c88736e-44f6-d807-3224-a60586bfd9fd.png)

6\. 残った要素「22」と探している「22」を比較する。目的のデータと合致したので探索終了。
![qiita2.7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/26a89dc5-30d4-9b1a-fd8f-75bf5fe927ae.png)


線形探索よりも処理時間は短くなりますが、データ群が整列されている必要があるため、整列されていないデータ群で使用したい場合はソートアルゴリズムを使用してから行う必要があります。
計算量はO(logN)です。

# 木構造、グラフの探索
木構造やグラフといった階層のあるデータ構造では、リストでの探索とは違う方法を使います。
本記事では木構造を例として説明を行います。

:::note info
木構造の要素部分をノード（節）、要素と要素のつながりをエッジ（枝）と呼びます。また、木構造の最初のノードをルートノード（根）、末端のノードはリーフノード（葉）と呼びます。
※この記事の以下では、ノードとエッジという呼称に統一します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/876e0ac4-a99a-e88d-613f-7087ea9b30c2.png">
:::

#### 身近な木構造データ

Webスクレイピングを行う場合を考えてみます。
Webスクレイピングとは、ウェブサイトから必要な情報を取り出すコンピュータ技術のことです。その際、サイトのHTMLのどこに必要な情報があるのかを探索する必要があります。

HTMLではタグで文字列を挟んで、記述を行います。タグの中に複数のタグを入れることもでき、階層を持つ構造になります。
以下のHTMLのコードの一部を木構造の図として表してみます。

```html:HTMLのコードの一部
<html>
    <head>
        <title>タイトル</title>
    </head>

    <body>
        <h1> 見出し </h1>
        <p> 要素1 </p>
    </body>
</html>
```

これを図に表すと

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/ff23f311-651d-63c3-09c5-e5217144f302.png">


このように表せます。

このように、HTMLのような構造化された文書を、DOMツリーという木構造のデータとして読み込み、必要な要素を抽出するのが、スクレイピングの主なタスクになります。

### 深さ優先探索
グラフや木構造を探索する際に使われるアルゴリズムの一つです。
木構造のノードをそれ以上進むことができないところまで進み、探索していく方法です。

#### イメージ
木構造を使って説明します。
<img width="330" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/9a298fb3-942e-a5c3-f2de-c949f1412279.gif">

#### 解説
1\. まず、木構造のルートを探索する。目的のデータと合致しなければ次のノードへ
<img width="330" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/0567aba5-5c62-8be5-913d-0b98bb450a1a.png">

2\. 1番左側のエッジを進み、探索を行う。目的のデータと合致しなければ次のノードへ
<img width="330" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/dd718e0a-2119-9514-55f2-f17daa8eb964.png">

3\. 同様にエッジを進み、探索を行う。目的のデータと合致しなければ次のノードへ。この操作をリーフノードに行き着くまで続ける。
<img width="330" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/c482be8e-abc6-9073-2fa1-2ef3668ed936.png">

4\. リーフノードに行き着いたら、ルートノードに戻り、左から2番目のエッジを進み探索を行う。目的のデータと合致しなければ次のノードに進んでいく。リーフノードにたどり着くまで続ける。
<img width="330" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/9d3965a6-eb0d-05c4-2bdc-b8bca0df1c8f.png">

5\. 目的のデータを見つける or 全部のノードを調べ終わったら探索終了。

この場合の時間計算量はO(|E|)、空間計算量はO(|V|)です。
（|E|はグラフのエッジの数、|V|はグラフのノードの数）

:::note info
**空間計算量**
空間計算量とは、探索を行う際に必要なメモリ量を表しています。
時間計算量同様、オーダー表記を使用して記述します。
メモリ量が小さいほど良いアルゴリズムだと言えます。
:::

### 幅優先探索
グラフや木構造を探索する際に使われるアルゴリズムの一つです。
木構造のルートノード（Level1の部分）から近いレベルのノードを探索し、終わったら次のレベルの探索を行うという方法です。

#### イメージ
木構造を使って説明します。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/1aa33c4e-561b-df05-f44f-3e24ebec8c11.gif">

#### 解説
1\. まず、木構造のルートノードを探索する。目的のデータと合致しなければ次のノードへ
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/1652dfa4-e3c6-fc3d-3bea-a9b2714a8473.png">

2\. Level2の1番左側のノードを探索し、目的のデータと合致しなければ次のノードへ
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/77c50451-4690-2308-e8ae-9c8a38e7eb0d.png">

3\. Level2の左から2番目のノードを探索し、目的のデータと合致しなければ次のノードへ
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/f14e9c38-088b-af35-a887-7725b462a034.png">

4\. Level2の右側のノードを探索し、目的のデータと合致しなければ次のノードへ。Level2のノードは全て探索が終わったためLevel3に進む。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/b197fc24-6bdd-a81e-46f4-4c0e1f43b624.png">

5\. Level3の1番左側のノードを探索し、目的のデータと合致しなければ次のノードへ。レベルごとに順番に探索していく。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3033130/e7220de8-88fe-a77b-782b-85f6330ec3e9.png">

6\. 目的のデータを見つける or 全部のノードを調べ終わったら探索終了。


時間計算量はO(|E|)、空間計算量はO(|V|)です。

# まとめ

<table>
  <thead>
    <tr>
      <th></th> <th>線形探索</th> <th>二分探索</th> <th>深さ優先探索</th> <th>幅優先探索</th>
    </tr>
  </thead>
  <tr>
    <td>型</td> <td>リスト</td> <td>リスト</td> <td>木構造<br>グラフ</td> <td>木構造<br>グラフ</td>
  </tr>
  <tr>
    <td>計算量<br>（時間計算量）</td> <td>O(N)</td> <td>O(logN)</td> <td>O(|E|)</td> <td>O(|E|)</td>
  </tr> 
  <tr>
    <td> 使う場面 </td> <td>データ数が少ない場合</td> <td>データがソートされている場合<br>データ数が多い場合</td> <td>木構造の幅が広い場合<br>深い部分に目的のデータがある場合</td> <td>木構造の深さが深い場合<br>浅い部分に目的のデータがある場合</td>
  </tr>
</table>

# 参考サイト
* [Algorithms Every Programmer Should Know](https://dev.to/surajondev/algorithms-every-programmer-should-know-part-1-searching-algorithm-1hd3)
* [アルゴリズムロジック](https://algo-logic.info/time-space-complexity/)
* [探索アルゴリズムとは？基本から応用まで分かりやすく解説します！](https://products.sint.co.jp/topsic/blog/search-algorithm)

# おわりに
今回は基本的な探索アルゴリズムの解説を行いました。
この他にもたくさんの種類のアルゴリズムが存在しているので、気になった方は調べてみると面白いかもしれません。


弊社では、経験の有無を問わず、社員やインターン生の採用を行っています。
興味のある方は[こちら](https://www.recruit.nuco.co.jp/?qiita_item_id=f1491f1eb108af40b7ab)をご覧ください。