Skip to content

Conversation

@sep037
Copy link
Collaborator

@sep037 sep037 commented Apr 11, 2025

🔗 문제 링크

트리의 부모 찾기

스크린샷 2025-04-11 22 45 27

✔️ 소요된 시간

30분

✨ 수도 코드

문제를 살짝 요약해보자면
루트 없는 트리 -> 무방향 그래프 (간선 정보만 존재)
루트는 1로 지정
각 노드의 부모 노드 번호 출력 !

IMG_FA1FD7D84E78-1

  1. 간선 정보를 바탕으로 인접 리스트를 생성하고

  2. 방문 기록을 담을 visited 배열과 부모 정보를 담을 배열을 선언했습니당

  3. DFS 함수로 현재 연결된 노드 중

    • 아직 방문하지 않은 노드라면
      - 부모 노드 저장
      - 재귀적으로 다음 노드 탐색
  4. 1번 루트 노드는 부모가 없으므로 제외하고 2번부터 N번 노드까지 부모를 출력했어요 !

📚 새롭게 알게된 내용

DFS는 재귀적으로 계속 깊이 들어가기 때문에 이미 방문한 노드에 방문 처리를 하는 습관을 꼭 들여야 겠다고 생각했어요

근데 제가 문제를 많이 안 풀어봐서 모르는데 노드는 거의 1부터 시작하나요 ? 여기서는 루트 노드가 1부터라 납득은 했는데 다른 문제들도 1부터인지 궁금해요 + 문제마다 다른지 .. ?

@sep037 sep037 changed the title 2025-04-10 트리의 부모 찾기 2-sep037 Apr 11, 2025
@sep037 sep037 self-assigned this Apr 11, 2025
Copy link
Collaborator

@MuchanKim MuchanKim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

간만에 오래안걸리고 푼 문제였네요. 마카로니씨랑 그냥 똑같은 방법으로 풀었어요. 인접리스트 + dfs

DFS는 visited 방문체크 필 ! 수 ! 입니다.(주입식교육)

노드는 거의 1부터 시작하나요 ? << 이 질문이 무슨 의미인지 고민했는데 그래프에서 인덱싱 방식이 0부터냐 1부터냐를 물으신걸로 답변하겠습니다.

---> 보통 0부터 시작인걸로 알고있었는데 검색해보니 한국 문제 방식에선 1이 많다네요(왜지감자)

고생하셨슴다! 깔끔한 풀이네요!

Comment on lines +16 to +24
for _ in 0..<N-1 {
let edge = readLine()!.split(separator: " ").map {Int($0)!}

let a = edge[0]
let b = edge[1]

graph[a].append(b)
graph[b].append(a)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전 중간에 변수 선언 없이 인덱스로 바로 넣어줬는데 마카로니씨처럼 선언해놓고 전달하는게 보기는 편한 것 같네요^^
전 아래처럼 짰슴다!

func getTree(_ n: Int) -> Tree {
    var tree = Array(repeating: [Int](), count: n + 1)
    
    for _ in 0..<n-1 {
        let edge = readLine()!.split(separator: " ").map { Int($0)! }
        
        // Tree = 양방향이라 양쪽 다연결해주기
        tree[edge[0]].append(edge[1])
        tree[edge[1]].append(edge[0])
    }
    
    return tree
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MuchanKim 옹 트리생성도 모듈화하는거 좋네용 무!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 우왕 주입식 교육 아주 좋습니다
쉬운 것부터 차근차근 . . ~

Comment on lines +26 to +37
func dfs(_ currentnode : Int){
visited[currentnode] = true

for next in graph[currentnode] {
if !visited[next] {
parent[next] = currentnode
dfs(next)
}
}
}

dfs(1)
Copy link
Collaborator

@MuchanKim MuchanKim Apr 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

교과서적인 코드 잘 보고 갑니다. 저도 완전 똑같이 풀었습니다 ㅎㅎ

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기본에 충실하기 !

Comment on lines +19 to +20
let a = edge[0]
let b = edge[1]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게도할 수 있고, 한줄로도 가능해요 !
취향차이긴한데, 공유합니다 !

let (a,b) = (edge[0],edge[1])

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 ~ 아주 깔꼼스
앞으로 이렇게 쓰는 것도 습관화 해볼게요 !

감쟈합니다 🥹

Comment on lines +26 to +35
func dfs(_ currentnode : Int){
visited[currentnode] = true

for next in graph[currentnode] {
if !visited[next] {
parent[next] = currentnode
dfs(next)
}
}
}
Copy link
Collaborator

@bishoe01 bishoe01 Apr 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

훨씬 깔끔하게 정석으로 푸셨네요.
괜히 막 처음에 복잡하게 생각하다보니까 다른쪽으로 풀었다가 돌아왔는데, 그래서 원래 복잡하게 풀어야하는지 알았어요.
전체코드는 코멘트로 달게요 !

Copy link
Collaborator

@bishoe01 bishoe01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수도코드

  1. edgeGraph : 간선그래프
  2. answerArr : 정답출력을 위한 각각 인덱스별 노드의 부모 저장소
  3. visited : contains로 판단하는 방문배열
  4. BFS로 푸는데 지난번 킨더가든 선생님의 방식을 착안해서 이번에는 queue를 index로 관리해줘봤습니다. queuePointer가 그 인덱스 관리 변수
  5. Head에 관한 내용은 주석에 나온대로 만약 인덱스 1에 [4,6]이 들어있다면 이제 4,6을 살펴볼때 answerArr에 <너의 부모는 1이야>라고 등록을 해주려하는데, 타고올라가기가 번거로운것같아서 튜플로 바꾸고 속성에 head를 추가했습니다.
import Foundation

func solution(_ N: Int, _ graph: [[Int]]) {
    var edgeGraph = Array(repeating: [Int](), count: N + 1)
    var answerArr = Array(repeating: 0, count: N + 1)
    var visited = Set<Int>()
    for line in graph {
        let (node1, node2) = (line[0], line[1])
        edgeGraph[node1].append(node2)
        edgeGraph[node2].append(node1)
    }

    var queue = [(head: 1, child: edgeGraph[1])] // 간선으로 놀아주는데, 만약 인덱스 1에 [4,6]이 들어있다면 이제 4,6을 살펴볼때 answerArr에 <너의 부모는 1이야>라고 등록을 해주려하는데, 타고올라가기가 번거로운것같아서 튜플로 바꾸고 속성에 head를 추가했어요
    visited.insert(1)
    var queuePointer = 0 // swift에서는 큐가 구현안되어있어서 pop,shift하는거 비효율적이니까 인덱스로 관리

//    print(edgeGraph)
    while queuePointer < queue.count {
        for node in queue[queuePointer].child {
            if !visited.contains(node) {
                queue.append((head: node, child: edgeGraph[node]))
                answerArr[node] = queue[queuePointer].head
                visited.insert(node)
            }
        }
        queuePointer += 1
    }
    // 출력 부분
    for i in 2 ..< N + 1 {
        print(answerArr[i])
    }
}

Copy link
Member

@giljihun giljihun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/* MARK: - SOLUTION
 
 <dfs풀이>
 1. tree, parent, visited 배열 만들기
 2. 입력
    * 런타임에러 고려 - 숫자가 아닌 경우, 한 줄에 여러 숫자가 들어온 경우
 3. dfs(1)부터 시작하여 방문노드 = true처리
 4. 해당 노드에 연결된 간선을 하나씩 접근하여 종착역을 찾아서 업데이트
 */

import Foundation

guard let N = Int(readLine() ?? "") else { exit(0) }

var tree = Array(repeating: [Int](), count: N+1)
var parent = Array(repeating: 0, count: N+1)
var visited = Array(repeating: false, count: N+1)

for _ in 0..<N-1 {
    guard let line = readLine() else { continue }
    let parts = line.split(separator: " ").compactMap { Int($0) }
    
    if parts.count == 2 {
        let firstNode = parts[0], secondNode = parts[1]
        tree[firstNode].append(secondNode)
        tree[secondNode].append(firstNode)
    } else { exit(0) }
}

func dfs(_ current: Int) {
    visited[current] = true
    for connectedNode in tree[current] {
        if !visited[connectedNode] {
            parent[connectedNode] = current
            dfs(connectedNode)
        }
    }
}

dfs(1)

(2..<N+1).forEach { print(parent[$0]) }

저는 이번 문제에선 런타임에러에 대한 PTSD로
조금 불필요할 수도 있는? 예외처리를 해주려고 노력했던거같아요
출력부분도 클로저로 깔끔하게 출력해봤습니다.
여러 리뷰를 하고 싶은데 입력부분, 출력문법 제외하면 그냥 똑같네요 ㅋㅋㅋ


  1. visited로 관리해주는건 무한 재귀에 빠질 수 있어서? 무조건 필수인 것 같아요.
  2. 그냥 문제마다 다르지 않나..? 싶은데 잘 모르겠습니다. 근데 크리티컬하진 않은 것 같아요 제 생각에!

Comment on lines +39 to +41
for i in 2...N {
print(parent[i])
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(2..<N+1).forEach { print(parent[$0]) }

요런식으로 앞에 범위를 정해주고 .forEach로 한줄로 출력할 수도 있슴다~!!!

.forEach { ... } 는 배열이나 범위 안의 각 원소를 하나씩 꺼내서 중괄호 { } 안에 있는 코드를 실행!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 쓰면 멋쟁이가 되는 '길'이네요
꿀 팁 감 사 하 옵 니 다 ~ ~

Copy link
Collaborator

@YooGyeongMo YooGyeongMo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dfs 문제 너무 좋았습니다 글로니. 코드가 엄청 깔끔하시군요.

Comment on lines +8 to +41
import Foundation

let N = Int(readLine()!)!

var graph = Array(repeating: [Int](), count: N+1)
var parent = Array(repeating:0, count: N+1)
var visited = Array(repeating: false, count: N+1)

for _ in 0..<N-1 {
let edge = readLine()!.split(separator: " ").map {Int($0)!}

let a = edge[0]
let b = edge[1]

graph[a].append(b)
graph[b].append(a)
}

func dfs(_ currentnode : Int){
visited[currentnode] = true

for next in graph[currentnode] {
if !visited[next] {
parent[next] = currentnode
dfs(next)
}
}
}

dfs(1)

for i in 2...N {
print(parent[i])
}
Copy link
Collaborator

@YooGyeongMo YooGyeongMo Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋군요 !

이번에 문법 연습한다고 Array 확장해서 컴팩트 맵을 만들어보고
튜플로 받아보고자 한번 시도해봤는데 통과되더군요 다행입니다 호호..

tree[a].append(b)
tree[b].append(a)

하고싶어서 했더니 되더군요 !

함수를 좀 분리해서 사용해봤더니 좋은거같아요 호호

//
//  부모의 트리.swift
//  알고리즘 연습
//
//  Created by Demian Yoo on 4/16/25.
//

/* TODO
1. 트리 구조 파악 -> 트리 루트 1, 양방향임
2. dfs 사용하면 될듯함 dfs(1)로 들어가서 깊이들어가서 빠져나오면서 그 위에 노드를 체크하고 뱉어주면됨.
3. 함수로 분할해보기
 
 */
import Foundation

let N = Int(readLine()!)!

var tree: [[Int]] = Array( repeating:[Int](), count: N+1 )
var parentArr: [Int] = Array( repeating:0, count: N+1 )
var visited = Array( repeating: false, count: N+1 )


// MARK - 함수 익스텐션 만들어보기
extension Array {
    func makeMap<T>(_ transform: (Element) -> T?) -> [T] {
        var result: [T] = []
        for element in self {
            if let value: T = transform(element) {
                result.append(value)
            }
        }
        return result
    }
    
    func asTuple() -> (Element, Element)? {
        guard self.count == 2 else { return nil }
        return (self[0], self[1])
    }
}

// MARK - 트리 만들기
func makeTree(_ loop: Int) {
    // loop-1번
    for _ in 0..<loop {
        // NOTE - 튜플로 들어감
        if let (a, b) = readLine()!.split(separator: " ").makeMap({ Int($0) }).asTuple() {
            tree[a].append(b)
            tree[b].append(a)
        }
    }
}

// MARK - dfs 부모찾기
func findParentNode(_ idx: Int) {
    visited[idx] = true
    
    for nextIdx in tree[idx] {
        if !visited[nextIdx] {
            parentArr[nextIdx] = idx
            findParentNode(nextIdx)
        }
    }
}

// MARK - 트리 출력하기
func printAnswer() {
    // 1번 노드 루트라서 표현 X
    for i in 2...N {
        print(parentArr[i])
    }
}

makeTree(N-1)
findParentNode(1)
printAnswer()

Copy link
Member

@alstjr7437 alstjr7437 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다들 이미 너무 완벽히 리뷰를 해주셨네요.. 할말이 없군요

코드도 다들 비슷한 것 같아서 저는 예전에 python으로 풀면서 bfs, dfs 연습했던거 참고했어요 ㅋㅋㅋ

import sys
sys.setrecursionlimit(10**8) #재귀 리미트
from collections import deque

n = int(input())

tree = {i:[] for i in range(1,n+1)}

for i in range(n-1):
    a, b = map(int, input().split())
    tree[a].append(b)
    tree[b].append(a)
# print(tree)

visited = [0] * (n+1)

def dfs(a):
    for i in tree[a]:
        if visited[i] == 0:
            visited[i] = a
            # print(visited, i, a)
            dfs(i)

def bfs():
    while queue:
        a = queue.popleft()
        for i in tree[a]:
            if visited[i] == 0:
                queue.append(i)
                visited[i] = a
                # print(visited, queue)


visited[1] = '어무이'

# dfs(1)

queue = deque()
queue.append(1)
bfs()


for i in range(2, n+1):
    print(visited[i])

@sep037 sep037 merged commit b78776a into main Apr 28, 2025
7 checks passed
@sep037 sep037 deleted the 2-sep037 branch April 28, 2025 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants