正方形の二次配列を渦状に取得する方法！

##1. はじめに
皆さん、初めまして！
今、大学でソフトウェア工学を専攻しているモナ坊です。

読まれる前に、読者には<b>コードよりも考え方</b>を参考として読んで欲しいという希望があります。

突然ですが、皆さん配列の流れに困惑したことはありますでしょうか？
そのくらい分かるわ！とつっこまれるかも知れませんが、もう少し読んでください。


<b>では、正方形の配列をポインターで渦状に移動させる方法はご存知でしょうか？</b>

詳しく説明致します。

<h1>本題</h1>
では、さっそく本題に入ります。

正方形の二次配列が存在するとします。
例.)


```python:array_traversal.py

array = [
    [1, 2, 3, 4]
    [12, 13, 14, 5]
    [11, 16, 15, 6]
    [10, 9, 8, 7]
]
```

この 縦x横の二次配列を渦状に一つ一つの要素を渡り、一次配列を取得してください。(時間計算量はO(n))


```:出力例
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
```

##考え方##
まずは考え方からです。



## 1. 少し視点を変えて見てみる
1つ目の画像：
![Screen Shot 2021-01-20 at 17.56.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/02364450-339b-dcd9-a7fb-35c0d2283c1c.png)

[１つ目の画像](#1-少し視点を変えて見てみる)をみるとわかりやすいかも知れません。
渦巻状に取得はするものの、そこで少し視点を変えて問題に取り組んでみると
<b>外側の四角と内側の四角に分けることができます。</b>

そう見てみると少し、複雑さがなくなりました。ね？


次にこの四角を迂回するにあたって、<b>ポインターが2つ（ロウとカラム用）ある</b>と考えてください。
ここでのポインターは、簡潔に言うと処理を指示する為に「この処理をここでしてね！」とコンピューターに伝えるためのものと捉えてください。

このポインター２つを渦状に取得させるために考慮する点が１つあります。
それは、<b>二重計上(double counting)の制御</b>です。

----------------------
補足：
ここでは、ロウは横列、カラムは縦列を指しているます。
二重計上(double counting) は簡潔に言いますと、処理にあたって同じ要素が被ってしまうことです。



<br>




## 2. 二重計上の制御

次の画像をご覧ください。

<b>２つ目：</b>
![Screen Shot 2021-01-20 at 18.18.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/08d09452-32f8-4044-8b05-2e45b6cb80dc.png)

こうすることによって<b>お互いに境界線を作ることができ、また二重計上も防ぐことができる</b>ので効率の良い解き方になってくると思います

--------------------------
*<b>SC, EC, SR, ERとは？？</b>
SCがカラムのはじめ、ECがカラムの終わり
SRがロウのはじめ、ERがロウの終わり
となっています。

------------------



##3.　一連の処理の流れ(外側)


それでは、最後に準備が整ったところで流れを説明します。

<h2> 3.1 トップロウ([1, 2, 3, 4])をカラム用のポインターで取得します</h2>
![Screen Shot 2021-01-20 at 20.30.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/46b91a04-8673-b212-a847-1231841533bc.png)


ここは、普段使われているfor loopの仕方でできますね。

<b>サンプルコード：</b>

```python:
   for col in range(sc, ec + 1):
       result.append(array[sr][col])
```

<h2> 3.2 ECが指しているカラムをERまでロウ用のポインターで取得します。(ここでは、[5, 6, 7]ですね。）</h2> 

![Screen Shot 2021-01-20 at 20.35.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/ad022db8-160f-62e4-2342-bb54d4ac542d.png)

<b>サンプルコード：</b>

```python:
   for row in range(sr + 1, er + 1):
       result.append(array[row][ec])
```


<h2> 3.3 ボトムロウ([10, 9, 8])を逆方向にカラム用のポインターで取得します。</h2> 
ここは、reverseというbuilt-in function を使って逆方向のfor loop を実装します。
![Screen Shot 2021-01-20 at 20.37.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/9a91c637-4ab1-0fda-f29f-2419633b0c1c.png)

<b>サンプルコード：</b>

```python:
   for col in reversed(range(sc, ec)):
       result.append(array[er][col])
```

<h2> 3.4 最後に、２行目の[12]と３行目の[11]を取得したいのでSR+1 ~ ER区間で逆方向にロウ用のポインターで取得します。</h2> 
ここも、reverse を使います。
![Screen Shot 2021-01-20 at 20.39.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/4b2b81a7-44b6-b66f-f7a5-6ea81d4688a4.png)

<b>サンプルコード：</b>

```python:
   for row in reversed(range(sr + 1, er)):
       result.append(array[row][sr])
```


<br>

これで、やっと外側の四角の要素を全て渦状に取得できました。

<br>



##4.　一連の処理の流れ(内側)



次は、内側の四角です。
![Screen Shot 2021-01-20 at 17.56.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/920249/e3a15af5-65aa-4828-12f3-bf1a46675add.png)
でも、このまま処理を同じように回すとhard codingになってしまいますね？

なので、<b>SRとSCを１つ足して内側に寄せます。また、ECとERを１つ足して内側に寄せる</b>ことによって1~5で作ったものを再利用することができます！ECOですね〜笑


```python:
#内側の正方形の処理
sr += 1 
er -= 1
sc += 1
ec -= 1		
```



以下からは、画像とコードでの説明を省略致します。

1. トップロウ([13, 14])をカラム用のポインターで取得します
こちらも同様に、普段使われているfor loopの仕方でできますね。

2. ECが指しているカラムをERまでロウ用のポインターで取得します。(ここでは、[15]ですね。）

3. ボトムロウ([16])を逆方向にカラム用のポインターで取得します。
ここは、reverseというbuilt-in function を使って逆方向のfor loop を実装します。

4. 最後に、SRがERと等しくなるのでrangeが０になり何も取得できないようになります。


<h1>完成コード</h1>



```python:array_traversal.py


def spiralTraverse(array):
	result = []
	sr, er = 0, len(array) - 1
	sc, ec = 0, len(array[0]) - 1
	
	while sr <= er and sc <= ec:
		for col in range(sc, ec + 1):
			result.append(array[sr][col])
			
		for row in range(sr + 1, er + 1):
			result.append(array[row][ec])
			
		for col in reversed(range(sc, ec)):
			result.append(array[er][col])
			
		for row in reversed(range(sr + 1, er)):
			result.append(array[row][sr])
			
		sr += 1 
		er -= 1
		sc += 1
		ec -= 1
		
	return result 
	
```

##終わりに

あくまで、１つの考え方なので、他にも解答方法はいくつもあります。例えば、再帰法を使った処理など。
注意点として、エッジケースをここでは考慮していないのでそこは問題に合わせて対応してください。


長くなりましたが、以上となります！お疲れ様でした！


<strong>LGTM よろしくお願いします！</strong>