πŸ’†πŸ»‍♂️ ν”Ό-μ—μŠ€/🐣 ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€

[μŠ€μœ„ν”„νŠΈ μ•Œκ³ λ¦¬μ¦˜] ν”„λ‘œκ·Έλž˜λ¨ΈμŠ€: ν‘œ νŽΈμ§‘

κ°œλ°œν•˜λŠ” ν›ˆμ΄ 2021. 10. 10. 17:13

문제

https://programmers.co.kr/learn/courses/30/lessons/81303

 

μ½”λ”©ν…ŒμŠ€νŠΈ μ—°μŠ΅ - ν‘œ νŽΈμ§‘

8 2 ["D 2","C","U 3","C","D 4","C","U 2","Z","Z"] "OOOOXOOO" 8 2 ["D 2","C","U 3","C","D 4","C","U 2","Z","Z","U 1","C"] "OOXOXOOO"

programmers.co.kr

[λ³Έ λ¬Έμ œλŠ” μ •ν™•μ„±κ³Ό νš¨μœ¨μ„± ν…ŒμŠ€νŠΈ 각각 μ μˆ˜κ°€ μžˆλŠ” λ¬Έμ œμž…λ‹ˆλ‹€.]

μ—…λ¬΄μš© μ†Œν”„νŠΈμ›¨μ–΄λ₯Ό κ°œλ°œν•˜λŠ” λ‹ˆλ‹ˆμ¦ˆμ›μŠ€μ˜ 인턴인 μ•™λͺ¬λ“œλŠ” λͺ…λ Ήμ–΄ 기반으둜 ν‘œμ˜ 행을 선택, μ‚­μ œ, λ³΅κ΅¬ν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λŠ” 과제λ₯Ό λ§‘μ•˜μŠ΅λ‹ˆλ‹€. μ„ΈλΆ€ μš”κ΅¬ 사항은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€

μœ„ κ·Έλ¦Όμ—μ„œ νŒŒλž€μƒ‰μœΌλ‘œ μΉ ν•΄μ§„ 칸은 ν˜„μž¬ μ„ νƒλœ 행을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€. 단, ν•œ λ²ˆμ— ν•œ ν–‰λ§Œ 선택할 수 있으며, ν‘œμ˜ λ²”μœ„(0ν–‰ ~ λ§ˆμ§€λ§‰ ν–‰)λ₯Ό λ²—μ–΄λ‚  수 μ—†μŠ΅λ‹ˆλ‹€. μ΄λ•Œ, λ‹€μŒκ³Ό 같은 λͺ…λ Ήμ–΄λ₯Ό μ΄μš©ν•˜μ—¬ ν‘œλ₯Ό νŽΈμ§‘ν•©λ‹ˆλ‹€.

  • "U X": ν˜„μž¬ μ„ νƒλœ ν–‰μ—μ„œ XμΉΈ μœ„μ— μžˆλŠ” 행을 μ„ νƒν•©λ‹ˆλ‹€.
  • "D X": ν˜„μž¬ μ„ νƒλœ ν–‰μ—μ„œ XμΉΈ μ•„λž˜μ— μžˆλŠ” 행을 μ„ νƒν•©λ‹ˆλ‹€.
  • "C" : ν˜„μž¬ μ„ νƒλœ 행을 μ‚­μ œν•œ ν›„, λ°”λ‘œ μ•„λž˜ 행을 μ„ νƒν•©λ‹ˆλ‹€. 단, μ‚­μ œλœ 행이 κ°€μž₯ λ§ˆμ§€λ§‰ 행인 경우 λ°”λ‘œ μœ— 행을 μ„ νƒν•©λ‹ˆλ‹€.
  • "Z" : κ°€μž₯ μ΅œκ·Όμ— μ‚­μ œλœ 행을 μ›λž˜λŒ€λ‘œ λ³΅κ΅¬ν•©λ‹ˆλ‹€. λ‹¨, ν˜„μž¬ μ„ νƒλœ 행은 λ°”λ€Œμ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄ μœ„ ν‘œμ—μ„œ "D 2"λ₯Ό μˆ˜ν–‰ν•  경우 μ•„λž˜ 그림의 μ™Όμͺ½μ²˜λŸΌ 4행이 μ„ νƒλ˜λ©°, "C"λ₯Ό μˆ˜ν–‰ν•˜λ©΄ μ„ νƒλœ 행을 μ‚­μ œν•˜κ³ , λ°”λ‘œ μ•„λž˜ ν–‰μ΄μ—ˆλ˜ "λ„€μ˜€"κ°€ 적힌 행을 μ„ νƒν•©λ‹ˆλ‹€(4행이 μ‚­μ œλ˜λ©΄μ„œ μ•„λž˜ 있던 행듀이 ν•˜λ‚˜μ”© λ°€λ € 올라였고, μˆ˜μ •λœ ν‘œμ—μ„œ λ‹€μ‹œ 4행을 μ„ νƒν•˜λŠ” 것과 λ™μΌν•©λ‹ˆλ‹€).

λ‹€μŒμœΌλ‘œ "U 3"을 μˆ˜ν–‰ν•œ λ‹€μŒ "C"λ₯Ό μˆ˜ν–‰ν•œ ν›„μ˜ ν‘œ μƒνƒœλŠ” μ•„λž˜ κ·Έλ¦Όκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

λ‹€μŒμœΌλ‘œ "D 4"λ₯Ό μˆ˜ν–‰ν•œ λ‹€μŒ "C"λ₯Ό μˆ˜ν–‰ν•œ ν›„μ˜ ν‘œ μƒνƒœλŠ” μ•„λž˜ κ·Έλ¦Όκ³Ό κ°™μŠ΅λ‹ˆλ‹€. 5행이 ν‘œμ˜ λ§ˆμ§€λ§‰ ν–‰ μ΄λ―€λ‘œ, 이 경우 λ°”λ‘œ μœ— 행을 μ„ νƒν•˜λŠ” 점에 μ£Όμ˜ν•©λ‹ˆλ‹€.

λ‹€μŒμœΌλ‘œ "U 2"λ₯Ό μˆ˜ν–‰ν•˜λ©΄ ν˜„μž¬ μ„ νƒλœ 행은 2행이 λ©λ‹ˆλ‹€.

μœ„ μƒνƒœμ—μ„œ "Z"λ₯Ό μˆ˜ν–‰ν•  경우 κ°€μž₯ μ΅œκ·Όμ— 제거된 "라이언"이 적힌 행이 μ›λž˜λŒ€λ‘œ λ³΅κ΅¬λ©λ‹ˆλ‹€.

λ‹€μ‹œν•œλ²ˆ "Z"λ₯Ό μˆ˜ν–‰ν•˜λ©΄ κ·Έ λ‹€μŒμœΌλ‘œ μ΅œκ·Όμ— 제거된 "콘"이 적힌 행이 μ›λž˜λŒ€λ‘œ λ³΅κ΅¬λ©λ‹ˆλ‹€. μ΄λ•Œ, ν˜„μž¬ μ„ νƒλœ 행은 λ°”λ€Œμ§€ μ•ŠλŠ” 점에 μ£Όμ˜ν•˜μ„Έμš”.

μ΄λ•Œ, μ΅œμ’… ν‘œμ˜ μƒνƒœμ™€ 처음 μ£Όμ–΄μ§„ ν‘œμ˜ μƒνƒœλ₯Ό λΉ„κ΅ν•˜μ—¬ μ‚­μ œλ˜μ§€ μ•Šμ€ 행은 "O", μ‚­μ œλœ 행은 "X"둜 ν‘œμ‹œν•˜λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

처음 ν‘œμ˜ ν–‰ 개수λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ •μˆ˜ n, μ²˜μŒμ— μ„ νƒλœ ν–‰μ˜ μœ„μΉ˜λ₯Ό λ‚˜νƒ€λ‚΄λŠ” μ •μˆ˜ k, μˆ˜ν–‰ν•œ λͺ…령어듀이 λ‹΄κΈ΄ λ¬Έμžμ—΄ λ°°μ—΄ cmdκ°€ λ§€κ°œλ³€μˆ˜λ‘œ μ£Όμ–΄μ§ˆ λ•Œ, λͺ¨λ“  λͺ…λ Ήμ–΄λ₯Ό μˆ˜ν–‰ν•œ ν›„ ν‘œμ˜ μƒνƒœμ™€ 처음 μ£Όμ–΄μ§„ ν‘œμ˜ μƒνƒœλ₯Ό λΉ„κ΅ν•˜μ—¬ μ‚­μ œλ˜μ§€ μ•Šμ€ 행은 O, μ‚­μ œλœ 행은 X둜 ν‘œμ‹œν•˜μ—¬ λ¬Έμžμ—΄ ν˜•νƒœλ‘œ return ν•˜λ„λ‘ solution ν•¨μˆ˜λ₯Ό μ™„μ„±ν•΄μ£Όμ„Έμš”.

풀이

νš¨μœ¨μ„±κΉŒμ§€ ν†΅κ³Όν•˜κΈ°μ— μ–΄λ €μš΄ λ¬Έμ œμ—ΏμŠ΅λ‹ˆλ‹€γ…  λ‹€ ν‘ΈλŠ”λ° 2μ‹œκ°„μ€ 걸린것 κ°™λ„€μš”.. 

 

이 문제λ₯Ό νš¨μœ¨μ„±κΉŒμ§€ ν†΅κ³Όν•˜λ €λ©΄ μ ˆλŒ€λ‘œ O(N) μ΄μƒμ˜ μ‹œκ°„λ³΅μž‘λ„κ°€ λ°œμƒν•˜λ©΄ μ•ˆλ©λ‹ˆλ‹€. κ·Έλž˜μ„œ μ–‘λ°©ν–₯ λ§ν¬λ“œ 리슀트λ₯Ό μ‚¬μš©ν•΄μ•Όν•©λ‹ˆλ‹€.

그리고 λ™μ‹œμ— 기쑴의 λ§ν¬λ“œ λ¦¬μŠ€νŠΈμ™€λŠ” 쑰금 λ‹€λ₯΄κ²Œ κ΅¬μ„±ν•΄μ•Όν•˜λŠ” 뢀뢄듀이 μžˆμŠ΅λ‹ˆλ‹€.

 

Node ꡬ쑰

λ¨Όμ €, 각 λ…Έλ“œκ°€ μžμ‹ μ˜ ν‘œ 인덱슀 정보λ₯Ό κΈ°μ–΅ν•˜κ³  μžˆμ–΄μ•Όν•©λ‹ˆλ‹€. λ§ˆμ§€λ§‰μ— 졜초의 ν‘œμ—μ„œ 달라진 λΆ€λΆ„λ§Œμ„ ν‘œμ‹œν•΄μ•Όν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

λ”°λΌμ„œ λ…Έλ“œ μžλ£Œκ΅¬μ‘°λŠ” λ‹€μŒμ²˜λŸΌ ꡬ성할 수 μžˆμŠ΅λ‹ˆλ‹€.

 

class Node {
    var data: Int?
    var next: Node?
    var prev: Node?
    
    init(data: Int, next: Node?, prev: Node?) {
        self.data = data
        self.next = next
        self.prev = prev
    }
}

 

dataμ—λŠ” 인덱슀λ₯Ό λ„£κ³ , next와 prev에 μžμ‹ μ˜ λ‹€μŒ, 이전 λ…Έλ“œμ˜ 참쑰값을 μ €μž₯ν•©λ‹ˆλ‹€. 

 

λ§ν¬λ“œλ¦¬μŠ€νŠΈ

이제 λ§ν¬λ“œλ¦¬μŠ€νŠΈλ₯Ό κ΅¬ν˜„ν•΄μ•Όν•˜λŠ”λ°μš”, λ§ν¬λ“œλ¦¬μŠ€νŠΈλŠ” λ‹€μŒκ³Ό 같은 정보λ₯Ό κ°€μ§€κ³  μžˆμ–΄μ•Όν•©λ‹ˆλ‹€.

  • μ‚­μ œλœ ν–‰μ˜ 정보 (isDeleted)
  • κ°€μž₯ μ•ž λ…Έλ“œμ˜ 포인터(head)
  • κ°€μž₯ λ§ˆμ§€λ§‰ λ…Έλ“œμ˜ 포인터(tail)
  • ν˜„μž¬ 가리킀고 μžˆλŠ” λ…Έλ“œ(cursor)

그리고 이 λ¬Έμ œμ—μ„œ μ‚¬μš©ν•˜λŠ” 연산은 총 4 μ’…λ₯˜μž…λ‹ˆλ‹€. 

  • μƒˆλ‘œμš΄ λ…Έλ“œ μ‚½μž…ν•˜κΈ°
  • ν˜„μž¬ 가리킀고 μžˆλŠ” λ…Έλ“œλ₯Ό μ‚­μ œν•˜κΈ° 
  • μ‚­μ œ ν–ˆλ˜ λ…Έλ“œλ₯Ό μ›λž˜μžλ¦¬μ— λ³΅μ›ν•˜κΈ°
  • ν˜„μž¬ 가리킀고 μžˆλŠ” λ…Έλ“œλ₯Ό λ³€κ²½ν•˜κΈ° 

 

λ…Έλ“œ μ‚½μž…

λ§ν¬λ“œλ¦¬μŠ€νŠΈμ—μ„œ κ°€μž₯ μ•žμ„ λ‚˜νƒ€λ‚΄λŠ” headλ§Œμ„ μ‚¬μš©ν•˜λ©΄ μƒˆλ‘œμš΄ 데이터λ₯Ό 넣을 λ•Œλ§ˆλ‹€ κ°€μž₯ λ§ˆμ§€λ§‰ λ…Έλ“œκΉŒμ§€ νƒμƒ‰ν•΄μ•Όν•˜λŠ” O(N) μ‹œκ°„λ³΅μž‘λ„κ°€ λ°œμƒν•˜κΈ° λ•Œλ¬Έμ— tailμ΄λΌλŠ” 포인터λ₯Ό μ‚¬μš©ν•΄μ„œ 항상 λ§ν¬λ“œλ¦¬μŠ€νŠΈμ˜ λ§ˆμ§€λ§‰μ„ 가리킀도둝 ν•˜κ³  μƒˆλ‘œμš΄ λ…Έλ“œλŠ” tail의 뒀에 λΆ™μ—¬μ£Όκ³  tail을 μ—…λ°μ΄νŠΈ ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ§„ν–‰ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ‚½μž…μ—°μ‚°μ„ O(1)λ§Œμ— 끝낼 수 μžˆμŠ΅λ‹ˆλ‹€.

 

mutating func append(data: Int, isInitCursor: Bool) {
        let node = Node(data: data, next: nil, prev: nil)
        if isInitCursor { cursor = node }
        
        if head == nil {
            head = node
            tail = node
        } else {
            node.prev = tail
            tail?.next = node
            tail = node
        }
        isDeleted.append(false)
    }

 

λ…Έλ“œ μ‚­μ œ

λ…Έλ“œμ˜ μ‚­μ œλŠ” μ–΄λ–€ νŠΉμ •ν•œ λ…Έλ“œλ₯Ό μ‚­μ œν•˜μ§€ μ•Šκ³ , ν˜„μž¬ 가리킀고 μžˆλŠ” λ…Έλ“œλ₯Ό μ‚­μ œν•˜λŠ” κ²ƒμœΌλ‘œ 연산을 μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ λͺ¨λ“  λ…Έλ“œλ₯Ό νƒμƒ‰ν•˜μ§€ μ•Šκ³  cursor에 μ €μž₯된 참쑰값을 μ°Ύμ•„κ°€ ν•΄λ‹Ή λ…Έλ“œμ˜ μ΄μ „λ…Έλ“œμ™€ λ‹€μŒλ…Έλ“œλ₯Ό μ—°κ²°ν•΄μ£ΌλŠ” κ²ƒμœΌλ‘œ μ΄λ²ˆμ—λ„ O(1)λ§Œμ— μ‚­μ œ 연산을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. 그리고 μ‚­μ œκ°€ λλ‚œ μ΄ν›„μ—λŠ” ν•΄λ‹Ή λ…Έλ“œκ°€ κ°€μ§€κ³  있던 인덱슀 값을 μ‚¬μš©ν•˜μ—¬ ν•΄λ‹Ή 행이 μ‚­μ œλ˜μ—ˆμŒμ„ μ €μž₯ν•©λ‹ˆλ‹€.

 

mutating func remove() -> Node? {
        let delNode = cursor
        
        cursor?.next?.prev = cursor?.prev
        cursor?.prev?.next = cursor?.next
        cursor = delNode?.next == nil ? delNode?.prev : delNode?.next
        
        isDeleted[delNode!.data!] = true
        return delNode
    }

 

λ…Έλ“œ 볡원 

λ…Έλ“œμ˜ 볡원은 μ•„μ£Ό κ°„λ‹¨ν•˜κ²Œ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ³΅μ›ν•˜λ €κ³  ν•˜λŠ” λ…Έλ“œκ°€ κ°€μ§€κ³  있던 μ΄μ „λ…Έλ“œμ™€ λ‹€μŒλ…Έλ“œμ˜ 참쑰값을 κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜μ—¬ λ³΅μ›ν•΄μ€λ‹ˆλ‹€. 그리고 μ‚­μ œλ˜μ—ˆλ˜ λ…Έλ“œκ°€ λ‹€μ‹œ λ³΅μ›λ˜μ—ˆμŒμ„ μ €μž₯ν•©λ‹ˆλ‹€.

 

    mutating func restore(node: Node?) {
        node?.prev?.next = node
        node?.next?.prev = node
        isDeleted[node!.data!] = false
    }

 

μ»€μ„œ 이동 

μ»€μ„œλ₯Ό 이동할 λ•ŒλŠ” μ£Όμ–΄μ§€λŠ” κ°’λ§ŒνΌ 포인터λ₯Ό μ΄λ™μ‹œμΌœμ€λ‹ˆλ‹€. μ–‘λ°©ν–₯ λ§ν¬λ“œλ¦¬μŠ€νŠΈμ΄κΈ° λ•Œλ¬Έμ— μœ„λ‘œ μ˜¬λΌκ°ˆλ•ŒλŠ” 이전 λ…Έλ“œλ“€μ„ νƒμƒ‰ν•˜κ³ , λ‚΄λ €κ°ˆ λ•ŒλŠ” λ‹€μŒλ…Έλ“œλ“€μ„ νƒμƒ‰ν•©λ‹ˆλ‹€. λ¬Έμ œμ—μ„œ λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜λŠ” 이동은 μž…λ ₯λ˜μ§€ μ•ŠλŠ”λ‹€κ³  μ£Όμ–΄μ ΈμžˆκΈ° λ•Œλ¬Έμ— λ”°λ‘œ μ˜ˆμ™Έμ²˜λ¦¬λ₯Ό ν•˜μ§€ μ•Šμ•„λ„ λ©λ‹ˆλ‹€. 이 연산은 μ£Όμ–΄μ§€λŠ” 이동값 x만큼의 O(x) 연산이 μˆ˜ν–‰λ©λ‹ˆλ‹€.

 

    mutating func moveUp(to amount: Int) {
        for _ in 0..<amount {
            cursor = cursor?.prev
        }
    }
    
    mutating func moveDown(to amount: Int) {
        for _ in 0..<amount {
            cursor = cursor?.next
        }
    }

 

λ§ˆμ§€λ§‰μœΌλ‘œ λͺ¨λ“  연산을 μˆ˜ν–‰ν•œ 뒀에도 μ‚­μ œλ˜μ–΄ μžˆλŠ” λ…Έλ“œλ“€μ„ κ΅¬λΆ„ν•΄μ„œ λ¬Έμžμ—΄λ‘œ λ§Œλ“€μ–΄ λ°˜ν™˜ν•˜λ©΄ ν•΄κ²°λ©λ‹ˆλ‹€!

 

return table.isDeleted.map({ $0 ? "X" : "O" }).joined(separator: "")

 

카카였 인턴십 μ½”ν…Œμ—μ„œ 이 문제λ₯Ό ν’€λ‹€κ°€ μ‹œκ°„μ΄ μ’…λ£Œλ˜μ—ˆλŠ”λ°, μ΄λ²ˆμ—λ„ μ—­μ‹œ μ—„μ²­ λ§Žμ€ μ‹œκ°„μ΄ κ±Έλ ΈμŠ΅λ‹ˆλ‹€. κ·Έλž˜λ„ 덕뢄에 λ§ν¬λ“œ λ¦¬μŠ€νŠΈλ„ λ‹€μ‹œ κ΅¬ν˜„ν•΄λ³Ό 수 있고 λ§Žμ€ 곡뢀가 λ˜μ—ˆμŠ΅λ‹ˆλ‹€!

 

μ½”λ“œ