양의 정수 n이 매개변수로 주어집니다.
n × n 배열에 1부터 n2까지 정수를 인덱스 [0][0]부터,
시계방향 나선형으로 배치한 이차원 배열을 return 하는 solution 함수를 작성해 주세요.
다시 생각해봐도 되게 쉽지 않았던 문제였는데,
문제가 어려울수록 문제를 잘게 쪼개거나 질문을 던지면 쉬워진다... 라고 누가 한 말이 생각났습니다.
그러니 "시계방향 나선형으로 배치한" 그리고 "이차원 배열" 두 개로 나눠서 생각해봅시다.
일단 결과를 채워넣을 배열인 n제곱 크기의 이차원 배열을 먼저 만들어봅시다.
// 2차원 정수 배열 선언
var result: [[Int]] = []
// 아래 initialArray는 제가 클로저 문법 연습하느라 좀 귀찮게 썼습니다.
// num을 받아서 그만큼의 0을 넣은 배열을 리턴합니다.
let initialArray: (Int) -> [Int] = { num in
var temp: [Int] = []
for _ in 1...num {
temp.append(0)
}
return temp
}
// 문제에서 주는 n을 받아 0으로 채워진 n제곱 크기의 2차원 배열을 생성합니다.
for _ in 1...n {
result.append(initialArray(n))
}
0으로 이뤄진 n제곱 크기의 2차원 배열 result가 만들어졌겠죠?
괜찮은 시작입니다.
시작이 반이니 이제 반 했다고 볼 수 있습니다.
아직 반이나 남았네, 또는 아직 반 밖에 못했네 같은 한탄은 대충 넣어두시고,
장원영님의 "럭키비키잔앙"을 머릿속에 새기면서 계속 진행해보도록 합시다.
프로그래머스에서 나오는 예제를 한번 볼까요?
뭔가 이해가 갈랑말랑하시나요?
행과 열을 result[0][0] 이런 식으로 보면 이해가 힘들죠?
답정너입니다 빨리 그렇다고 하세요
흉악한 기계놈들이나 잘 알아먹지 한낱 나약한 휴먼에 불과한 우리들은,
조금 더 쉽게 이해할 방법을 찾아야 합니다.
중학교 시절 학교에서 수많은 수포자를 양산했던 함수와 그래프를 떠올려봅시다.
긍까... 행이 x축이고 열이 y축이라 생각해봅시다.
감이 좀 오시나요?
를 이용해 1부터 n제곱까지의 숫자를 result 배열에 넣어줄 계획입니다.
시작점인 (0, 0)에 1, (1, 0)에 2 ...(생략)... 뭐 대략 그렇게요.
문제가 조금은 쉬워지셨나요?
뭔가 번뜩이셨다면 이 글을 다 읽기 전에 문제를 혼자서 풀어보면 좋습니다.
설명 계속하겠습니다.
x좌표와 y좌표로 사용할 변수 두개를 0으로 생성해줍니다.
현재 넣어야할 번호 변수도 1로 생성해줍니다.
그리고 좌표들을 이용해 현재 위치의 값을 바꿔주는 함수를 하나 만들어줍니다.
var xCoordinate: Int = 0
var yCoordinate: Int = 0
var currentNumber: Int = 1
func replaceValue() {
result[yCoordinate].remove(at: xCoordinate) // 해당 좌표에 있는 값을 제거하고,
result[yCoordinate].insert(currentNumber, at: xCoordinate) // 새로운 숫자로 채워준다.
}
이렇게 되면, x와 y좌표를 이용해 currentNumber를 증가시켜가면서 n제곱이 될때까지 맞는 위치에 넣어줄 수 있겠죠?
근데 우리는 "시계방향 나선형으로 배치"해야 한다는 과제가 남아있습니다.
이런. 산 넘어 산이군요.
하지만 "나를 죽이지 못 하는 것은 나를 더 강하게 만들 뿐이다" 라는 격언을 떠올리며 계속해봅시다.
뭔가_좀_다르긴_한데.jpg
처음에 언급했듯, 문제가 어려울수록 질문을 던지면 쉬워집니다.
이해를 돕기 위해 시각 자료를 만들어봤습니다.
옛날에 피쳐폰으로 즐겨하던 지렁이 게임이 생각나네요.
여튼 핵심은, 진행하던 방향이 막히면 우회전을 하면 된다!가 되겠습니다.
다만 여기서 "막혔다"는 두 가지로 나뉘겠죠.
- 배열 범위를 넘어선 것,
- 특정 값이 이미 들어가 있다.
감이 오셨나요?
그럼 구현해야 할 사항을 나열해봅시다.
- 진행하고 있는 방향이 어디인가?
- 앞이 막혔는가?
- 우회전한다.
- 앞으로 진행한다.
부터 봅시다.
스위프트의 enum(열거형)을 사용해주면 정말 깔꼼하게 진행하고 있는 방향을 정의할 수 있습니다.
우향, 좌향, 하향, 상향을 각각 동서남북으로 정의해줍시다.
초기 진행방향은 오른쪽으로 가야 하니 동쪽으로 지정해주고요.
enum Direction {
case east, west, north, south
}
var currentlyFacing: Direction = .east
는 두 가지 경우가 존재한다 했습니다.
근데 방금 1번에서 진행방향(= currentlyFacing)을 구현했기 때문에,
동서남북으로 진행중일때 각각 어떤 조건이어야 앞이 막힌 것인지 정의해주면 되겠습니다.
switch/case문으로 작성해봅시다.
func isBlocked() -> Bool {
var trueIfBlocked: Bool
switch currentlyFacing {
case .east: if xCoordinate == n - 1 || result[yCoordinate][xCoordinate + 1] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .west: if xCoordinate == 0 || result[yCoordinate][xCoordinate - 1] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .north: if yCoordinate == 0 || result[yCoordinate - 1][xCoordinate] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .south: if yCoordinate == n - 1 || result[yCoordinate + 1][xCoordinate] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
}
return trueIfBlocked
}
currentlyFacing의 값을 현재 값에 기반해서 변경해줍니다.
마찬가지로 switch/case문을 활용해 봅시다.
func turnRight() {
switch currentlyFacing {
case .east: currentlyFacing = .south
case .west: currentlyFacing = .north
case .north: currentlyFacing = .east
case .south: currentlyFacing = .west
}
}
진행하는 방향(currentlyFacing)에 따라 x,y좌표에 +-1씩 해주면 되겠습니다.
마찬가지로 switch/case문을 활용해 봅시다.
func moveFoward() {
switch currentlyFacing {
case .east: xCoordinate += 1
case .west: xCoordinate -= 1
case .north: yCoordinate -= 1
case .south: yCoordinate += 1
}
}
어후... 이제 진짜 거의 다 끝났습니다.
지금까지 작성했던 currentlyFacing, moveFoward(), turnRight(), isBlocked(), replaceValue(), currentValue()를 총동원하여 while문 아래로 묶어주면...!
while currentNumber <= nSquared {
if currentValue() == 0 { // 현 x,y좌표의 값이 초기값인 0이라면,
replaceValue() // currentNumber로 교체한다.
if isBlocked() { // 앞이 막혔다면,
turnRight() // 우회전 하고,
moveFoward() // 앞으로 간다.
} else {
moveFoward() // 안막혔으면 그냥 앞으로 간다.
}
}
currentNumber += 1
}
끝입니다. 휴.
전체 코드:
import Foundation
func solution(_ n:Int) -> [[Int]] {
let nSquared: Int = n * n
var result: [[Int]] = []
var xCoordinate: Int = 0
var yCoordinate: Int = 0
var currentNumber: Int = 1
let initialArray: (Int) -> [Int] = { num in
var temp: [Int] = []
for _ in 1...num {
temp.append(0)
}
return temp
}
for _ in 1...n {
result.append(initialArray(n))
}
enum Direction {
case east, west, north, south
}
var currentlyFacing: Direction = .east
func moveFoward() {
switch currentlyFacing {
case .east: xCoordinate += 1
case .west: xCoordinate -= 1
case .north: yCoordinate -= 1
case .south: yCoordinate += 1
}
}
func turnRight() {
switch currentlyFacing {
case .east: currentlyFacing = .south
case .west: currentlyFacing = .north
case .north: currentlyFacing = .east
case .south: currentlyFacing = .west
}
}
func isBlocked() -> Bool {
var trueIfBlocked: Bool
switch currentlyFacing {
case .east: if xCoordinate == n - 1 || result[yCoordinate][xCoordinate + 1] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .west: if xCoordinate == 0 || result[yCoordinate][xCoordinate - 1] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .north: if yCoordinate == 0 || result[yCoordinate - 1][xCoordinate] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
case .south: if yCoordinate == n - 1 || result[yCoordinate + 1][xCoordinate] != 0 { trueIfBlocked = true } else { trueIfBlocked = false }
}
return trueIfBlocked
}
func replaceValue() {
result[yCoordinate].remove(at: xCoordinate)
result[yCoordinate].insert(currentNumber, at: xCoordinate)
}
func currentValue() -> Int {
return result[yCoordinate][xCoordinate]
}
while currentNumber <= nSquared {
if currentValue() == 0 {
replaceValue()
if isBlocked() {
turnRight()
moveFoward()
} else {
moveFoward()
}
}
currentNumber += 1
}
return result
}
진짜 이 문제 뭐예요?