【叫号系统】Redis ZSet结构

        本文记录一下ZSet的使用心得,使用实例在【叫号系统】中,是一个简单的小系统。

【叫号系统】:用户来到考试现场进行签到,签到成功的用户进入“等待队列”。

考试科目分为:学科一、学科二、学科三、学科四。

考试场地分为:多个教室,多台电脑(有不同型号,每个型号对应不同科目,一个科目可能有两种型号)。

此时将电脑分为 两个队列:空闲队列、已分配队列。

叫号系统自动从队列中第一个用户进行分配电脑(电脑有空闲情况),用户考完试结束使用设备重新进入等待队列,直到四个学科考完。

                

由于叫号系统的数据频繁更新,在此使用redis对数据进行管理和缓存,根据需求需要存储的数据分为:用户等待、电脑空闲或已分配;

以下通过详细介绍数据结构的具体设计与实现,完整代码放于文末;

一、ZSet介绍

        ZSet 是有序的 集合,存储格式为 ( key , value, score)

  •  key: redis的关键字           
  • value: 值,由于是集合,所以重复的数据会被覆盖。
  • score:正常理解是分数,按照分数大小进行排序, 可以使用 当前时间戳 System.currentTimeMillis(),按照进入队列时间进行排序。

  value直接存储bean实例,根据值删除会找不到此对象。也许是redis序列化的问题 。可以使用JSON字符串格式存储, 同时取出时使用JSON.parse()转化为实体对象。 

二、等待队列

用户签到后先进入等待队列,等待队列使用redis的zset数据结构,根据进入的时间顺序进行排列。

前戏:

1、将等待队列封装为 ExamWaitListUtils.java 工具类,方便使用 。

        使用 @Component  注解 使 springboot 自动注入到容器中。

        再注入redisTemplate 对象。

2、redis的key值使用 “EXAM_WAIT_LIST” 与 “schoolId ” 进行拼接。

  • EXAM_WAIT_LIST : 自定义的字符串,封装再SocketCommon.java类中
  • schoolId:  考场学校的id,用来区分在哪所学校进行考试。

1、入队

通过 redisTemplate.opsForZSet()  获取ZSet对象。

调用add()  方法添加数据。

2、获取列表

1)、获取数据:

ZSet有几种获取集合数据的方法,在此使用其中两种,正序 和 逆序 获取。

  • 正序:range()
  • 逆序:reserserange()

range(key, start, end) 方法获取redis 数据拥有三个参数,start是数据列表的起始位置,end是数据列表的结束位置。用数学的区间表示就是:[start, end]  包含。

2)、实现分页:

通过[ start, end] 区间,可以计算分页。

分页规则: 输入当前页 pageCount 和 页面大小 pageSize, 获取到当前页的pageSize条数据。如果pageSize<=0 ,则获取列表所有数据。

 将pageCount 和 pageSize 转化为start 和 end 。计算方式如下。

int pageCount = page.getPageCount() <= 1 ? 1 : page.getPageCount();
int pageSize = page.getPageSize() <= 0 ? 0 : page.getPageSize();
//开始位置: 从1开始
int start = (pageCount - 1) * pageSize;
// 结束位置: 如果页面大小 = 0 时 获取所有数据, 包含当前位置
int end = pageSize == 0 ? -1 : pageCount * page.getPageSize() - 1;

 如果需要实现 输入查询条件筛选数据,只能将所有数据获取出来,筛选结束再进行分页。(redis没有实现跟mysql一样可以where查找数据) 

3、删除列表数据

redis的zset结构也有几种删除数据方法 

  • remove(key, value): 根据值vlue删除数据,这里由于之前使用JSON字符串存储bean对象,所以删除时也要先把bean对象转化为JSON字符串。
  • removeRange(key, start, end) : 根据区间[ statr, end] 范围删除数据,可以队列删除首个数据 [0,0]

4、 获取队列大小

 可以通过redis 的size(key) 方法,获取队列中有多少条数据。

     /**
     * 等待队列大小
     *
     * @return 返回 ExamWaitQueue
     */
    public Long size(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        return zSetRedis.size(SocketCommon.EXAM_WAIT_LIST + schoolId);
    }

二、空闲与已分配队列

设计思路,

  • 设备初始化: 将可用的设备都放入空闲队列。
  • 分配设备: 从等待队列中随机分配设备

具体使用与zset的息息相关,需要注意的就是,拿出空闲队列设备放入已分配设备中时,需要删除空闲设备中的旧数据。

三、完整代码

封装等待队列工具类(ExamWaitListUtils.java):


/**
 * 考试 排队列表 Redis 工具类
 *
 * @author Admin
 */
@Component
@Slf4j
public class ExamWaitListUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取列表数据
     *
     * @param sort 排序 0-按请求时间正序(早-晚) 1-按请求时间逆序(晚-早)
     * @param page 分页数据
     * @return 返回1
     */
    public Page<ExamWaitQueue> list(Integer sort, Long schoolId, CommonParam page) {
        int pageCount = page.getPageCount() <= 1 ? 1 : page.getPageCount();
        int pageSize = page.getPageSize() <= 0 ? 0 : page.getPageSize();
        //开始位置: 从1开始
        int start = (pageCount - 1) * pageSize;
        // 结束位置: 如果页面大小 = 0 时 获取所有数据, 包含当前位置
        int end = pageSize == 0 ? -1 : pageCount * page.getPageSize() - 1;
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<Object> typedTuples = null;
        if (sort == 0) {
            typedTuples = zSetRedis.range(SocketCommon.EXAM_WAIT_LIST + schoolId, start, end);
        } else {
            typedTuples = zSetRedis.reverseRange(SocketCommon.EXAM_WAIT_LIST + schoolId, start, end);
        }
        typedTuples = Optional.ofNullable(typedTuples).orElse(new HashSet<>());
        List<ExamWaitQueue> collect = typedTuples.stream().map(v -> JSON.parseObject(v.toString(), ExamWaitQueue.class)).collect(Collectors.toList());
        Page<ExamWaitQueue> examWaitQueuePage = new Page<>();
        examWaitQueuePage.setRecords(collect);         //数据
        examWaitQueuePage.setCurrent(pageCount);    //当前页
        //总数
        long total = Optional.ofNullable(zSetRedis.size(SocketCommon.EXAM_WAIT_LIST + schoolId)).orElse(0L);
        examWaitQueuePage.setTotal(total);
        examWaitQueuePage.setSize(pageSize == 0 ? total : pageSize);//页面大小
        return examWaitQueuePage;
    }


    /**
     * 弹出队列第一条数据
     *
     * @return 如果(没有数据或弹出失败)返回null
     */
    public ExamWaitQueue popFirst(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<Object> objects = Optional.ofNullable(zSetRedis.range(SocketCommon.EXAM_WAIT_LIST + schoolId, 0, 0))
                .orElse(new HashSet<>());
        if (CollectionUtils.isEmpty(objects)) {
            return null;
        }
        Object o = objects.stream().findFirst().orElse(null);
        if (o != null ) {
            ExamWaitQueue examWaitQueue = JSON.parseObject(o.toString(), ExamWaitQueue.class);
            this.removeFirst(examWaitQueue.getExamSchoolId());
            return examWaitQueue;
        }
        return null;

    }

    /**
     * 等待队列大小
     *
     * @return 返回 ExamWaitQueue
     */
    public Long size(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        return zSetRedis.size(SocketCommon.EXAM_WAIT_LIST + schoolId);
    }

    /**
     * 判断队列是否为空
     *
     * @return 返回 true - 为空, false - 不为空
     */
    public boolean isEmpty(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Long size = Optional.ofNullable(zSetRedis.size(SocketCommon.EXAM_WAIT_LIST + schoolId))
                .orElse(0L);
        return size <= 0;
    }

    /**
     * 判断队列是否不为空
     *
     * @return 返回 true - 不为空, false - 为空
     */
    public boolean isNotEmpty(Long schoolId) {
        return !isEmpty(schoolId);
    }

    /**
     * 获取等待考试用户(最先进入等待队列的用户)
     * @param schoolId 考场学校id
     * @param index 索引(为空则第一个)
     * @return ex
     */
    public ExamWaitQueue get(Long schoolId, Integer index) {
        index = Optional.ofNullable(index).orElse(0);
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<Object> objects = Optional.ofNullable(zSetRedis.range(SocketCommon.EXAM_WAIT_LIST + schoolId, index, index))
                .orElse(new HashSet<>());
        return objects.stream().map(v -> JSON.parseObject(v.toString(), ExamWaitQueue.class)).findFirst().orElse(null);
    }


    /**
     * 添加列表数据
     *
     * @param examWaitQueue 添加参数
     * @return 返回1
     */
    public int add(ExamWaitQueue examWaitQueue) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        examWaitQueue.setInWaitTime(new Date());
        zSetRedis.add(SocketCommon.EXAM_WAIT_LIST + examWaitQueue.getExamSchoolId(), JSON.toJSONString(examWaitQueue), System.currentTimeMillis());
        return 1;
    }

    /**
     * 移除第一个元素(最先进入队列的)
     *
     * @return 返回
     */
    public boolean removeFirst(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Long remove = zSetRedis.removeRange(SocketCommon.EXAM_WAIT_LIST + schoolId, 0, 0);
        return remove != null && remove > 0;
    }


    /**
     * 移除第一个元素(最先进入队列的)
     *
     * @return 返回
     */
    public boolean remove(ExamWaitQueue object) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Long remove = zSetRedis.remove(SocketCommon.EXAM_WAIT_LIST + object.getExamSchoolId(), JSON.toJSONString(object));
        return remove != null && remove > 0;
    }


}

封装空闲与已分配队列工具类:

@Slf4j
@Component
public class LaboratoryDistributionUtils {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private ExamWaitListUtils waitListUtils;

    /**
     * 考试空闲设备标识符
     */
    public final static String MACHINE_IDLE = "MACHINE_IDLE:";

    /**
     * 考试已分配设备标识符
     */
    public final static String MACHINE_USE = "MACHINE_USE:";


    /**
     *
     * 初始化方法
     */
    public void __init__(List<ExamMachineQueue> machinesList){
//        Map<String, List<ExamMachineQueue>> subjectNumberMap = machinesList.stream()
//                .collect(Collectors.groupingBy(ExamMachineQueue::getSubjectNumber));
        for (ExamMachineQueue machineQueue : machinesList) {
            this.addIdle(machineQueue);
        }
    }


    /**
     * 给等待队列所有学员 分配空闲设备(若无空闲则不分配)
     * @param schoolId 考试学校id
     * @return bean  如果无空闲设备返回为null
     */
    public Boolean distributionBatch(Long schoolId){
        Integer index = 0;
        while(waitListUtils.isNotEmpty(schoolId) && index < waitListUtils.size(schoolId)){
            ExamWaitQueue examWaitQueue = waitListUtils.get(schoolId, index);
            if(this.distributionMachine(examWaitQueue)!=null){
                waitListUtils.remove(examWaitQueue);
            }else{
                index++;
            }
        }
        return true;
    }


    /**
     * 给学员分配空闲设备
     * @param waitQueue 学员
     * @return bean  如果无空闲设备返回为null
     */
    public ExamMachineQueue distributionMachine(ExamWaitQueue waitQueue){
        ExamMachineQueue machineQueue = null;
        if(waitQueue==null) return null;
        //遍历学生考试科目
        for (Integer subjectNumber : waitQueue.getProcess()) {
            //获取可用设备
            machineQueue = this.getIdle(waitQueue.getExamSchoolId(), subjectNumber);
            if(BeanUtil.isNotEmpty(machineQueue)){
                break;
            }
        }
        if(BeanUtil.isNotEmpty(machineQueue)){
            BeanUtils.copyProperties(waitQueue, machineQueue);
            this.addUse(machineQueue, true);
            return machineQueue;
        }
        return null;
    }

    /**
     * 给空闲设备分配考试学员
     * @param machineQueue 空闲设备
     * @return bean  如果无空闲设备返回为null
     */
    public boolean distributionMember(ExamMachineQueue machineQueue){
        if(BeanUtil.isNotEmpty(machineQueue)){
            CommonParam commonParam = new CommonParam();
            commonParam.setPageSize(-1);
            List<ExamWaitQueue> waitQueues = waitListUtils.list(0, machineQueue.getExamSchoolId(), commonParam).getRecords();
            for (ExamWaitQueue waitQueue : waitQueues) {
                if(waitQueue.getProcess().contains(machineQueue.getSubjectNumber())){
                    //先移除之前在使用队列保存的旧数据
                    this.remove(machineQueue);
                    BeanUtil.copyProperties(waitQueue, machineQueue);
                    //添加使用队列
                    this.addUse(machineQueue, false);
                    //移除等待队列
                    waitListUtils.remove(waitQueue);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 结束使用设备
     * @param schoolId 学校id
     * @param subjectNumber 设备所属科目
     * @param machineId 设备id
     * @return 设备信息
     */
    public ExamWaitQueue endUse(Long schoolId, Integer subjectNumber, Long machineId){
        List<ExamMachineQueue> examMachineQueues = this.useList(schoolId, String.valueOf(subjectNumber));
        Optional.ofNullable(examMachineQueues).orElseThrow(()->new ApiException("没有使用中的设备"));
        List<ExamMachineQueue> collect = examMachineQueues.stream()
                .filter(v -> v.getMachineId().equals(machineId)).collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(collect)){
            ExamMachineQueue machineQueue = collect.get(0);
            //如果学员process还有考试科目未完成,进入等待队列
            ExamWaitQueue examWaitQueue = BeanUtil.copyProperties(machineQueue, ExamWaitQueue.class);
            examWaitQueue.getProcess().remove(subjectNumber);
            if(CollectionUtils.isNotEmpty(examWaitQueue.getProcess())){
                waitListUtils.add(examWaitQueue);
            }
            //重新从等待队列分配设备 (若等待队列无数据,则进入空闲队列)
            if(!this.distributionMember(machineQueue)){
                this.addIdle(machineQueue);
            }
            return examWaitQueue;

        }
        return null;
    }

    /**
     * 获取使用中设备列表
     * @param schoolId 学校id
     * @return bean  没有设备返回为null
     */
    public List<ExamMachineQueue> useList(Long schoolId, String subjectNumber){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<Object> uses = new HashSet<>();
        if("*".equals(subjectNumber)){
            Set<String> keys = redisTemplate.keys(this.keyGroup(MACHINE_USE, String.valueOf(schoolId), "*"));
            keys = Optional.ofNullable(keys).orElse(new HashSet<>());
            for (String key : keys) {
                Set<Object> range = zSetRedis.range(key, 0, -1);
                range = Optional.ofNullable(range).orElse(new HashSet<>());
                uses.addAll(range);
            }
        }else{
            uses = zSetRedis.range(this.keyGroup(MACHINE_USE, String.valueOf(schoolId), subjectNumber), 0, -1);
        }
        uses = Optional.ofNullable(uses).orElse(new HashSet<>());
        return uses.stream().map(v->JSON.parseObject(v.toString(), ExamMachineQueue.class))
                .collect(Collectors.toList());
    }

    /**
     * redis键组合
     * @param key 常量
     * @param schoolId 学校id
     * @param subjectNumber 所属科目
     * @return 例如: MACHINE_IDLE:1:1
     */
    public String keyGroup(String key, String schoolId, String subjectNumber){
        return key + schoolId + ":" + subjectNumber;
    }


    /**
     * 获取第一台空闲设备(队列中的第一台)
     * @param schoolId 学校id
     * @param subjectNumber 设备所属科目
     * @return bean
     */
    public ExamMachineQueue getIdle(Long schoolId, Integer subjectNumber){
        return this.get(MACHINE_IDLE, schoolId, subjectNumber);
    }

    /**
     * 获取已使用设备(队列中的第一台)
     * @param schoolId 学校id
     * @param subjectNumber 设备所属科目
     * @return bean
     */
    public ExamMachineQueue getUse(Long schoolId, Integer subjectNumber){
        return this.get(MACHINE_USE, schoolId, subjectNumber);
    }


    /**
     * 获取队列第一个设备
     * @param key MACHINE_IDLE:空闲队列, MACHINE_USE:使用中的队列
     * @param schoolId 学校id
     * @param subjectNumber 设备所属科目
     * @return ExamMachineQueue  如果队列为空返回null
     */
    private ExamMachineQueue get(String key, Long schoolId, Integer subjectNumber){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<Object> objects = zSetRedis.range(this.keyGroup(key, String.valueOf(schoolId), String.valueOf(subjectNumber)), 0, 0);
        ExamMachineQueue machineQueue = null;
        if(CollectionUtils.isNotEmpty(objects)){
            machineQueue = objects.stream().map(v->JSON.parseObject(v.toString(), ExamMachineQueue.class))
                    .findFirst().orElse(null);
        }
        return machineQueue;
    }

    /**
     * 添加空闲设备列表数据
     * @param addParam 添加参数
     * @return 返回1
     */
    public Boolean addIdle(ExamMachineQueue addParam){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        //添加空闲列表
        Boolean add = zSetRedis.add(this.keyGroup(MACHINE_IDLE
                        , String.valueOf(addParam.getExamSchoolId())
                        , String.valueOf(addParam.getSubjectNumber())) , JSON.toJSONString(addParam), System.currentTimeMillis());
        //移除使用中队列
        if(Boolean.TRUE.equals(add)){
            this.remove(addParam);
        }
        return add;
    }

    /**
     * 添加使用中设备列表数据
     * @param addParam 添加参数
     * @param isRemove 需要添加的设备是否存在 “空闲队列”,true-存在:同时移除空闲队列
     * @return 返回1
     */
    public Boolean addUse( ExamMachineQueue addParam, boolean isRemove){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        //防止修改值不能移除等待队列
        ExamMachineQueue machineQueue = BeanUtil.copyProperties(addParam, ExamMachineQueue.class);
        machineQueue.setInUseTime(new Date());
        //计算用户到使用设备的等待时间
        machineQueue.getWaitTimeList().add(addParam.getSubjectNumber(), DateUtil.betweenMs(machineQueue.getInWaitTime(), new Date()));
        String useKey = this.keyGroup(MACHINE_USE, String.valueOf(machineQueue.getExamSchoolId()), String.valueOf(machineQueue.getSubjectNumber()));
        String idleKey = this.keyGroup(MACHINE_IDLE, String.valueOf(machineQueue.getExamSchoolId()), String.valueOf(machineQueue.getSubjectNumber()));
        Boolean add = zSetRedis.add(useKey , JSON.toJSONString(machineQueue), System.currentTimeMillis());
        if(Boolean.TRUE.equals(add) && isRemove){
            Long aLong = zSetRedis.removeRange(idleKey,0, 0);
        }
        return add;
    }

    /**
     * 移除使用队列 中指定元素
     * @return 返回
     */
    public boolean remove(ExamMachineQueue removeParam){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        String key = this.keyGroup(MACHINE_USE, String.valueOf(removeParam.getExamSchoolId()), String.valueOf(removeParam.getSubjectNumber()));
        Long remove = zSetRedis.remove(key,  JSON.toJSONString(removeParam));
        return remove!=null && remove>0;
    }


    /**
     * 空闲设备总数
     * @return 返回
     */
    public Long idleAllNum(Long schoolId){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<String> keys = redisTemplate.keys(this.keyGroup(MACHINE_IDLE, String.valueOf(schoolId), "*"));
        keys = Optional.ofNullable(keys).orElse(new HashSet<>());
        Long sum = 0L;
        for (String key : keys) {
            Long size = zSetRedis.size(key);
            sum += size;

        }
        return sum;
    }


    /**
     * 空闲设备总数
     * @return 返回
     */
    public Map<String, Long> statics(Long schoolId){
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Set<String> keys = redisTemplate.keys(this.keyGroup(MACHINE_IDLE, String.valueOf(schoolId), "*"));
        keys = Optional.ofNullable(keys).orElse(new HashSet<>());
        HashMap<String, Long> map = new HashMap<>(16);
        Set<Object> uses = new HashSet<>();
        for (String key : keys) {
            Set<Object> range = zSetRedis.range(key, 0, -1);
            range = Optional.ofNullable(range).orElse(new HashSet<>());
            uses.addAll(range);

        }
        Map<String, List<ExamMachineQueue>> queueMap = uses.stream()
                .map(v ->  JSON.parseObject(v.toString(), ExamMachineQueue.class))
                .collect(Collectors.groupingBy(ExamMachineQueue::getType));
        queueMap.forEach((key, value)->{
            map.put(key, (long) value.size());
        });
        return map;
    }


    /**
     * 判断队列是否为空
     *
     * @return 返回 true - 为空, false - 不为空
     */
    public boolean isEmpty(Long schoolId) {
        ZSetOperations<String, Object> zSetRedis = redisTemplate.opsForZSet();
        Long size = Optional.ofNullable(zSetRedis.size(SocketCommon.EXAM_WAIT_LIST + schoolId))
                .orElse(0L);
        return size <= 0;
    }

    /**
     * 判断队列是否不为空
     *
     * @return 返回 true - 不为空, false - 为空
     */
    public boolean isNotEmpty(Long schoolId) {
        return !isEmpty(schoolId);
    }

}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
银行叫系统可以使用两种数据结构实现:有序链表和队列。 1. 有序链表:有序链表是一种按照特定顺序排列元素的链表。在银行叫系统中,可以使用有序链表来按照客户到达时间的顺序来排列客户。每当有新的客户到达时,将其插入到有序链表的适当位置。当某个客户完成业务后,可以从链表中删除该客户。这样,银行工作人员可以按照链表中的顺序依次处理客户。 2. 队列:队列是一种先进先出(FIFO)的数据结构。在银行叫系统中,可以使用队列来模拟客户排队的过程。每当有新的客户到达时,将其加入到队列的末尾。当某个客户完成业务后,从队列的头部取出下一个客户进行处理。这样,银行工作人员可以按照队列中的顺序依次处理客户。 以下是使用C语言实现银行叫系统的示例代码: 1. 使用有序链表实现银行叫系统: ```c #include <stdio.h> #include <stdlib.h> // 定义客户结构体 typedef struct { int arrivalTime; // 到达时间 int serviceTime; // 业务所需时间 } Customer; // 定义链表节点结构体 typedef struct Node { Customer customer; struct Node* next; } Node; // 插入客户到有序链表中 void insertCustomer(Node** head, Customer customer) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->customer = customer; newNode->next = NULL; if (*head == NULL || customer.arrivalTime < (*head)->customer.arrivalTime) { newNode->next = *head; *head = newNode; } else { Node* current = *head; while (current->next != NULL && current->next->customer.arrivalTime <= customer.arrivalTime) { current = current->next; } newNode->next = current->next; current->next = newNode; } } // 删除链表中的客户 void deleteCustomer(Node** head) { if (*head == NULL) { return; } Node* temp = *head; *head = (*head)->next; free(temp); } // 打印链表中的客户信息 void printCustomers(Node* head) { Node* current = head; while (current != NULL) { printf("Arrival Time: %d, Service Time: %d\n", current->customer.arrivalTime, current->customer.serviceTime); current = current->next; } } int main() { Node* head = NULL; // 添加客户到链表中 Customer customer1 = {10, 5}; insertCustomer(&head, customer1); Customer customer2 = {5, 3}; insertCustomer(&head, customer2); Customer customer3 = {8, 4}; insertCustomer(&head, customer3); // 打印链表中的客户信息 printCustomers(head); // 删除链表中的客户 deleteCustomer(&head); // 打印链表中的客户信息 printCustomers(head); return 0; } ``` 2. 使用队列实现银行叫系统: ```c #include <stdio.h> #include <stdlib.h> // 定义客户结构体 typedef struct { int arrivalTime; // 到达时间 int serviceTime; // 业务所需时间 } Customer; // 定义队列结构体 typedef struct { Customer* customers; int front; int rear; int capacity; } Queue; // 创建队列 Queue* createQueue(int capacity) { Queue* queue = (Queue*)malloc(sizeof(Queue)); queue->customers = (Customer*)malloc(capacity * sizeof(Customer)); queue->front = 0; queue->rear = -1; queue->capacity = capacity; return queue; } // 判断队列是否为空 int isEmpty(Queue* queue) { return queue->rear < queue->front; } // 判断队列是否已满 int isFull(Queue* queue) { return queue->rear == queue->capacity - 1; } // 入队 void enqueue(Queue* queue, Customer customer) { if (isFull(queue)) { printf("Queue is full.\n"); return; } queue->customers[++queue->rear] = customer; } // 出队 void dequeue(Queue* queue) { if (isEmpty(queue)) { printf("Queue is empty.\n"); return; } queue->front++; } // 打印队列中的客户信息 void printCustomers(Queue* queue) { for (int i = queue->front; i <= queue->rear; i++) { printf("Arrival Time: %d, Service Time: %d\n", queue->customers[i].arrivalTime, queue->customers[i].serviceTime); } } int main() { Queue* queue = createQueue(5); // 入队 Customer customer1 = {10, 5}; enqueue(queue, customer1); Customer customer2 = {5, 3}; enqueue(queue, customer2); Customer customer3 = {8, 4}; enqueue(queue, customer3); // 打印队列中的客户信息 printCustomers(queue); // 出队 dequeue(queue); // 打印队列中的客户信息 printCustomers(queue); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值