51mee - AI智能招聘平台Logo
模拟面试题目大全招聘中心会员专区

设计一个期货订单排序算法,用于撮合引擎,需支持时间优先、价格优先等规则,请分析其时间复杂度及空间复杂度。

广州期货交易所BO1.理学工学类专业难度:中等

答案

1) 【一句话结论】采用基于堆的优先队列,结合订单ID到堆位置的哈希表映射,支持时间优先(时间戳升序)和价格优先(买入价高、卖出价低优先),插入/删除及撤销操作时间复杂度均为O(log n),空间复杂度O(n),适用于高并发期货撮合引擎。

2) 【原理/概念讲解】解释订单类型与优先级规则:订单分为买入(Buy)和卖出(Sell),价格优先规则为买入按“价格越高(买方出价越高)优先”,卖出按“价格越低(卖方要价越低)优先”;时间优先规则统一为“时间戳升序,最早到先处理”。使用堆(完全二叉树,满足堆序性),插入新订单时通过“上浮”(最大堆/最小堆)调整位置,删除堆顶时替换堆顶元素并“下沉”调整,保证操作时间复杂度O(log n)。为支持撤销操作,维护一个哈希表(订单ID→堆位置),撤销时通过ID快速定位订单,用无效值替换堆顶元素并调整堆,避免遍历整个堆。类比:急救中心任务队列,紧急任务(时间早或优先级高)先处理,堆自动排序,插入后自动调整位置,撤销时直接替换堆顶,不影响其他订单。

3) 【对比与适用场景】

数据结构定义插入/删除时间复杂度空间复杂度适用场景注意点
数组排序(快排)顺序存储,排序后有序O(n log n)O(1)(原地)低并发,静态数据不支持动态插入删除,撤销操作需重新排序
平衡二叉搜索树(红黑树)自平衡树,支持动态插入删除O(log n)O(n)需频繁插入删除,有序访问高并发下响应时间可能比堆慢,因旋转操作
堆(带ID映射表)完全二叉树+哈希表(ID→位置)插入/删除O(log n),撤销O(log n)O(n)高并发订单撮合,优先级排序撤销通过堆顶替换+调整实现高效删除,需维护哈希表
跳表(无锁)分层链表,支持动态插入删除插入/删除O(log n),撤销O(log n)O(n)极高并发,无锁场景需额外内存,实现复杂

4) 【示例】
订单示例及撤销操作伪代码:

class OrderBook:
    def __init__(self):
        self.bids = MaxHeap()  # 买入(价格高优先)
        self.asks = MinHeap()  # 卖出(价格低优先)
        self.time_queue = MinHeap()  # 时间优先,按时间戳升序
        self.id_to_pos = {}  # 订单ID到堆位置的映射

    def add_order(self, order_type, price, time, qty, order_id):
        order = (price, time, qty, order_id)
        if order_type == "buy":
            self.bids.push((price, time, order))
            self.time_queue.push((time, order))
        else:
            self.asks.push((price, time, order))
            self.time_queue.push((time, order))
        self.id_to_pos[order_id] = (self.time_queue.size() - 1, self.bids.size() - 1 if order_type == "buy" else self.asks.size() - 1)

    def cancel_order(self, order_id, cancel_time):
        pos, bid_pos = self.id_to_pos.get(order_id)
        if pos is not None:
            self.time_queue.replace_top((cancel_time, None))  # 替换为无效值
            self.time_queue.sift_down(0)
            if bid_pos != -1:
                self.bids.replace_top((float('-inf'), None))  # 买入用最大堆
                self.bids.sift_down(0)
            else:
                self.asks.replace_top((float('inf'), None))  # 卖出用最小堆
                self.asks.sift_down(0)
            del self.id_to_pos[order_id]

    def match(self):
        while not self.bids.empty() and not self.asks.empty():
            bid_price, _, bid = self.bids.top()
            ask_price, _, ask = self.asks.top()
            if bid_price >= ask_price:
                self.bids.pop()
                self.asks.pop()

5) 【面试口播版答案】
面试官您好,设计期货订单排序算法时,核心是用堆结构结合订单ID映射表。具体来说,订单分为买入和卖出,价格优先规则是买入按价格高优先、卖出按价格低优先,时间优先按时间戳升序。用最大堆处理买入订单(价格高优先),最小堆处理卖出订单(价格低优先),同时维护一个时间队列(最小堆,时间早先处理)。为了支持撤销操作,我们维护一个哈希表,记录每个订单ID在时间队列和价格队列中的位置。当撤销订单时,通过ID快速定位,用无效值替换堆顶元素并调整堆,这样撤销操作的时间复杂度也是O(log n)。插入新订单时,先按时间戳插入时间队列,再根据类型插入价格队列,撮合时从价格队列取出最高买入价和最低卖出价匹配,成交后更新剩余订单。这种设计能高效处理高并发订单,满足时间优先和价格优先的规则。

6) 【追问清单】

  • 问:如何保证高并发下订单插入的线程安全?答:用读写锁(读多写少时)或细粒度锁(按订单类型锁),避免全局锁导致性能瓶颈。
  • 问:订单有市价、止损等复杂类型如何扩展?答:在堆中增加更多优先级维度(如价格+时间+订单类型),或用多级堆(时间堆+价格堆),按优先级维度分层排序。
  • 问:大量订单撤销时,内存回收效率如何?答:用内存池管理订单对象,减少分配开销;或按时间分片,只保留最近K条订单,避免内存碎片。
  • 问:时间优先和价格优先的冲突如何处理?答:优先时间优先,同时间戳订单按价格优先处理,避免优先级倒置。
  • 问:空间复杂度是否可以优化?答:用内存池管理订单对象,减少分配开销;或按时间分片,只保留最近K条订单。

7) 【常见坑/雷区】

  • 忽略订单ID映射表:撤销操作若遍历堆,导致时间复杂度O(n),应通过ID映射快速定位。
  • 未区分同时间戳订单的优先级:同时间戳订单应按价格优先(如买入价高优先),否则可能导致错误。
  • 未考虑并发锁粒度:全局锁导致高并发下性能下降,应采用细粒度锁或读写锁。
  • 优先级顺序错误:卖出订单价格低优先,但实际应为价格低(卖出价低)优先,易混淆。
  • 堆维护成本:频繁插入删除导致堆调整次数多,未考虑堆平衡策略(如斐波那契堆,复杂度更高)。
51mee.com致力于为招聘者提供最新、最全的招聘信息。AI智能解析岗位要求,聚合全网优质机会。
产品招聘中心面经会员专区简历解析Resume API
联系我们南京浅度求索科技有限公司admin@51mee.com
联系客服
51mee客服微信二维码 - 扫码添加客服获取帮助
© 2025 南京浅度求索科技有限公司. All rights reserved.
公安备案图标苏公网安备32010602012192号苏ICP备2025178433号-1