返回

算法巧解:数组与链表双管齐下,搞定连续中值

前端

连续中值算法详解:数组与链表双解法

在数据流分析和滑动窗口算法等场景中,连续中值计算至关重要。它可以实时反映数据流中值的变化趋势。本文将深入解析两种经典的连续中值算法:数组解法和链表解法,助你轻松掌握这道面试难题。

数组解法

原理:

数组解法利用排序数组的特性。每次新值插入时,通过二分法查找插入位置,保持数组有序。这样,数组中位数指针始终指向中值元素。

算法步骤:

  1. 初始化一个有序数组 arr
  2. 当收到新值 x 时:
    • 采用二分法查找 xarr 中的插入位置 idx
    • x 插入 arr 中的 idx 位置。
  3. 计算并返回数组 arr 的中值。

优缺点:

  • 插入和查找操作高效。
  • 删除操作较慢,需要重新排序数组。

代码示例:

import bisect

class Solution:
    def __init__(self):
        self.arr = []

    def insert(self, x):
        idx = bisect.bisect_left(self.arr, x)
        self.arr.insert(idx, x)

    def get_median(self):
        n = len(self.arr)
        if n % 2 == 0:
            return (self.arr[n // 2] + self.arr[n // 2 - 1]) / 2
        else:
            return self.arr[n // 2]

链表解法

原理:

链表解法使用双向链表,并维护一个中位数指针 mid。每次新值插入时,遍历链表找到合适位置插入,并更新 mid 指针指向中值元素。

算法步骤:

  1. 初始化一个双向链表 lst 和中位数指针 mid
  2. 当收到新值 x 时:
    • 遍历链表,找到第一个大于等于 x 的节点 node
    • 如果 node 为空,则将 x 插入链表尾部。
    • 否则,将 x 插入 node 之前。
  3. 更新中位数指针 mid
    • 如果链表长度为奇数,则 mid 指向中间节点。
    • 如果链表长度为偶数,则 mid 指向两个中间节点的左侧节点。
  4. 返回 mid 节点的值作为中值。

优缺点:

  • 插入和删除操作高效。
  • 查找操作较慢,需要遍历链表。

代码示例:

class Node:
    def __init__(self, val):
        self.val = val
        self.prev = None
        self.next = None

class Solution:
    def __init__(self):
        self.head = Node(float('inf'))
        self.tail = Node(float('inf'))
        self.head.next = self.tail
        self.tail.prev = self.head
        self.mid = self.head

    def insert(self, x):
        node = self.head
        while node.next.val < x:
            node = node.next
        new_node = Node(x)
        new_node.next = node.next
        node.next = new_node
        new_node.prev = node
        new_node.next.prev = new_node
        self.update_mid()

    def update_mid(self):
        n = self.get_length()
        if n % 2 == 1:
            self.mid = self.head.next
        else:
            self.mid = self.mid.next

    def get_length(self):
        node = self.head.next
        length = 0
        while node != self.tail:
            length += 1
            node = node.next
        return length

    def get_median(self):
        return self.mid.val

总结

数组解法和链表解法各有其特点:

  • 数组解法插入和查找高效,适合数据量较大且需要频繁查找中值的情况。
  • 链表解法插入和删除高效,适合数据流不断变化且需要频繁更新中值的情况。

根据实际场景需求,选择最合适的解法至关重要。

常见问题解答

  1. 数组解法的时间复杂度是多少?

    • 二分查找插入时间复杂度为 O(log n),因此整体时间复杂度为 O(n log n)。
  2. 链表解法的时间复杂度是多少?

    • 遍历链表和更新中位数指针时间复杂度为 O(n),因此整体时间复杂度为 O(n)。
  3. 哪种解法更节省空间?

    • 数组解法通常需要更多空间来存储排序数组,而链表解法只需要存储节点指针,因此链表解法更节省空间。
  4. 哪种解法在并行处理方面更有效?

    • 数组解法适合并行处理,因为可以将数组划分为多个子数组并同时处理。链表解法难以并行处理,因为更新中位数指针需要对整个链表进行遍历。
  5. 是否还有其他连续中值算法?

    • 除数组和链表解法外,还有其他算法,如堆、分治和滚动中值,各有其优缺点和适用场景。