📜  门| GATE-CS-2016(套装2)|第 64 题(1)

📅  最后修改于: 2023-12-03 14:58:31.370000             🧑  作者: Mango

门| GATE-CS-2016(套装2)|第 64 题

本题是一道算法题,要求实现一个门控系统来控制进出门的权限并记录每个人的进出情况。这可以在许多实际场景中应用,如办公区门禁系统、学生宿舍门禁系统等。

题目描述

门控系统需要支持以下三个功能:

  1. 注册门禁卡:当一个人得到门禁卡时,需要将卡编号和卡持有人的ID号码注册到门禁系统中。
  2. 注销门禁卡:当一个人丢失或不再使用门禁卡时,需要将该卡编号从门禁系统中注销。
  3. 检查进出情况:门控系统需要记录每个人进出门的情况,并能够查询某个人最近一段时间内的进出记录。

门控系统应当支持并发使用,并能够正确地处理并发读写请求。

实现思路

该门控系统最主要的数据结构是一个哈希表,键为卡编号,值为该卡对应的人的ID号码和一张进出记录表。

进出记录表是一个有序的双向链表,每个节点包含进出状态(入门或出门)、时间戳和相应的卡编号和人ID号码。为了实现快速查询,记录表按时间戳从早到晚排序。同时,在每个人的记录表中维护两个指针,一个指向最早的进门记录节点,另一个指向最近的出门记录节点。这样,在查询某个人最近的进出记录时,只需要从最近的出门记录节点开始向前遍历链表,直到遇到最近的进门记录。

系统中涉及到的所有操作(注册、注销、进出门、查询记录)都需要加锁实现并发使用。锁可以粗粒度到整个门禁系统,也可以细粒度到每个单一卡的记录表。

复杂度分析

门控系统在注册卡、注销卡和进出门时需要进行哈希表的读写操作,时间复杂度为O(1)。查询某个人最近的进出记录时需要遍历链表,时间复杂度为O(k),其中k为该人在系统中的进出次数。由于链表按时间戳排序,因此在大多数情况下k不会很大,因此查询的时间复杂度也可以被视为O(1)。

代码示例
class GateSystem:

    def __init__(self):
        self.cards = {}
        self.lock = Lock()

    def register_card(self, card_id, person_id):
        with self.lock:
            if card_id in self.cards:
                raise ValueError('Card id already registered')
            self.cards[card_id] = {'person_id': person_id, 'records': LinkedList()}
    
    def unregister_card(self, card_id):
        with self.lock:
            if card_id not in self.cards:
                raise ValueError('Card id not registered')
            del self.cards[card_id]
    
    def enter(self, card_id):
        with self.lock:
            if card_id not in self.cards:
                raise ValueError('Card id not registered')
            card = self.cards[card_id]
            card['records'].add('in', time.time(), card_id, card['person_id'])
    
    def exit(self, card_id):
        with self.lock:
            if card_id not in self.cards:
                raise ValueError('Card id not registered')
            card = self.cards[card_id]
            card['records'].add('out', time.time(), card_id, card['person_id'])
    
    def get_records(self, person_id, time_span):
        with self.lock:
            records = []
            if person_id is None:
                for card in self.cards.values():
                    records.extend(card['records'].get_records(time_span))
            else:
                for card in self.cards.values():
                    if card['person_id'] == person_id:
                        records.extend(card['records'].get_records(time_span))
            return records


class LinkedList:

    class Node:
        def __init__(self, action, timestamp, card_id, person_id):
            self.action = action
            self.timestamp = timestamp
            self.card_id = card_id
            self.person_id = person_id
            self.next = None
            self.prev = None

    def __init__(self):
        self.head = None
        self.tail = None

    def add(self, action, timestamp, card_id, person_id):
        node = self.Node(action, timestamp, card_id, person_id)
        if self.head is None:
            self.head = self.tail = node
        else:
            node.prev = self.tail
            self.tail.next = node
            self.tail = node
    
    def get_records(self, time_span):
        records = []
        node = self.tail
        while node:
            if time.time() - node.timestamp > time_span:
                break
            records.append((node.timestamp, node.person_id, node.card_id, node.action))
            node = node.prev
        return records

以上是一个简单的Python实现,使用了线程锁保证并发安全。哈希表使用Python自带的字典实现,链表使用自定义的双向链表实现。为了简单起见,在记录表中保存了时间戳而非日期,同时默认使用time.time()获取当前时间戳。