17298번: 오큰수
문제 풀이 아이디어
# 시간복잡도 제한
: 그냥 수열의 모든 수에 대해 하나하나 오큰수를 구하면 이중반복문을 사용할 수 밖에 없게 됩니다.
: 즉 O(n**2)가 되어 n이 최대 1,000,000인 경우 무조건 시간초과가 납니다.
: 따라서 O(n)의 시간복잡도를 가진 방법을 사용해야 합니다.
# 관점의 전환
: 초점를 오큰수를 구하는 수 a[i]에 두고
: 뒤에 오는 어떤 수가 NGE가 될 수 있는지 구하는 것이 아니라
: NGE(i)에 초점을 두고 문제에 접근하는 것입니다.
: -> 현재 a[i]가 어떤 수의 NGE가 될 수 있는지 찾는 것입니다.
# 사용해야하는 자료구조 = stack
: 현재 a[i]가 어떤 수의 오큰수가 될 수 있는지 확인하기 위해서는
: a[i - 1] ~ a[0]의 수 중에서 아직 오큰수를 구하지 못한 수들과 비교하여
: 그 수보다 a[i]가 큰지 확인해야 합니다.
: 위 방법대로 진행하기 위해서는 stack을 활용해봅니다.
: a[0]에서 시작해서 아직 오큰수를 구하지 못한 수를 stack에 넣어 둔다고 생각합니다.
: 그리고 하나씩 pop하면서 a[i]가 오큰수가 될 수 있는지 확인하는 방법입니다.
# 의사코드
: 아직 오큰수를 구하지 못한 a[i]의 i를 담을 stack을 선언합니다.
: 그리고 a[1]부터 순회합니다.
: a[stack.last]가 a[i]를 NGE로 가질 수 있는지 따집니다.
: stack이 비었거나 a[stack.last] a[i]보다 작다면 (= stack에는 a[i]보다 큰 수만 들어있다 = a[i]가 더 이상 아래 수들의 NGE가 될 수 없다.)
: i를 스택에 넣습니다.
# 시간 복잡도
: while문에 while문이 있어 이중반복문처럼 생겼지만
: 논리적으로 생각하면 a[i]와 그 아래 모든 수를 비교하는 것이 아니라 오큰수를 구하지 못한 수들과만 비교하는 것이므로
: 시간복잡도는 O(n)이라고 볼 수 있습니다.
풀이
let N = Int(readLine()!)!
let a = readLine()!.split(separator: " ").map { Int(String($0))! }
var stack = [0]
var i = 1
var result = Array(repeating: -1, count: N)
while i < N {
while !stack.isEmpty && a[i] > a[stack.last!] {
result[stack.popLast()!] = a[i]
}
stack.append(i)
i += 1
}
print(result.map { String($0) }.joined(separator: " "))