返回

技术面试必备:算法与链表实战演练

前端

今夜算法时刻,让我们一起踏上征程,探索算法与链表的世界。从尾到头打印链表、返回倒数第 k 个节点、环形链表、K 个一组翻转链表、只出现一次的数字,这些经典题目将成为我们今晚的训练场。

从尾到头打印链表

从尾到头打印链表是一个经典的算法问题。链表是一种线性的数据结构,由一组节点组成,每个节点包含一个值和一个指向下一个节点的指针。从尾到头打印链表需要逆向遍历链表,将每个节点的值从后往前打印出来。

我们可以使用栈或递归来解决这个问题。使用栈时,我们将节点压入栈中,然后弹出栈顶节点并打印其值。使用递归时,我们可以将问题分解为更小的子问题,即打印链表的剩余部分,然后再打印当前节点的值。

# 使用栈
def print_list_from_tail_using_stack(head):
    stack = []
    while head:
        stack.append(head)
        head = head.next
    while stack:
        print(stack.pop().val)

# 使用递归
def print_list_from_tail_using_recursion(head):
    if head is None:
        return
    print_list_from_tail_using_recursion(head.next)
    print(head.val)

返回倒数第 k 个节点

返回倒数第 k 个节点也是一个常见的算法问题。与从尾到头打印链表类似,我们需要遍历链表,但这次我们只需要找到第 k 个节点的值。

我们可以使用双指针法来解决这个问题。我们使用两个指针,一个指向链表头,另一个指向链表尾。我们先让尾指针向前移动 k 步,然后让两个指针同时向前移动。当尾指针到达链表末尾时,头指针指向的节点就是倒数第 k 个节点。

def find_kth_from_tail(head, k):
    if head is None or k <= 0:
        return None
    fast = head
    slow = head
    for _ in range(k):
        fast = fast.next
    while fast is not None:
        fast = fast.next
        slow = slow.next
    return slow

环形链表

环形链表是指链表中存在一个环,即链表中某个节点的 next 指针指向了之前的某个节点。检测环形链表的一个简单方法是使用快慢指针。

我们使用两个指针,一个指向链表头,另一个指向链表头。我们让快指针每次移动两步,慢指针每次移动一步。如果链表中有环,则快指针最终会追上慢指针。

def has_cycle(head):
    if head is None or head.next is None:
        return False
    slow = head
    fast = head.next
    while slow != fast:
        if fast is None or fast.next is None:
            return False
        slow = slow.next
        fast = fast.next.next
    return True

K 个一组翻转链表

K 个一组翻转链表是指将链表中的节点每 k 个一组翻转。例如,对于链表 1->2->3->4->5,k=2 时,翻转后的链表为 2->1->4->3->5。

我们可以使用双指针和递归来解决这个问题。使用双指针时,我们使用两个指针,一个指向当前组的头节点,另一个指向当前组的尾节点。我们先将当前组的节点翻转,然后将尾节点的 next 指针指向下一组的头节点。使用递归时,我们可以将问题分解为更小的子问题,即翻转链表的剩余部分,然后翻转当前组的节点。

# 使用双指针
def reverse_k_group_using_two_pointers(head, k):
    if head is None or k <= 1:
        return head
    dummy = ListNode(0)
    dummy.next = head
    prev = dummy
    curr = head
    while curr:
        next_group_head = curr
        for _ in range(k - 1):
            next_group_head = next_group_head.next
            if next_group_head is None:
                return dummy.next
        next_group_tail = next_group_head.next
        next_group_head.next = None
        prev.next = reverse_list(curr)
        curr.next = next_group_tail
        prev = curr
        curr = next_group_tail
    return dummy.next

# 使用递归
def reverse_k_group_using_recursion(head, k):
    if head is None or k <= 1:
        return head
    curr = head
    for _ in range(k):
        if curr is None:
            return head
        curr = curr.next
    new_head = reverse_list(head, curr)
    head.next = reverse_k_group_using_recursion(curr, k)
    return new_head

只出现一次的数字

只出现一次的数字是指在一个数组中,除了一个元素之外,其他所有元素都出现了两次。找出这个只出现一次的元素。

我们可以使用异或运算来解决这个问题。异或运算的性质是,相同的数字异或为 0,不同的数字异或为 1。因此,我们可以遍历数组,将每个元素与结果进行异或运算。最终的结果就是只出现一次的元素。

def find_single_number(nums):
    result = 0
    for num in nums:
        result ^= num
    return result

结语

算法与链表是技术面试中的重要组成部分。通过解决这些经典题目,我们可以提高算法思维和链表操作技能。在今后的学习和工作中,这些知识和技能将为我们解决实际问题提供坚实的基础。