방향성에 거스르지 않도록 순서대로 나열한 것
입니다. DAG(Directed Acyclic Graph, 유향 비순환 그래프(사이클이 없는 그래프))
에서만 수행될 수 있습니다. import Foundation
// Ring Buffer를 활용한 Queue
struct RingBuffer {
private var array: [Int?]
private var readIndex = 0
private var writeIndex = 0
init(count: Int) {
array = Array(repeating: nil, count: count)
}
var first: Int? {
array[readIndex]
}
private var availableSpaceForReading: Int {
writeIndex - readIndex
}
private var availableSpaceForWriting: Int {
array.count - availableSpaceForReading
}
var isEmpty: Bool {
availableSpaceForReading == 0
}
var isFull: Bool {
availableSpaceForWriting == 0
}
@discardableResult
mutating func write(_ element: Int) -> Bool {
if !isFull {
array[writeIndex % array.count] = element
writeIndex += 1
return true
} else {
return false
}
}
mutating func read() -> Int? {
if !isEmpty {
let element = array[readIndex % array.count]
readIndex += 1
return element
} else {
return nil
}
}
}
// RingBuffer를 활용한 Queue 생성
struct Queue {
private var ringBuffer: RingBuffer
init(count: Int) {
ringBuffer = RingBuffer(count: count)
}
var isEmpty: Bool {
ringBuffer.isEmpty
}
var peek: Int? {
ringBuffer.first
}
@discardableResult
mutating func enqueue(_ element: Int) -> Bool {
ringBuffer.write(element)
}
mutating func dequeue() -> Int? {
ringBuffer.read()
}
}
// Kahn's Algorithms
func topologicalSortKahn(graph: [[Int]], vertices: Int) -> [Int]? {
// 진입 차수를 위한 배열
// 최초 '0'으로 초기화
var inDegree = [Int](repeating: 0, count: vertices)
// 링 버퍼 큐는 최대 N개 즉 노드의 개수만큼의 크기를 할당해야한다.
// 최악의 경우 모든 차수가 0일수도 있기 때문이다.
var queue = Queue(count: vertices - 1)
// 정렬된 노드를 저장하는 배열
var order: [Int] = []
// 진입 차수 계산
// 인접 리스트 그래프 활용
for node in graph {
for edge in node {
inDegree[edge] += 1
}
}
// 진입 차수가 0인 노드 큐에 추가
for i in 1..<inDegree.count {
if inDegree[i] == 0 {
queue.enqueue(i)
}
}
// BFS 수행
while !queue.isEmpty {
let node = queue.dequeue()!
order.append(node)
for edge in graph[node] {
inDegree[edge] -= 1
if inDegree[edge] == 0 {
queue.enqueue(edge)
}
}
}
// 모든 노드가 처리되었는지 확인
// 즉 큐가 조기 종료되면 order count가 vertices와 다름
if order.count == vertices - 1 {
return order
} else {
return nil
}
}
// 예시 그래프 사용
let n = 7
let graph = [[], [2,5],[3,6],[4],[7],[6],[4],[]]
let graph2 = [[], [2,5],[3],[4,6],[7],[6],[4,2],[]]
if let sorted = topologicalSortKahn(graph: graph, vertices: n + 1) {
print("위상 정렬 결과: \(sorted)")
} else {
print("사이클이 존재합니다.")
}
if let sorted = topologicalSortKahn(graph: graph2, vertices: n + 1) {
print("위상 정렬 결과: \(sorted)")
} else {
print("사이클이 존재합니다.")
}
위상 정렬 결과: [1, 2, 5, 3, 6, 4, 7]
사이클이 존재합니다.
push
합니다. 이는 해당 노드에서 출발하는 모든 간선이 처리되었음을 의미합니다. 방문 x, 방문 중, 방문 완료
로 나누며, 방문 중
인 노드를 다시 방문하게 되면, 그래프의 사이클이 존재한다고 판단합니다. enum VisiteState {
case unvisited, visiting, visited
}
var visited: [VisiteState] = []
var stack: [Int] = []
var hasCycle = false
func dfs(graph: [[Int]], node: Int) {
// 방문 중일때 방문했을 경우 Cycle 발생
if visited[node] == .visiting {
hasCycle = true
return
}
// 이미 방문한 node는 무시
if visited[node] == .visited {
return
}
visited[node] = .visiting
for neighbor in graph[node] {
dfs(graph: graph, node: neighbor)
if hasCycle { return }
}
visited[node] = .visited
// 방문이 다 종료되면 Stack에 Node 추가
stack.append(node)
}
func topologicalSort(graph: [[Int]], vertices: Int) -> [Int]? {
visited = [VisiteState](repeating: .unvisited, count: vertices)
stack = []
hasCycle = false
// 모든 노드에 대해서 dfs 실행
for i in 1..<vertices {
if visited[i] == .unvisited {
dfs(graph: graph, node: i)
if hasCycle { return nil }
}
}
return stack.reversed()
}
// 예시 그래프 사용
let n = 7
let graph = [[], [2,5],[3,6],[4],[7],[6],[4],[]]
let graph2 = [[], [2,5],[3],[4,6],[7],[6],[4,2],[]]
if let sorted = topologicalSort(graph: graph, vertices: n + 1) {
print(sorted.map { String($0)}.joined(separator: " "))} else {
print("사이클이 존재합니다.")
}
if let sorted = topologicalSort(graph: graph2, vertices: n + 1) {
print(sorted.map { String($0)}.joined(separator: " "))
} else {
print("사이클이 존재합니다.")
}
1 5 2 6 3 4 7
사이클이 존재합니다.
Kahn's Algorithm
은 진입 차수와 큐를 활용해서 위상 정렬를 수행하며, DFS
는 재귀 함수를 활용해서 위상 정렬을 수행합니다. 3 2
1 3
2 3
1 2 3
4 2
4 2
3 1
4 2 3 1
// MARK: 1. Kahn's Algorithm을 사용하여 문제 해결
import Foundation
// Ring Buffer를 활용한 Queue
struct RingBuffer {
private var array: [Int?]
private var readIndex = 0
private var writeIndex = 0
init(count: Int) {
array = Array(repeating: nil, count: count)
}
var first: Int? {
array[readIndex]
}
private var availableSpaceForReading: Int {
writeIndex - readIndex
}
private var availableSpaceForWriting: Int {
array.count - availableSpaceForReading
}
var isEmpty: Bool {
availableSpaceForReading == 0
}
var isFull: Bool {
availableSpaceForWriting == 0
}
@discardableResult
mutating func write(_ element: Int) -> Bool {
if !isFull {
array[writeIndex % array.count] = element
writeIndex += 1
return true
} else {
return false
}
}
mutating func read() -> Int? {
if !isEmpty {
let element = array[readIndex % array.count]
readIndex += 1
return element
} else {
return nil
}
}
}
struct Queue {
private var ringBuffer: RingBuffer
init(count: Int) {
ringBuffer = RingBuffer(count: count)
}
var isEmpty: Bool {
ringBuffer.isEmpty
}
var peek: Int? {
ringBuffer.first
}
@discardableResult
mutating func enqueue(_ element: Int) -> Bool {
ringBuffer.write(element)
}
mutating func dequeue() -> Int? {
ringBuffer.read()
}
}
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
let N = testCase[0]
let M = testCase[1]
var graph = [[Int]](repeating: [Int](), count: N + 1)
var inDegree = [Int](repeating: 0, count: N + 1)
var queue = Queue(count: N)
var order: [Int] = []
for _ in 0..<M {
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
// 앞에 서야 한다. 즉, 하나의 간선으로 연결된 다음 노드
let parent = testCase[0]
let child = testCase[1]
graph[parent].append(child)
inDegree[child] += 1
}
// 진입 차수가 0인 노드 큐에 추가
for i in 1..<inDegree.count {
if inDegree[i] == 0 {
queue.enqueue(i)
}
}
// BFS 수행
while !queue.isEmpty {
let node = queue.dequeue()!
order.append(node)
for edge in graph[node] {
inDegree[edge] -= 1
if inDegree[edge] == 0 {
queue.enqueue(edge)
}
}
}
print(order.map { String($0) }.joined(separator: " "))
// MARK: 2. DFS를 사용하여 문제 해결
import Foundation
enum VisiteState {
case unvisited, visiting, visited
}
var visited: [VisiteState] = []
var stack: [Int] = []
var hasCycle = false
func dfs(graph: [[Int]], node: Int) {
// 방문 중일때 방문했을 경우 Cycle 발생
if visited[node] == .visiting {
hasCycle = true
return
}
// 이미 방문한 node는 무시
if visited[node] == .visited {
return
}
visited[node] = .visiting
for neighbor in graph[node] {
dfs(graph: graph, node: neighbor)
if hasCycle { return }
}
visited[node] = .visited
// 방문이 다 종료되면 Stack에 Node 추가
stack.append(node)
}
func topologicalSort(graph: [[Int]], vertices: Int) -> [Int]? {
visited = [VisiteState](repeating: .unvisited, count: vertices)
stack = []
hasCycle = false
// 모든 노드에 대해서 dfs 실행
for i in 1..<vertices {
if visited[i] == .unvisited {
dfs(graph: graph, node: i)
if hasCycle { return nil }
}
}
return stack.reversed()
}
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
let N = testCase[0]
let M = testCase[1]
var graph = [[Int]](repeating: [Int](), count: N + 1)
for _ in 0..<M {
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
// 앞에 서야 한다. 즉, 하나의 간선으로 연결된 다음 노드
let parent = testCase[0]
let child = testCase[1]
graph[parent].append(child)
}
if let sorted = topologicalSort(graph: graph, vertices: N + 1) {
print(sorted.map { String($0) }.joined(separator: " "))
}
4 2
4 2
3 1
3 1 4 2
0
이면 추가가 된다. 즉, 이는 앞서 들어온 숫자 또는 같은 차수에서 들어온 숫자들만 있다는 뜻이다. import Foundation
struct Heap {
var elements: [Int] = []
let sort: (Int, Int) -> Bool
init(sort: @escaping (Int, Int) -> Bool, elements: [Int] = [] ) {
self.sort = sort
self.elements = elements
if !elements.isEmpty {
for i in stride(from: count / 2 - 1, through: 0, by: -1) {
siftDown(from: i)
}
}
}
var isEmpty: Bool {
elements.isEmpty
}
var count: Int {
elements.count
}
func peek() -> Int? {
elements.first
}
func leftChildIndex(ofParentAt index: Int) -> Int {
(index * 2) + 1
}
func rightChildIndex(ofParentAt index: Int) -> Int {
(index * 2) + 2
}
func parentIndex(ofChildAt index: Int) -> Int {
(index - 1) / 2
}
private mutating func siftDown(from index: Int) {
var parent = index
while true {
let left = leftChildIndex(ofParentAt: parent)
let right = rightChildIndex(ofParentAt: parent)
var candidate = parent
if left < count && sort(elements[left], elements[candidate]) {
candidate = left
}
if right < count && sort(elements[right], elements[candidate]) {
candidate = right
}
if candidate == parent {
return
}
elements.swapAt(parent, candidate)
parent = candidate
}
}
private mutating func siftUp(from index: Int) {
var child = index
var parent = parentIndex(ofChildAt: child)
while child > 0 && sort(elements[child], elements[parent]) {
elements.swapAt(child, parent)
child = parent
parent = parentIndex(ofChildAt: child)
}
}
mutating func remove() -> Int? {
guard !isEmpty else {
return nil
}
elements.swapAt(0, count - 1)
defer {
siftDown(from: 0)
}
return elements.removeLast()
}
mutating func insert(_ element: Int) {
elements.append(element)
siftUp(from: elements.count - 1)
}
}
struct Queue {
private var heap: Heap
init(sort: @escaping (Int, Int) -> Bool, elements: [Int] = []) {
heap = Heap(sort: sort, elements: elements)
}
var isEmpty: Bool {
heap.isEmpty
}
var peek: Int? {
heap.peek()
}
@discardableResult
mutating func enqueue(_ element: Int) -> Bool {
heap.insert(element)
return true
}
mutating func dequeue() -> Int? {
heap.remove()
}
}
func topologicalSortKahn(graph: [[Int]], vertices: Int) -> [Int] {
var inDegree = [Int](repeating: 0, count: vertices)
var queue = Queue(sort: <, elements: [Int]() )
var order: [Int] = []
for node in graph {
for edge in node {
inDegree[edge] += 1
}
}
for i in 1..<inDegree.count {
if inDegree[i] == 0 {
queue.enqueue(i)
}
}
while !queue.isEmpty {
let node = queue.dequeue()!
order.append(node)
for edge in graph[node] {
inDegree[edge] -= 1
if inDegree[edge] == 0 {
queue.enqueue(edge)
}
}
}
return order
}
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
let N = testCase[0]
let M = testCase[1]
var graph = [[Int]](repeating: [Int](), count: N + 1)
for _ in 0..<M {
let testCase = readLine()!.components(separatedBy: " ").map { Int($0)! }
// 앞에 서야 한다. 즉, 하나의 간선으로 연결된 다음 노드
let parent = testCase[0]
let child = testCase[1]
graph[parent].append(child)
}
let sorted = topologicalSortKahn(graph: graph, vertices: N + 1)
print(sorted.map { String($0) }.joined(separator: " "))
- 팀의 수 n을 포함하고 있는 한 줄. (2 ≤ n ≤ 500)
- n개의 정수 ti를 포함하고 있는 한 줄. (1 ≤ ti ≤ n) ti는 작년에 i등을 한 팀의 번호이다. 1등이 가장 성적이 높은 팀이다. 모든 ti는 서로 다르다.
- 상대적인 등수가 바뀐 쌍의 수 m (0 ≤ m ≤ 25000)
- 두 정수 ai와 bi를 포함하고 있는 m줄. (1 ≤ ai < bi ≤ n) 상대적인 등수가 바뀐 두 팀이 주어진다. 같은 쌍이 여러 번 발표되는 경우는 없다.
- n개의 정수를 한 줄에 출력한다. 출력하는 숫자는 올해 순위이며, 1등팀부터 순서대로 출력한다. 만약, 확실한 순위를 찾을 수 없다면 "?"를 출력한다. 데이터에 일관성이 없어서 순위를 정할 수 없는 경우에는 "IMPOSSIBLE"을 출력한다.
3
5
5 4 3 2 1
2
2 4
3 4
3
2 3 1
0
4
1 2 3 4
3
1 2
3 4
2 3
5 3 2 4 1
2 3 1
IMPOSSIBLE
IMPOSSIBLE
이라는 것을 이해를 못하고 있었는데, 문제를 다시 읽어보다가 이는 간선이 5 -> 4 -> 3 -> 2 -> 1 이렇게가 아니라 5 -> (4,3,2,1), 4 -> (3,2,1), 3 -> (2,1), 2 -> (1) 이렇게 라는 것을 깨닫게 되었다. IMPOSSIBLE
을 출력해주면 된다. node
의 edges
들이 0
이 되는게 2개 이상일 경우 정확한 순위를 알 수 없기 때문에 ?
을 출력해주면 된다.import Foundation
// Ring Buffer를 활용한 Queue
struct RingBuffer {
private var array: [Int?]
private var readIndex = 0
private var writeIndex = 0
init(count: Int) {
array = Array(repeating: nil, count: count)
}
var first: Int? {
array[readIndex]
}
private var availableSpaceForReading: Int {
writeIndex - readIndex
}
private var availableSpaceForWriting: Int {
array.count - availableSpaceForReading
}
var isEmpty: Bool {
availableSpaceForReading == 0
}
var isFull: Bool {
availableSpaceForWriting == 0
}
@discardableResult
mutating func write(_ element: Int) -> Bool {
if !isFull {
array[writeIndex % array.count] = element
writeIndex += 1
return true
} else {
return false
}
}
mutating func read() -> Int? {
if !isEmpty {
let element = array[readIndex % array.count]
readIndex += 1
return element
} else {
return nil
}
}
}
struct Queue {
private var ringBuffer: RingBuffer
init(count: Int) {
ringBuffer = RingBuffer(count: count)
}
var isEmpty: Bool {
ringBuffer.isEmpty
}
var peek: Int? {
ringBuffer.first
}
@discardableResult
mutating func enqueue(_ element: Int) -> Bool {
ringBuffer.write(element)
}
mutating func dequeue() -> Int? {
ringBuffer.read()
}
}
func topologicalSortKahn(graph: [[Int]], vertices: Int) {
var inDegree = [Int](repeating: 0, count: vertices)
var queue = Queue(count: vertices - 1)
var order: [Int] = []
// 진입 차수 계산
for node in graph {
for edge in node {
inDegree[edge] += 1
}
}
// 진입 차수가 0인 노드 큐에 추가
for i in 1..<inDegree.count {
if inDegree[i] == 0 {
queue.enqueue(i)
}
}
// BFS 수행
while !queue.isEmpty {
let node = queue.dequeue()!
order.append(node)
var isCertain = 0
for edge in graph[node] {
inDegree[edge] -= 1
if inDegree[edge] == 0 {
isCertain += 1
if isCertain > 1 {
print("?")
return
}
queue.enqueue(edge)
}
}
}
// 모든 노드가 처리되었는지 확인
// 즉 큐가 조기 종료되면 order count가 vertices와 다름
if order.count == vertices - 1 {
print(order.map { String($0) }.joined(separator: " "))
return
} else {
print("IMPOSSIBLE")
return
}
}
// 테스트 케이스 수
let N = Int(readLine()!)!
for _ in 0..<N {
// 팀원 수
let M = Int(readLine()!)!
// 팀원 배열
let testCase = [0] + readLine()!.components(separatedBy: " ").map { Int($0)! }
var graph = [[Int]](repeating: [Int](), count: M + 1)
// 그래프 초기화
for (index, value) in testCase.enumerated() {
guard index > 0 else { continue }
guard index + 1 <= M else { continue }
for i in (index + 1)...M {
graph[value].append(testCase[i])
}
}
let thisYear = Int(readLine()!)!
for _ in 0..<thisYear {
let testCase2 = readLine()!.components(separatedBy: " ").map { Int($0)! }
let node1 = testCase2[0]
let node2 = testCase2[1]
if graph[node1].contains(node2) {
graph[node1] = graph[node1].filter { $0 != node2 }
graph[node2].append(node1)
} else if graph[node2].contains(node1) {
graph[node2] = graph[node2].filter { $0 != node1 }
graph[node1].append(node2)
}
}
topologicalSortKahn(graph: graph, vertices: M + 1)
}
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.