剑指 Offer II 031. 最近最少使用缓存
comments: true
edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20031.%20%E6%9C%80%E8%BF%91%E6%9C%80%E5%B0%91%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98/README.md
剑指 Offer II 031. 最近最少使用缓存
题目描述
运用所掌握的数据结构,设计和实现一个 LRU (Least Recently Used,最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity)以正整数作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
示例:
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
提示:
1 <= capacity <= 30000 <= key <= 100000 <= value <= 105- 最多调用
2 * 105次get和put
进阶:是否可以在 O(1) 时间复杂度内完成这两种操作?
注意:本题与主站 146 题相同:https://leetcode.cn/problems/lru-cache/
解法
方法一:哈希表 + 双向链表
我们可以用“哈希表”和“双向链表”实现一个 LRU 缓存。
- 哈希表:用于存储 key 和对应的节点位置。
- 双向链表:用于存储节点数据,按照访问时间排序。o(1)
当访问一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。这样就能保证链表尾部存储的就是最近最久未使用的节点,当节点数量大于缓存最大空间时就淘汰链表尾部的节点。
当插入一个节点时,如果节点存在,我们将其从原来的位置删除,并重新插入到链表头部。如果不存在,我们首先检查缓存是否已满,如果已满,则删除链表尾部的节点,将新的节点插入链表头部。
时间复杂度 O ( 1 ) O(1) O(1),空间复杂度 O ( c a p a c i t y ) O(capacity) O(capacity)。
Python3
class Node:def __init__(self,key=0,val=0):self.key=keyself.val=valself.prev=Noneself.next=Noneclass LRUCache:def __init__(self, capacity: int):self.cache={}self.capacity=capacity#重要标记: 方便o(1)插入首尾,达到 (链头:最近 链位:最久)self.head=Node()self.tail=Node()self.head.next=self.tailself.tail.prev=self.headself.len=0def get(self, key: int) -> int:if key not in self.cache:return -1Nd=self.cache[key]self.exist_to_head(Nd)return Nd.valdef put(self, key: int, value: int) -> None:if key in self.cache:Nd=self.cache[key]Nd.val=valueself.exist_to_head(Nd)else:Nd=Node(key,value)self.put_to_head(Nd)self.len+=1self.cache[key]=Ndif self.len>self.capacity: #注意1位置:先增后删的意义!!!Nd=self.del_tail()self.len-=1del self.cache[Nd.key]def put_to_head(self,node):node.next=self.head.nextnode.prev=self.headnode.next.prev=nodeself.head.next=nodedef del_tail(self):del_node=self.tail.prevprv_node=del_node.prevprv_node.next=self.tailself.tail.prev=prv_nodereturn del_nodedef exist_to_head(self,node):#先 delprv_node=node.prevnx_node=node.nextprv_node.next=nx_nodenx_node.prev=prv_node#再 put_to_headself.put_to_head(node)# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
Java
class Node {int key;int val;Node prev;Node next;Node() {}Node(int key, int val) {this.key = key;this.val = val;}
}class LRUCache {private Map<Integer, Node> cache = new HashMap<>();private Node head = new Node();private Node tail = new Node();private int capacity;private int size;public LRUCache(int capacity) {this.capacity = capacity;head.next = tail;tail.prev = head;}public int get(int key) {if (!cache.containsKey(key)) {return -1;}Node node = cache.get(key);moveToHead(node);return node.val;}public void put(int key, int value) {if (cache.containsKey(key)) {Node node = cache.get(key);node.val = value;moveToHead(node);} else {Node node = new Node(key, value);cache.put(key, node);addToHead(node);++size;if (size > capacity) {node = removeTail();cache.remove(node.key);--size;}}}private void moveToHead(Node node) {removeNode(node);addToHead(node);}private void removeNode(Node node) {node.prev.next = node.next;node.next.prev = node.prev;}private void addToHead(Node node) {node.next = head.next;node.prev = head;head.next = node;node.next.prev = node;}private Node removeTail() {Node node = tail.prev;removeNode(node);return node;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/
C++
struct Node {int k;int v;Node* prev;Node* next;Node(): k(0), v(0), prev(nullptr), next(nullptr) {}Node(int key, int val): k(key), v(val), prev(nullptr), next(nullptr) {}
};class LRUCache {
public:LRUCache(int capacity): cap(capacity), size(0) {head = new Node();tail = new Node();head->next = tail;tail->prev = head;}int get(int key) {if (!cache.count(key)) return -1;Node* node = cache[key];moveToHead(node);return node->v;}void put(int key, int value) {if (cache.count(key)) {Node* node = cache[key];node->v = value;moveToHead(node);} else {Node* node = new Node(key, value);cache[key] = node;addToHead(node);++size;if (size > cap) {node = removeTail();cache.erase(node->k);--size;}}}private:unordered_map<int, Node*> cache;Node* head;Node* tail;int cap;int size;void moveToHead(Node* node) {removeNode(node);addToHead(node);}void removeNode(Node* node) {node->prev->next = node->next;node->next->prev = node->prev;}void addToHead(Node* node) {node->next = head->next;node->prev = head;head->next = node;node->next->prev = node;}Node* removeTail() {Node* node = tail->prev;removeNode(node);return node;}
};/*** Your LRUCache object will be instantiated and called as such:* LRUCache* obj = new LRUCache(capacity);* int param_1 = obj->get(key);* obj->put(key,value);*/
Go
type node struct {key, val intprev, next *node
}type LRUCache struct {capacity intcache map[int]*nodehead, tail *node
}func Constructor(capacity int) LRUCache {head := new(node)tail := new(node)head.next = tailtail.prev = headreturn LRUCache{capacity: capacity,cache: make(map[int]*node, capacity),head: head,tail: tail,}
}func (this *LRUCache) Get(key int) int {n, ok := this.cache[key]if !ok {return -1}this.moveToFront(n)return n.val
}func (this *LRUCache) Put(key int, value int) {n, ok := this.cache[key]if ok {n.val = valuethis.moveToFront(n)return}if len(this.cache) == this.capacity {back := this.tail.prevthis.remove(back)delete(this.cache, back.key)}n = &node{key: key, val: value}this.pushFront(n)this.cache[key] = n
}func (this *LRUCache) moveToFront(n *node) {this.remove(n)this.pushFront(n)
}func (this *LRUCache) remove(n *node) {n.prev.next = n.nextn.next.prev = n.prevn.prev = niln.next = nil
}func (this *LRUCache) pushFront(n *node) {n.prev = this.headn.next = this.head.nextthis.head.next.prev = nthis.head.next = n
}
TypeScript
class LRUCache {capacity: number;map: Map<number, number>;constructor(capacity: number) {this.capacity = capacity;this.map = new Map();}get(key: number): number {if (this.map.has(key)) {const val = this.map.get(key)!;this.map.delete(key);this.map.set(key, val);return val;}return -1;}put(key: number, value: number): void {this.map.delete(key);this.map.set(key, value);if (this.map.size > this.capacity) {this.map.delete(this.map.keys().next().value);}}
}/*** Your LRUCache object will be instantiated and called as such:* var obj = new LRUCache(capacity)* var param_1 = obj.get(key)* obj.put(key,value)*/
Rust
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;struct Node {key: i32,value: i32,prev: Option<Rc<RefCell<Node>>>,next: Option<Rc<RefCell<Node>>>,
}impl Node {#[inline]fn new(key: i32, value: i32) -> Self {Self {key,value,prev: None,next: None,}}
}struct LRUCache {capacity: usize,cache: HashMap<i32, Rc<RefCell<Node>>>,head: Option<Rc<RefCell<Node>>>,tail: Option<Rc<RefCell<Node>>>,
}/*** `&self` means the method takes an immutable reference.* If you need a mutable reference, change it to `&mut self` instead.*/
impl LRUCache {fn new(capacity: i32) -> Self {Self {capacity: capacity as usize,cache: HashMap::new(),head: None,tail: None,}}fn get(&mut self, key: i32) -> i32 {match self.cache.get(&key) {Some(node) => {let node = Rc::clone(node);self.remove(&node);self.push_front(&node);let value = node.borrow().value;value}None => -1,}}fn put(&mut self, key: i32, value: i32) {match self.cache.get(&key) {Some(node) => {let node = Rc::clone(node);node.borrow_mut().value = value;self.remove(&node);self.push_front(&node);}None => {let node = Rc::new(RefCell::new(Node::new(key, value)));self.cache.insert(key, Rc::clone(&node));self.push_front(&node);if self.cache.len() > self.capacity {let back_key = self.pop_back().unwrap().borrow().key;self.cache.remove(&back_key);}}};}fn push_front(&mut self, node: &Rc<RefCell<Node>>) {match self.head.take() {Some(head) => {head.borrow_mut().prev = Some(Rc::clone(node));node.borrow_mut().prev = None;node.borrow_mut().next = Some(head);self.head = Some(Rc::clone(node));}None => {self.head = Some(Rc::clone(node));self.tail = Some(Rc::clone(node));}};}fn remove(&mut self, node: &Rc<RefCell<Node>>) {match (node.borrow().prev.as_ref(), node.borrow().next.as_ref()) {(None, None) => {self.head = None;self.tail = None;}(None, Some(next)) => {self.head = Some(Rc::clone(next));next.borrow_mut().prev = None;}(Some(prev), None) => {self.tail = Some(Rc::clone(prev));prev.borrow_mut().next = None;}(Some(prev), Some(next)) => {next.borrow_mut().prev = Some(Rc::clone(prev));prev.borrow_mut().next = Some(Rc::clone(next));}};}fn pop_back(&mut self) -> Option<Rc<RefCell<Node>>> {match self.tail.take() {Some(tail) => {self.remove(&tail);Some(tail)}None => None,}}
}
C#
public class LRUCache {class Node {public Node Prev;public Node Next;public int Key;public int Val;}private Node head = new Node();private Node tail = new Node();private Dictionary<int, Node> cache = new Dictionary<int, Node>();private readonly int capacity;private int size;public LRUCache(int capacity) {this.capacity = capacity;head.Next = tail;tail.Prev = head;}public int Get(int key) {Node node;if (cache.TryGetValue(key, out node)) {moveToHead(node);return node.Val;}return -1;}public void Put(int key, int Val) {Node node;if (cache.TryGetValue(key, out node)) {moveToHead(node);node.Val = Val;} else {node = new Node() { Key = key, Val = Val };cache.Add(key, node);addToHead(node);if (++size > capacity) {node = removeTail();cache.Remove(node.Key);--size;}}}private void moveToHead(Node node) {removeNode(node);addToHead(node);}private void removeNode(Node node) {node.Prev.Next = node.Next;node.Next.Prev = node.Prev;}private void addToHead(Node node) {node.Next = head.Next;node.Prev = head;head.Next = node;node.Next.Prev = node;}private Node removeTail() {Node node = tail.Prev;removeNode(node);return node;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.Get(key);* obj.Put(key,Val);*/
Swift
class Node {var key: Intvar val: Intvar prev: Node?var next: Node?init() {self.key = 0self.val = 0}init(_ key: Int, _ val: Int) {self.key = keyself.val = val}
}class LRUCache {private var cache = [Int: Node]()private let head: Nodeprivate let tail: Nodeprivate let capacity: Intprivate var size: Intinit(_ capacity: Int) {self.capacity = capacityself.size = 0self.head = Node()self.tail = Node()head.next = tailtail.prev = head}func get(_ key: Int) -> Int {guard let node = cache[key] else {return -1}moveToHead(node)return node.val}func put(_ key: Int, _ value: Int) {if let node = cache[key] {node.val = valuemoveToHead(node)} else {let newNode = Node(key, value)cache[key] = newNodeaddToHead(newNode)size += 1if size > capacity {let tail = removeTail()cache.removeValue(forKey: tail.key)size -= 1}}}private func moveToHead(_ node: Node) {removeNode(node)addToHead(node)}private func removeNode(_ node: Node) {node.prev?.next = node.nextnode.next?.prev = node.prev}private func addToHead(_ node: Node) {node.next = head.nextnode.prev = headhead.next?.prev = nodehead.next = node}private func removeTail() -> Node {let node = tail.prev!removeNode(node)return node}
}/*** Your LRUCache object will be instantiated and called as such:* let obj = LRUCache(capacity)* let ret_1: Int = obj.get(key)* obj.put(key, value)*/
相关文章:
剑指 Offer II 031. 最近最少使用缓存
comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20031.%20%E6%9C%80%E8%BF%91%E6%9C%80%E5%B0%91%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98/README.md 剑指 Offer II 031. 最近最少使用缓存 题目描述 运用所掌握的…...
Windows 11【1001问】查看Windows 11 版本的18种方法
随着技术的飞速发展,操作系统作为连接硬件与软件的核心桥梁,其版本管理和更新变得尤为重要。对于用户而言,了解自己设备上运行的具体Windows 11版本不仅有助于优化系统性能,还能确保安全性和兼容性。然而,不同场景和需…...
小程序性能优化-预加载
在微信小程序中,数据预加载是提升用户体验的重要优化手段。以下是处理数据预加载的完整方案: 一、预加载的适用场景 跳转页面前的数据准备 如从列表页进入详情页前,提前加载详情数据首屏加载后的空闲时间 在首页加载完成后,预加载…...
vue3中展示markdown格式文章的三种形式
一、安装 # 使用 npm npm i kangc/v-md-editor -S# 使用yarn yarn add kangc/v-md-editor二、三种实现形式 1、编辑器的只读模式 main.ts文件中配置: import VMdEditor from kangc/v-md-editor; import kangc/v-md-editor/lib/style/base-editor.css;const app …...
(视频教程)Compass代谢分析详细流程及python版-R语言版下游分析和可视化
不想做太多的前情解说了,有点累了,做了很久的内容,包括整个分析,从软件安装和报错解决到后期下游python版-R语言版下游分析和可视化!单细胞代谢分析我们写过很多了,唯独少了最“高级”的compass,…...
文件描述符与重定向
1. open系统调用 在 Linux 中, open() 系统调用用于打开一个文件或设备,并返回一个文件描述符,通过该描述符可以进行文件读写操作。open() 可以用于创建新文件或打开已存在的文件,具体行为取决于传递给它的参数。 需要包含的头文件…...
为什么深度学习选择Tensor而非NumPy数组?核心优势深度解析
简短总结: 支持 GPU 加速:Tensor 提供对 GPU 的原生支持,能够有效加速计算,而 NumPy 则通常只能在 CPU 上运行。支持自动求导:深度学习模型的训练依赖于参数的优化,而 Tensor 提供了自动求导功能ÿ…...
python把html网页转换成pdf标题没有乱码,正文都乱码
在使用Python将HTML网页转换成PDF时,遇到标题没有乱码但正文乱码的问题,通常是由于字符编码处理不当或字体支持问题导致的。以下是一些可能的原因和解决方案: 原因分析 字符编码不匹配: HTML文件的编码与PDF转换工具或库所使用的…...
基于fast-whisper模型的语音识别工具的设计与实现
目录 摘 要 第1章 绪 论 1.1 论文研究主要内容 1.1.1模型类型选择 1.1.2开发语言的选择 1.2 国内外现状 第2章 关键技术介绍 2.1 关键性开发技术的介绍 2.1.1 Faster-Whisper数据模型 2.1.2 Django 第3章 系统分析 3.1 构架概述 3.1.1 功能构架 3.1.2 模块需求描述 3.2 系统开…...
详解:事务注解 @Transactional
创作内容丰富的干货文章很费心力,感谢点过此文章的读者,点一个关注鼓励一下作者,激励他分享更多的精彩好文,谢谢大家! Transactional 是 Spring Framework 中常用的注解之一,它可以被用于管理事务。通过使用…...
场内、场外期权怎么开户?期权佣金是多少?
期权交易需要一定的知识和经验,以有效管理风险和制定策略。 场内期权开户(以50ETF为例) 场内期权开户的各种方式大差不差,咱们就先以50ETF期权为例子看下。 场内期权开户条件包括: 首先是资金的要求,50万…...
Linux:进程概念
目录 1 冯诺依曼体系 2 操作系统(Operator System) 3 如何理解管理 3.1计算机管理硬件 3.2 管理逻辑图 3.3 怎样管理 4 什么是进程? 5 查看进程 5.1 ps ajx显示所有进程信息 5.2 /proc(内存文件系统) 5.2.1 ls /proc/PID 5.2.2 ls /proc/PID -al 5…...
Rabbit MQ 高频面试题【刷题系列】
文章目录 一、公司生产环境用的什么消息中间件?二、Kafka、ActiveMQ、RabbitMQ、RocketMQ有什么优缺点?三、解耦、异步、削峰是什么?四、消息队列有什么缺点?五、RabbitMQ一般用在什么场景?六、简单说RabbitMQ有哪些角…...
破解密码防线:渗透测试中的密码攻击手法汇总
密码是网络安全中的一道重要防线,然而,若密码策略不严密,往往会为攻击者提供可乘之机。本文将简要介绍渗透测试中关于密码的几种常见攻击思路和手法。 1. 确认使用默认及常见的账号密码 在渗透测试的初期,攻击者通常会尝试使用系…...
大模型在白血病诊疗全流程风险预测与方案制定中的应用研究
目录 一、绪论 1.1 研究背景与意义 1.2 国内外研究现状 1.3 研究目的与内容 二、大模型技术与白血病相关知识 2.1 大模型技术原理与特点 2.2 白血病的病理生理与诊疗现状 三、术前风险预测与手术方案制定 3.1 术前数据收集与预处理 3.2 大模型预测术前风险 3.3 根据…...
5-2JVM内存的各种应用
一、堆区(Heap)——对象实例的存储池 实际应用场景: 对象实例化:所有通过 new 关键字创建的对象实例均存储在堆中。例如: java Person person new Person(“张三”); // person对象实例分配在堆区1,4,6 大对象直…...
【NLP 28、一文速通NLP文本分类任务 —— 深度学习】
目录 一、深度学习 — pipeline 流水线 1.配置文件 config.py Ⅰ、路径相关 Ⅱ、模型相关 Ⅲ、训练相关 2.数据加载 loader.py Ⅰ、类初始化 Ⅱ、加载数据并预处理 Ⅲ、文本编码 Ⅳ、对输入序列截断或填充 Ⅴ、返回数据长度 Ⅵ、返回对应索引位置元素 Ⅶ、加载词表 Ⅷ、封装数据…...
nvidia驱动更新,centos下安装openwebui+ollama(非docker)
查看centos内核版本 uname -a cat /etc/redhat-release下载对应的程序(这个是linux64位版本通用的) https://cn.download.nvidia.cn/tesla/550.144.03/NVIDIA-Linux-x86_64-550.144.03.run cudnn想办法自己下一下,我这里是12.x和11.x通用的…...
UnrealEngine UE5 可视化 从地球观察火星 金星 土星 运动轨迹
视频参考:https://www.bilibili.com/video/BV1KpXSYdEdo/ 从地球观察土星的运动轨迹 从地球观察火星 轨迹 从地球观察金星的运动轨迹...
蓝桥杯 五子棋对弈
五子棋对弈 问题描述 “在五子棋的对弈中,友谊的小船说翻就翻?” 不!对小蓝和小桥来说,五子棋不仅是棋盘上的较量,更是心与心之间的沟通。这两位挚友秉承着"友谊第一,比赛第二"的宗旨ÿ…...
springmvc热点面试题开胃菜
1. Spring MVC的核心组件有哪些?它们的作用是什么? 答案: Spring MVC的核心组件包括以下部分,每个组件都有其特定的作用: DispatcherServlet: 前端控制器,是Spring MVC的核心。它负责接收所有H…...
关于深度学习的一份介绍
在这篇文章中,我将介绍有关深度学习的东西,主要是它与神经网络的关系、目前主要的网络有哪些,以及加深神经网络的意义等。 一、联系 在之前的文章中,我曾介绍过神经网络,而所谓的神经网络其实就是深度学习的一种架构…...
JavaScript系列02-函数深入理解
本文介绍了JavaScript函数相关知识,包括 函数声明与函数表达式 - 解释两者的区别,提升行为,以及使用场景箭头函数特性 - 讲解语法、词法this、不能作为构造函数等特点this绑定机制 - 详细讲解四种绑定规则:默认绑定、隐式绑定、显…...
Netty是怎么实现Java NIO多路复用的?(源码)
目录 NIO多路复用实现事件循环是什么?核心源码(1)调用 NioEventLoopGroup 默认构造器(2)指定 SelectorProvider(3)创建 Selector(4)创建单线程和队列(5&#…...
SourceTree配置SSH步骤详解
1. 生成SSH密钥对 如果尚未生成SSH密钥,需先创建: Windows/macOS/Linux通用方法 打开终端(或Git Bash)。 输入以下命令(替换为你的邮箱): bash 复制 ssh-keygen -t ed25519 -C "your_em…...
Rocky Linux 8.5 6G内存 静默模式(没图形界面)安装Oracle 19C
Oracle19c 下载地址 Database Software Downloads | Oraclehttps://www.oracle.com/database/technologies/oracle-database-software-downloads.html#db_ee 目录 一、准备服务器 1、服务器可以克隆、自己装 2、修改主机名 3、重启 4、关闭selinux 5、关闭防火墙 5.1、…...
免费轻巧多功能 PDF 处理工具:转换、压缩、提取一应俱全
软件技术 今天要给大家分享一款超实用的 PDF 处理工具,它免费又轻巧,如同随时待命的得力小帮手,功能之强大超乎想象,真的值得大家收藏。 这款工具是绿色版软件,解压后开启,满满的 PDF 处理功能便映入眼帘…...
基于ssm的校园跑腿管理系统+vue
作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 系统共有管理员、用户两个角色 管理员主要的功能用户信息管理、任务信息管理、任务类型管理、接单信息管理、公告信息管理、投诉信息管理、公告类型管…...
java数据结构_Map和Set_9.1
1. 搜索树 1.1 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有的结点都小于根结点的值若它的右子树不为空,则右子树上所有的结点都大于根结点的值…...
横向移动靶场-Tr0ll: 3
Tr0ll: 3来自 <Tr0ll: 3 ~ VulnHub> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182,靶场IP192.168.23.187 3,对靶机进行端口服务探测 …...
