《剑指Offer》阅读笔记(swift):链表篇(9题)

728 阅读3分钟

我的Github地址

小码哥《恋上数据结构与算法》笔记

极客时间《iOS开发高手课》笔记

iOS大厂面试高频算法题总结

iOS面试资料汇总

  • 链表(9道):
    • 剑指Offer(6):从尾到头打印链表
    • 剑指Offer(18):删除链表的节点
    • 剑指Offer(18):删除链表中重复的节点
    • 剑指Offer(22):链表中倒数第k个结点
    • 剑指Offer(23):链表中环的入口节点
    • 剑指Offer(24):反转链表
    • 剑指Offer(25):合并两个排序的链表
    • 剑指Offer(35):复杂链表的复制
    • 剑指Offer(52):两个链表的第一个公共节点

面试题6:从尾到头打印链表

题目一

思路一:栈

代码

    func reversePrint(_ head: ListNode?) -> [Int] {
        var result = [Int]()
        var node = head
        while node != nil {
            result.append(node!.val)
            node = node!.next
        }
        result.reverse()
        return result
    }

思路二:递归

代码

    func reversePrint(_ head: ListNode?) -> [Int] {
        guard let head = head else {
            return [Int]()
        }
        var list = reversePrint(head.next)
        list.append(head.val)
        return list
    }

面试题18:删除链表的节点

题目一

思路一

代码

    func deleteNode(_ head: ListNode?, _ val: Int) -> ListNode? {
        var next = head?.next
        var cur = head
        
        if cur?.val == val {
            cur = cur?.next
            return cur
        }
        
        while next != nil {
            if next?.val == val {
                cur?.next = next?.next
                return head
            } else {
                cur = next
                next = next?.next
            }
        }
        return head
    }

思路二

题目二:删除链表中重复的节点

思路一

代码

    func deleteDuplicates(_ head: ListNode?) -> ListNode? {
        
        let dummy = ListNode(-1)
        var newHead = head
        dummy.next = newHead
        
        while newHead != nil {
            if newHead?.val == newHead?.next?.val {
                newHead?.next = newHead?.next?.next
            } else {
                newHead = newHead?.next
            }
        }
        
        return dummy.next
    }

面试题22:链表中倒数第k个结点

题目一

思路一

思路二

代码

    func getKthFromEnd(_ head: ListNode?, _ k: Int) -> ListNode? {
        // 1. 常规解法
        // var list = [ListNode]()
        // var tempHead = head
        // while tempHead != nil {
        //     list.append(tempHead!)
        //     tempHead = tempHead?.next
        // }
        
        // return list[list.count-k]

        // 2. 快慢
        var fast : ListNode? = head
        var slow : ListNode? = head
        var tk = k
        while tk > 0 {
            fast = fast?.next
            tk -= 1
        }
        
        while fast != nil {
            fast = fast?.next
            slow = slow?.next
        }
        
        return slow
    }

相关题目

举一反三

面试题23:链表中环的入口节点

题目一

思路一

代码

func detectCycle(_ head: ListNode?) -> ListNode? {
    let newHead = ListNode.init(-1)
    newHead.next = head
    
    // 1. 声明快慢指针
    var slow: ListNode? = newHead
    var fast: ListNode? = newHead
    
    // 2. 快慢指针开始移动
    while fast != nil {
        slow = slow?.next
        fast = fast?.next?.next
        
        // 3. 找到环,重置慢指针
        if slow === fast {
            slow = newHead
            // 4. 快慢指针一起移动,找到环
            while slow !== fast {
                slow = slow?.next
                fast = fast?.next
            }
            return slow
        }
    }
    return nil
}

面试题24:反转链表

题目一

思路一

代码

func reverseList(_ head: ListNode?) -> ListNode? {
    
    var newHead: ListNode? = nil
    var cur = head
    
    while cur != nil {
        // 1 记录即将断开的节点
        let tmp = cur!.next
        // 2 翻转
        cur!.next = newHead
        // 3 重制
        newHead = cur!
        cur = tmp
    }
    return newHead
}

思路二:递归

代码

    func reverseList(_ head: ListNode?) -> ListNode? {
      if head == nil || head?.next == nil {
        return head
      } else {
        //! 前面反转好的
        let p = reverseList(head?.next)
        //! 当前结点的下一个结点指向自己
        head?.next?.next = head
        //! 自己的 next 置为nil
        head?.next = nil
        return p
      }
    }

面试题25:合并两个排序的链表

题目一

思路一

代码

    func mergeTwoLists(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
    let newList = ListNode.init(-1)
    var cur = newList
    var l1 = l1
    var l2 = l2
    
    while l1 != nil && l2 != nil {
        if l1!.val > l2!.val {
            cur.next = l2
            l2 = l2?.next
        } else {
            cur.next = l1
            l1 = l1?.next
        }
        cur = cur.next!
    }
    cur.next = l1 ?? l2
    return newList.next
    }

思路二:递归

代码

    func mergeTwoLists(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? {
      if l1 == nil {
        return l2
      } else if l2 == nil {
        return l1
      } else if l1!.val < l2!.val {
        l1?.next = mergeTwoLists(l1?.next, l2)
        return l1
      } else {
        l2?.next = mergeTwoLists(l1, l2?.next)
        return l2
      }
    }

面试题35:复杂链表的复制

题目一

思路一:哈希表

面试题52:两个链表的第一个公共节点

题目一

思路一

思路二

思路三

思路四

  • 不管相不相交,当currentA拼接B的时候,currentB拼接A的时候,他们能同时走到对方的尾节点,如果不相交,此时都为空,跳出循环
  • 如果相交,那么我们逆推,既然能在尾节点相遇,那么尾节点的前继节点也是相遇,直到我们逆推到第一个相交点。

代码

func getIntersectionNode(_ headA: ListNode?, _ headB: ListNode?) -> ListNode? {
    var A = headA
    var B = headB
    // 1. A和B要么相等,要么同时为nil,===表示对象相等。
    while A !== B {
        A = A == nil ? headB : A!.next
        B = B == nil ? headA : B!.next
    }
    return A
}