알고리즘/알고리즘 문제풀이
BinaryTree_Leetcode_236. Lowest Common Ancestor of a Binary Tree
Downey
2025. 4. 3. 15:47
문제접근
- DFS를 사용하여 p, q를 탐색
- 탐색하며 지나온 TreeNode를 배열로 기록
- p,q를 찾았다면, 각자 지나온 기록 Treenode 배열에서 겹치는 마지막 element탐색
- 탐색 결과 반환
정답코드
성능이 처참하다..
831msBeats5.74%
더보기
/**
* Definition for a binary tree node.
* public class TreeNode {
* public var val: Int
* public var left: TreeNode?
* public var right: TreeNode?
* public init(_ val: Int) {
* self.val = val
* self.left = nil
* self.right = nil
* }
* }
*/
class Solution {
func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? {
guard
let root = root,
let p = p,
let q = q
else { return nil }
var qMap = [TreeNode]()
var pMap = [TreeNode]()
func dfs(_ root: TreeNode?, _ p: TreeNode, _ q: TreeNode, _ history: [TreeNode]) {
guard let root else { return }
var newHistory = history
newHistory.append(root)
if root.val == p.val {
pMap = newHistory
}
if root.val == q.val {
qMap = newHistory
}
dfs(root.left, p, q, newHistory)
dfs(root.right, p, q, newHistory)
}
dfs(root, p, q, [])
let common = qMap.lastIndex(where: { q in
let result:Bool = pMap.contains(where: { q.val == $0.val})
return result
})
if let common {
return qMap[common]
} else{
return nil
}
}
}
모범코드
1. 32ms가 걸리는, 대중적인 코드
더보기
class Solution {
func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? {
guard let root = root else { return nil }
if root === p || root === q {
return root
}
let left = lowestCommonAncestor(root.left, p, q)
let right = lowestCommonAncestor(root.right, p, q)
if left != nil && right != nil {
return root
}
return left != nil ? left : right
}
}
2. 상위 0.2%의 13ms 코드
더보기
class Solution {
func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? {
guard let root = root else { return nil }
var stack: [(TreeNode, NodeState)] = [(root, .bothPending)]
var oneNodeFound = false
var lcaIndex = -1
while !stack.isEmpty {
var (node, state) = stack.removeLast()
if state == .bothPending {
// Before traversing children, check if this is one of the target nodes
if node === p || node === q {
if oneNodeFound {
return stack[lcaIndex].0 // LCA is at lcaIndex in stack
}
oneNodeFound = true
lcaIndex = stack.count // Store LCA candidate index
}
// Push back with updated state and process left child
stack.append((node, .leftDone))
if let left = node.left {
stack.append((left, .bothPending))
}
}
else if state == .leftDone {
// Process right child
stack.append((node, .bothDone))
if let right = node.right {
stack.append((right, .bothPending))
}
}
else {
// Node fully processed, check if we need to update LCA index
if oneNodeFound && lcaIndex == stack.count {
lcaIndex -= 1 // Reduce LCA candidate index
}
}
}
return nil // Should never reach here
}
}
enum NodeState {
case bothPending
case leftDone
case bothDone
}
/*
Iterative DFS + parent tracking
Time: O(n)
Space: O(n)
--------------------------------------------------------------------
Step 1: Map every node to it's parent
Step 2: Iterate over every node from p, to the root, and add each node to a set
Step 3: Iterate over every node from q, to the root, and check the set
The first node found that is common between both sets is the LCA
*/
재학습 코드
모범코드 1을 이해하고 내가 작성한 코드 -> 24ms
코드흐름:
- let left, let right에서 dfs가 진행
- 최하단 노드까지 진행, 최하단 노드의 left, right는 존재하지 않으므로, left, right가 nil
- 둘 다 nil이므로, if 구문의 조건을 만족하는 경우가 없어서 nil 반환
- 이를 반복하다 p or q와 동일한 instance가 탐색됨, 해당 instance( root ) 반환
- left or right에 반환된 값이 하나만 존재하면 상위 노드로 해당 값 반환
- 반복되다가, left, right가 둘 다 존재하는 경우 발생, left, right 둘 다 존재하면 해당 노드가 LCA, 그러므로 root를 반환
- 6 경우가 발생했다면, 6경우의 상위 노드의 left or right에 LCA가 반환 될 것, 그 때, 다른 한 쪽에서는 nil이 반환됨
- 그러면 left or right가 반복 반환되며 정답을 반환
더보기
class Solution {
func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? {
guard let root else { return nil }
if root === q || root === p {
return root
}
let left = lowestCommonAncestor(root.left, p, q)
let right = lowestCommonAncestor(root.right, p, q)
if left != nil && right != nil {
return root
}
if let left {
return left
}
if let right {
return right
}
return nil
}
}