LeetCode - 460 LFU缓存(Java JS Python)

题目来源

460. LFU 缓存 - 力扣(LeetCode)

题目描述

请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。

实现 LFUCache 类:

  • LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
  • int get(int key) - 如果键 key 存在于缓存中,则获取键的值,否则返回 -1 。
  • void put(int key, int value) - 如果键 key 已存在,则变更其值;如果键不存在,请插入键值对。当缓存达到其容量 capacity 时,则应该在插入新项之前,移除最不经常使用的项。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最久未使用 的键。

为了确定最不常使用的键,可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时,它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作,使用计数器的值将会递增。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例

输入

["LFUCache", "put", "put", "get", "put", "get", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [3], [4, 4], [1], [3], [4]]

输出

[null, null, null, 1, null, -1, 3, null, -1, 3, 4]

解释

// cnt(x) = 键 x 的使用计数
// cache=[] 将显示最后一次使用的顺序(最左边的元素是最近的)
LFUCache lfu = new LFUCache(2);
lfu.put(1, 1);   // cache=[1,_], cnt(1)=1
lfu.put(2, 2);   // cache=[2,1], cnt(2)=1, cnt(1)=1
lfu.get(1);      // 返回 1
                 // cache=[1,2], cnt(2)=1, cnt(1)=2
lfu.put(3, 3);   // 去除键 2 ,因为 cnt(2)=1 ,使用计数最小
                 // cache=[3,1], cnt(3)=1, cnt(1)=2
lfu.get(2);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,1], cnt(3)=2, cnt(1)=2
lfu.put(4, 4);   // 去除键 1 ,1 和 3 的 cnt 相同,但 1 最久未使用
                 // cache=[4,3], cnt(4)=1, cnt(3)=2
lfu.get(1);      // 返回 -1(未找到)
lfu.get(3);      // 返回 3
                 // cache=[3,4], cnt(4)=1, cnt(3)=3
lfu.get(4);      // 返回 4
                 // cache=[3,4], cnt(4)=2, cnt(3)=3

提示

  • 1 <= capacity <= 104
  • 0 <= key <= 105
  • 0 <= value <= 109
  • 最多调用 2 * 10^5 次 get 和 put 方法

题目解析

LFU缓存可以通过:两个哈希表(Map结构) + 双向链表来实现。

我们可以定义两个哈希表keyMap和freqMap,其中:

  • keyMap的键是"本题key",值为"双向链表节点node"
  • freqMap的键是"本题key的使用次数freq",值为”对应使用次数freq的key对应的node组成的双向链表“

而双向链表节点node用于记录key的如下信息:

  • key:本题的key
  • val:本题的value
  • freq:对应key使用次数

可能上面说法比较晦涩,下面通过图示来说:










以上就是两个哈希Map,以及双向链表完成LFU缓存的逻辑。

上面逻辑中,有一个问题,那么就是每次LFU容量不足时,我们需要删除掉最少、最远使用的key,那么首先如何找到最少使用次数的key呢?

上面图示中,我们是通过人眼识别出freqMap键列中最小的键,即为最少使用次数。

那么代码该如何实现呢?

我们可以定义一个全局变量(或者类静态变量)minFreq来记录最少使用次数,而minFreq的更新有如下时机:

  • 1、put新增操作一定会带来一个使用次数freq=1的键,且此新键的使用次数1一定是最少的,此时我们可以更新minFreq=1
  • 2、get和put更新操作会新增对应key的使用次数,因此在新增使用次数后,该key需要从对应使用次数的DLink中去除,如果对应DLink只有该key对应节点,且对应DLink是最少使用次数对应的容器链表,那么删除key后,该DLInk就为空,那么最少使用次数的key就没了。此时我们可以直接将minFreq++,因为如果当前key在新增使用次数前是唯一的最少使用次数key,那么当前key新增使用次数后,依旧是最少使用次数的key。而minFreq就是当前key新增使用次数前的使用次数。

Java算法源码

import java.util.HashMap;

class LFUCache {
  /** 双向链表节点 */
  static class Node {
    /** 记录本题的键 */
    int key;

    /** 记录本题的值 */
    int val;

    /** 记录该键被访问的次数 */
    int freq;

    /** 当前节点的上一个节点 */
    Node prev;

    /** 当前节点的下一个节点 */
    Node next;

    public Node(int key, int val, int freq) {
      this.key = key;
      this.val = val;
      this.freq = freq;
      this.prev = null;
      this.next = null;
    }
  }

  /** 双向链表 */
  static class Link {
    /** 链表中节点个数 */
    int size;

    /** 链表头节点 */
    Node head;

    /** 链表尾节点 */
    Node tail;

    public Link() {
      this.size = 0;
      this.head = null;
      this.tail = null;
    }

    /**
     * 尾插
     *
     * @param node 要被插入的节点
     */
    public void addLast(Node node) {
      if (this.size == 0) {
        // 空链表,则node节点插入后,即为头、尾节点
        this.head = node;
        this.tail = node;
      } else {
        // 非空链表,则node节点插入到tail节点后面
        this.tail.next = node;
        node.prev = this.tail;
        this.tail = node;
      }
      this.size++;
    }

    /**
     * 删除指定节点
     *
     * @param node 要删除的节点
     */
    public void remove(Node node) {
      // 空链表没有节点,所以无法删除
      if (this.size == 0) return;

      if (this.size == 1) {
        // 链表只有一个节点,则删除完后,变为空链表
        this.head = null;
        this.tail = null;
      } else if (this.head == node) {
        // 如果要删除的节点是头节点
        this.head = this.head.next;
        this.head.prev = null;
      } else if (this.tail == node) {
        // 如果要删除的节点是尾节点
        this.tail = this.tail.prev;
        this.tail.next = null;
      } else {
        // 如果要删除的节点是中间节点
        node.prev.next = node.next;
        node.next.prev = node.prev;
      }

      this.size--;
    }
  }

  /** keyMap用于记录key对应的node */
  HashMap<Integer, Node> keyMap;

  /** freqMap的key是访问次数,value是具有相同访问次数的key对应的node组成的链表,链表头是最远访问的,链表尾是最近访问的 */
  HashMap<Integer, Link> freqMap;

  /** LFU缓存中能记录的最多key的数量 */
  int capacity;

  /** LFU缓存中所有的key中最少的访问次数 */
  int minFreq;

  public LFUCache(int capacity) {
    this.keyMap = new HashMap<>();
    this.freqMap = new HashMap<>();
    this.capacity = capacity;
    this.minFreq = 0;
  }

  public int get(int key) {
    if (this.keyMap.containsKey(key)) {
      // 存在对应key,则返回对应val
      Node node = this.keyMap.get(key);
      incNodeFreq(node); // get操作会新增对应key的访问次数
      return node.val;
    } else {
      // 不存在对应key,则返回-1
      return -1;
    }
  }

  public void put(int key, int value) {
    // 对应key已存在,则为更新场景
    if (this.keyMap.containsKey(key)) {
      Node node = this.keyMap.get(key);
      incNodeFreq(node); // 更新操作会增加对应key的访问次数
      node.val = value;
    }
    // 对应key不存在,则为新增场景
    else {
      // 先判断容量是否超过,keyMap的key数量就是LFU缓存中记录的key数量
      if (this.keyMap.size() >= this.capacity) {
        Link link = this.freqMap.get(this.minFreq);
        int removeKey = link.head.key;
        link.remove(link.head); // 最少访问次数所在链表的头节点,即为:最少、最远访问的key,容量不足时,需要优先删除它
        this.keyMap.remove(removeKey); // 注意,不要遗漏将keyMap中该key删除
      }

      // 新增key,则对应key的访问次数为1,且为最少访问次数
      this.minFreq = 1;
      Node node = new Node(key, value, this.minFreq);

      // 将新增key对应的node加入到freqMap,和keyMap中
      this.freqMap.putIfAbsent(this.minFreq, new Link());
      this.freqMap.get(this.minFreq).addLast(node);
      this.keyMap.put(key, node);
    }
  }

  /**
   * 增加key的访问次数
   *
   * @param node key对应的node
   */
  public void incNodeFreq(Node node) {
    // 由于key的访问次数增加,因此要从原访问次数的链表中删除
    this.freqMap.get(node.freq).remove(node);

    // 如果原链表删除当前key对应的节点后为空,且原链表对应的访问次数就是最少访问次数
    if (this.freqMap.get(node.freq).size == 0 && node.freq == this.minFreq) {
      // 则最少访问次数对应的key没有了,因此最少访问次数++(即当前key访问次数++后,当前key的访问次数还是最少访问次数)
      this.minFreq++;
    }

    // 当前key访问次数++
    node.freq++;

    // 将当前key对应的node转移到对应增加后的访问次数对应的链表尾部(最近访问)
    this.freqMap.putIfAbsent(node.freq, new Link());
    this.freqMap.get(node.freq).addLast(node);
  }
}

JS算法源码

/** 双向链表节点 */
class Node {
  constructor(key, val, freq) {
    /** 记录本题的键 */
    this.key = key;
    /** 记录本题的值 */
    this.val = val;
    /** 记录该键被访问的次数 */
    this.freq = freq;
    /** 当前节点的上一个节点 */
    this.prev = null;
    /** 当前节点的下一个节点 */
    this.next = null;
  }
}

/** 双向链表 */
class Link {
  constructor() {
    /** 链表中节点个数 */
    this.size = 0;
    /** 链表头节点 */
    this.head = null;
    /** 链表尾节点 */
    this.tail = null;
  }

  /**
   * 尾插
   * @param {*} node 要被插入的节点
   */
  addLast(node) {
    if (this.size == 0) {
      // 空链表,则node节点插入后,即为头、尾节点
      this.head = node;
      this.tail = node;
    } else {
      // 非空链表,则node节点插入到tail节点后面
      this.tail.next = node;
      node.prev = this.tail;
      this.tail = node;
    }
    this.size++;
  }

  /**
   * 删除指定节点
   * @param {*} node 要删除的节点
   */
  remove(node) {
    // 空链表没有节点,所以无法删除
    if (this.size == 0) return;

    if (this.size == 1) {
      // 链表只有一个节点,则删除完后,变为空链表
      this.head = null;
      this.tail = null;
    } else if (this.head == node) {
      // 如果要删除的节点是头节点
      this.head = this.head.next;
      this.head.prev = null;
    } else if (this.tail == node) {
      // 如果要删除的节点是尾节点
      this.tail = this.tail.prev;
      this.tail.next = null;
    } else {
      // 如果要删除的节点是中间节点
      node.prev.next = node.next;
      node.next.prev = node.prev;
    }

    this.size--;
  }
}

/**
 * @param {number} capacity
 */
var LFUCache = function (capacity) {
  /** keyMap用于记录key对应的node */
  this.keyMap = new Map();
  /** freqMap的key是访问次数,value是具有相同访问次数的key对应的node组成的链表,链表头是最远访问的,链表尾是最近访问的 */
  this.freqMap = new Map();
  /** LFU缓存中能记录的最多key的数量 */
  this.capacity = capacity;
  /** LFU缓存中所有的key中最少的访问次数 */
  this.minFreq = 0;
};

/**
 * @param {number} key
 * @return {number}
 */
LFUCache.prototype.get = function (key) {
  if (this.keyMap.has(key)) {
    // 存在对应key,则返回对应val
    const node = this.keyMap.get(key);
    this.incNodeFreq(node); // get操作会新增对应key的访问次数
    return node.val;
  } else {
    // 不存在对应key,则返回-1
    return -1;
  }
};

/**
 * @param {number} key
 * @param {number} value
 * @return {void}
 */
LFUCache.prototype.put = function (key, value) {
  // 对应key已存在,则为更新场景
  if (this.keyMap.has(key)) {
    const node = this.keyMap.get(key);
    this.incNodeFreq(node); // 更新操作会增加对应key的访问次数
    node.val = value;
  }
  // 对应key不存在,则为新增场景
  else {
    // 先判断容量是否超过,keyMap的key数量就是LFU缓存中记录的key数量
    if (this.keyMap.size >= this.capacity) {
      const link = this.freqMap[this.minFreq];
      const removeKey = link.head.key;
      link.remove(link.head); // 最少访问次数所在链表的头节点,即为最少、最远访问的key,容量不足时,需要优先删除它
      this.keyMap.delete(removeKey); // 注意,不要遗漏将keyMap中该key删除
    }

    // 新增key,则对应key的访问次数为1,且为最少访问次数
    this.minFreq = 1;

    // 将新增key对应的node加入到freqMap,和keyMap中
    const node = new Node(key, value, this.minFreq);

    if (this.freqMap[this.minFreq] == undefined) {
      this.freqMap[this.minFreq] = new Link();
    }

    this.freqMap[this.minFreq].addLast(node);
    this.keyMap.set(key, node);
  }
};

/**
 * 增加key的访问次数
 * @param {*} node key对应的node
 */
LFUCache.prototype.incNodeFreq = function (node) {
  // 由于key的访问次数增加,因此要从原访问次数的链表中删除
  this.freqMap[node.freq].remove(node);

  // 如果原链表删除当前key对应的节点后为空,且原链表对应的访问次数就是最少访问次数
  if (this.freqMap[node.freq].size == 0 && node.freq == this.minFreq) {
    // 则最少访问次数对应的key没有了,因此最少访问次数++(即当前key访问次数++后,当前key的访问次数还是最少访问次数)
    this.minFreq++;
  }

  // 当前key访问次数++
  node.freq++;

  // 将当前key对应的node转移到对应增加后的访问次数对应的链表尾部(最近访问)
  if (this.freqMap[node.freq] == undefined) {
    this.freqMap[node.freq] = new Link();
  }

  this.freqMap[node.freq].addLast(node);
};

Python算法源码

# 双向链表节点
class Node:
    def __init__(self, key, val, freq):
        """
        :param key: 记录本题的键
        :param val: 记录本题的值
        :param freq: 记录该键被访问的次数
        """
        self.key = key
        self.val = val
        self.freq = freq
        self.prev = None
        self.next = None


# 双向链表
class Link:
    def __init__(self):
        self.size = 0
        self.head = None
        self.tail = None

    def addLast(self, node):
        """
        尾插
        :param node: 要被插入的节点
        """
        if self.size == 0:
            # 空链表,则node节点插入后,即为头、尾节点
            self.head = node
            self.tail = node
        else:
            # 非空链表,则node节点插入到tail节点后面
            self.tail.next = node
            node.prev = self.tail
            self.tail = node

        self.size += 1

    def remove(self, node):
        """
        删除指定节点
        :param node: 要删除的节点
        """
        if self.size == 0:
            # 空链表没有节点,所以无法删除
            return

        if self.size == 1:
            # 链表只有一个节点,则删除完后,变为空链表
            self.head = None
            self.tail = None
        elif self.head == node:
            # 如果要删除的节点是头节点
            self.head = self.head.next
            self.head.prev = None
        elif self.tail == node:
            # 如果要删除的节点是尾节点
            self.tail = self.tail.prev
            self.tail.next = None
        else:
            # 如果要删除的节点是中间节点
            node.prev.next = node.next
            node.next.prev = node.prev

        self.size -= 1


class LFUCache(object):

    def __init__(self, capacity):
        self.keyMap = {}  # keyMap用于记录key对应的node
        self.freqMap = {}  # freqMap的key是访问次数,value是具有相同访问次数的key对应的node组成的链表,链表头是最远访问的,链表尾是最近访问的
        self.capacity = capacity  # LFU缓存中能记录的最多key的数量
        self.minFreq = 0  # LFU缓存中所有的key中最少的访问次数

    def get(self, key):
        if key in self.keyMap:
            # 存在对应key,则返回对应val
            node = self.keyMap[key]
            self.incNodeFreq(node)  # get操作会新增对应key的访问次数
            return node.val
        else:
            # 不存在对应key,则返回-1
            return -1

    def put(self, key, value):
        if key in self.keyMap:
            # 对应key已存在,则为更新场景
            node = self.keyMap[key]
            self.incNodeFreq(node)  # 更新操作会增加对应key的访问次数
            node.val = value
        else:
            # 对应key不存在,则为新增场景
            # 先判断容量是否超过,keyMap的key数量就是LFU缓存中记录的key数量
            if len(self.keyMap) >= self.capacity:
                link = self.freqMap[self.minFreq]
                removeKey = link.head.key
                link.remove(link.head)  # 最少访问次数所在链表的头节点,即为:最少、最远访问的key,容量不足时,需要优先删除它
                self.keyMap.pop(removeKey)  # 注意,不要遗漏将keyMap中该key删除

            # 新增key,则对应key的访问次数为1,且为最少访问次数
            self.minFreq = 1
            node = Node(key, value, self.minFreq)

            # 将新增key对应的node加入到freqMap,和keyMap中
            self.freqMap.setdefault(self.minFreq, Link())
            self.freqMap.get(self.minFreq).addLast(node)
            self.keyMap[key] = node

    def incNodeFreq(self, node):
        """
        增加key的访问次数
        :param node: key对应的node
        """
        # 由于key的访问次数增加,因此要从原访问次数的链表中删除
        self.freqMap[node.freq].remove(node)

        # 如果原链表删除当前key对应的节点后为空,且原链表对应的访问次数就是最少访问次数
        if self.freqMap[node.freq].size == 0 and node.freq == self.minFreq:
            # 则最少访问次数对应的key没有了,因此最少访问次数++(即当前key访问次数++后,当前key的访问次数还是最少访问次数)
            self.minFreq += 1

        # 当前key访问次数++
        node.freq += 1

        # 将当前key对应的node转移到对应增加后的访问次数对应的链表尾部(最近访问)
        self.freqMap.setdefault(node.freq, Link())
        self.freqMap[node.freq].addLast(node)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/252552.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及消息追踪、延时消息实战

文章目录 消息追踪延时消息实战 消息追踪 设置消息追踪需要修改 broker 启动的配置文件&#xff0c;添加一行配置&#xff1a;traceTopicEnabletrue 即可&#xff0c;操作如下&#xff1a; # 进入到 rocketmq 的安装目录中 # 先复制一份配置文件 cp broker.conf custom.conf …

高并发音频转口型服务器开发

课程名称&#xff1a;高并发口型服务器开发 高并发口型服务器 哈喽&#xff0c;大家好&#xff0c;我叫人宅&#xff0c;很高兴和大家一起分享本套课程&#xff0c;高并发口型服务器开发。 虚拟人的口型一直是我们避免不了问题&#xff0c;即使我们不做虚拟人实时口型&#x…

WorkPlus搭建高效即时通讯,打造高效协作新标杆

在现代企业中&#xff0c;即时通讯已成为团队沟通和协作的重要组成部分。作为一款创新的即时通讯工具&#xff0c;WorkPlus致力于提供高效、便捷的沟通和协作体验&#xff0c;打造全新的企业沟通标杆。 WorkPlus的出色之处在于其高效的即时通讯能力。无论是团队内部交流还是跨团…

H266/VVC标准的编码结构介绍

概述 CVS&#xff1a; H266的编码码流包含一个或多个编码视频序列&#xff08;Coded Video Swquence&#xff0c;CVS&#xff09;&#xff0c;每个CVS以帧内随机接入点&#xff08;Intra Random Access Point&#xff0c; IRAP&#xff09;或逐渐解码刷新&#xff08;Gradual …

MySQL 教程 2.2

MySQL WHERE 子句 我们知道从 MySQL 表中使用 SELECT 语句来读取数据。 如需有条件地从表中选取数据&#xff0c;可将 WHERE 子句添加到 SELECT 语句中。 WHERE 子句用于在 MySQL 中过滤查询结果&#xff0c;只返回满足特定条件的行。 语法 以下是 SQL SELECT 语句使用 WH…

项目实战:自动驾驶之方向盘操纵

项目介绍 根据汽车前方摄像头捕捉的画面,控制汽车方向盘转动的方向和角度,这是自动驾驶要解决的核心问题。这个项目主要是通过使用深度神经网络解决一个回归问题。不同于分类、识别场景,回归问题中神经网络输出的是一个连续的值。 通过这个项目的学习,可以将神经网络用于通…

Python开源项目周排行 2023年第40周

Python 趋势周报&#xff0c;按周浏览往期 GitHub,Gitee 等最热门的Python开源项目&#xff0c;入选的项目主要参考GitHub Trending,部分参考了Gitee和其他。排名不分先后&#xff0c;都是当周相对热门的项目。 入选公式&#xff1d;70%GitHub Trending20%Gitee10%其他 关注微…

CSS 的背景属性(开发中常用)

目录 1 内容预览 背景颜色 背景图片 背景平铺 背景图片位置(常用) 背景图像固定 背景复合写法 背景色半透明 实现案例 1 内容预览 背景属性可以设置背景颜色、背景图片、背景平铺、背景图片位置、背景图像固定等。 注意&#xff1a; 把表格中的五个属背下来&#xff0c…

搞懂这6 个持续集成工具,领先80%测试人!

开发人员喜欢把写的代码当成自己的孩子&#xff0c;他们会被当成艺术品一样呵护。作为家长&#xff0c;总是会认为自己的孩子是最好的&#xff0c;也会尽全力给自己的孩子最好的&#xff0c;就算有时候会超出自己的能力范围。 最终&#xff0c;孩子会走出去&#xff0c;和其他…

京东看数据-京东运营数据分析-10月宠物市场销售数据分析(宠物店铺数据查询)

近年来&#xff0c;宠物成为越来越多家庭的成员&#xff0c;宠物数量不断增长&#xff0c;“宠物经济”异军突起&#xff0c;成为消费市场中特殊的存在。“撸猫”“吸狗”正迅速成为人们交际的热门谈资&#xff0c;宠物经济也迈入快速发展阶段。 根据鲸参谋电商数据分析平台的相…

fckeditor编辑器改造示例:增加PRE,CODE控件

查看专栏目录 Network 灰鸽宝典专栏主要关注服务器的配置&#xff0c;前后端开发环境的配置&#xff0c;编辑器的配置&#xff0c;网络服务的配置&#xff0c;网络命令的应用与配置&#xff0c;windows常见问题的解决等。 文章目录 修改方法&#xff1a;1&#xff09;修改fckco…

JS基本语法

JS基本语法 变量数据类型原始数据类型 函数定义第一种方式第二种方式 JS 对象ArrayStringJavaScript 自定义对象JSONDOMBOM JS 事件事件监听事件绑定常见事件 变量 数据类型 原始数据类型 函数定义 第一种方式 第二种方式 JS 对象 Array String JavaScript 自定义对象 JSON …

VR汽车技术服务虚拟仿真实训平台更好地辅助职业上岗培训

VR汽车虚拟仿真教学软件是一种基于虚拟现实技术的教学辅助工具。它能够模拟真实的汽车环境和操作场景&#xff0c;让学生能够通过虚拟仿真来学习和实践汽车相关知识和技能。 与传统的教学方式相比&#xff0c;VR汽车虚拟仿真教学软件具有更高的视觉沉浸感和互动性&#xff0c;能…

19.Tomcat搭建

Tomcat 简介 Tomcat的安装和启动 前置条件 • JDK 已安装(JAVA_HOME环境变量已被成功配置) Windows 下安装 访问 http://tomcat.apache.org ⇒ 左侧边栏 “Download” 2. 解压缩下载的文件到 “D:\tomcat”, tomcat的内容最终被解压到 “D:\tomcat\apache-tomcat-9.0.84” 3.…

Stable LM Zephyr 3B:手机上的强大LLM助手

概览 最近&#xff0c;Stability.ai宣布开源了Stable LM Zephyr 3B&#xff0c;这是一个30亿参数的大语言模型&#xff08;LLM&#xff09;&#xff0c;专为手机、笔记本等移动设备设计。其突出的特点是参数较小、性能强大且算力消耗低&#xff0c;能够自动生成文本、总结摘要…

PLC-Recorder V3 修改服务器和客户端通讯端口的方法

PLC-Recorder V3是服务器和客户端的架构&#xff0c;他们之间用TCP通讯。如果客户端无法与服务器建立连接&#xff08;重启也无效&#xff0c;并且确保没有老版本的PLC-Recorder在运行&#xff09;&#xff0c;则可能是端口被占用了。这时候需要修改他们之间的通讯端口&#xf…

打造‘产业大数据综合服务平台’,助力智慧园区建设!

随着大数据、人工智能、云计算、物联网等新一代信息技术的发展与应用&#xff0c;我国各类型园区正在向“智慧园区”转型升级&#xff0c;逐步开启数字化、智能化的运营管理模式。智慧园区的建设不仅需要基础设施的智慧化&#xff0c;更要实现园区规划、运营、管理、服务的智慧…

求解最大子段和问题

求解最大子段和问题。 对于给定序列a1,a2,a3……an,寻找它的某个连续子段&#xff0c;使得其和最大。如( -2,11,-4,13,-5,-2 )最大子段是{ 11,-4,13 }其和为20。 要求&#xff1a;分别用教材所给的三种方法求解&#xff08;简单方法、分治法、动态规划&#xff09;&#xff0…

scala方法与函数

定义方法定义函数方法和函数的区别scala的方法函数操作 1.9 方法与函数 1.9.1 定义方法 定义方法的基本格式是&#xff1a; def 方法名称&#xff08;参数列表&#xff09;&#xff1a;返回值类型 方法体 def add(x: Int, y: Int): Int x y println(add(1, 2)) // 3 //也…

html第一阶段到底再讲什么

通过这个方式来学习javaweb的全部框架 服务器数据库和连接前端和后端的方式