LeetCode——数据结构设计

146.LRU缓存

方法
LRU缓存机制可以通过一个哈希表和一个双向链表来实现。我们使用哈希表和双向链表来维护缓存中所有的键值对。

  • 双向链表按照被使用的顺序存储这些键值对。靠近头部的键值对是最近使用的,靠近尾部的节点是最久未使用的。
  • 哈希表通过键值对的键值映射到其在双向链表中的位置。

注意

  1. 每次执行get或者put操作,我们要将操作的键值对所在的节点移动到双向链表的头部。
  2. 在双向链表的实现中,使用伪头部和伪首部标记界限,这样在添加和删除节点时不需要检查相邻链表节点是否存在。

155.最小栈

方法(直接看题解):

  1. 辅助栈中维护与最小栈中元素对应的最小值
  2. 不使用辅助栈,最小栈中存放元素与当前最小值的差值

173.二叉搜索树迭代器

方法:扁平化或迭代。
思路和算法

  • 扁平化指的是对二叉搜索树做一次完全的递归遍历,获取中序遍历的递归结果并保存在数组中。随后,利用得到的数组本身实现迭代器
  • 迭代指的是利用栈这一数据结构,通过迭代的方式对二叉搜索树做中序遍历。此时,我们无需计算出所有的中序遍历结果,只需要实时维护当前栈的状态即可。

208.实现Trie(前缀树)

本质上是二十六叉数。

284.顶端迭代器

最无语的题目,没有之一。

295.数据流的中位数

对顶堆

对于这道题目,我们只需要维护数据流的中位数即可。具体而言,

维护一个大根堆和小根堆。大根堆存放所有小于等于中位数的数字,小根堆存放所有大于等于中位数的数字。

记大根堆的数字个数是x,小根堆的数字个数是y,在添加数字的过程要保证x = yx = y + 1

297.二叉树的序列化与反序列化

序列化:先序遍历,

  1. 若节点不是空节点,把节点值转为字符串,并在结尾处要加上,,表示一个节点。
  2. 若节点是空节点,用字符#表示,并在结尾处加上,

反序列化:先序遍历

  1. 若当前字符不是#,则取出数字,并继续递归
  2. 若当前字符是#,则返回空节点

303.区域和检索-矩阵不可变

一维前缀和。

304.二维区域和检索-矩阵不可变

二维前缀和

341.扁平化嵌套列表迭代器

难点:初始化
成员变量:

  1. vector<int> res;
  2. int idx

由于vector<NestedInteger>的每一个元素可能是整数,也可能是一个列表,因此先判断当前元素是否是整数,如果是整数,把该整数加入到res中;否则利用函数getList()得到vector<NestedInteger>类型的变量并递归处理。


355.设计推特

先罗列一下推特的功能:

  1. 允许用户关注他人
  2. 允许用户取消关注
  3. 允许用户发送推文
  4. 允许用户访问自己以及自己关注的用户发送的最新10条推文

推文有以下限制条件:

  1. 每条推文发送时间一定不同
  2. 每条推文的编号一定不同

根据功能,确定数据变量:

  1. 推特的第一个功能和第二个功能要求推特维护一个用户和其他用户的关系列表,而且方便添加和删除用户。由于哈希表unordered_map<int,set<int>>完美符合要求,因此选择哈希表。
  2. 推特的第三个功能要求推特维护一个用户和他/她发送的推文之间的映射关系。由于哈希表unordered_map<int,vector<int>>完美符合要求,因此选择哈希表。
  3. 推特的第四个功能要求在记录用户所发送的推文时,要额外维护每条推文的时间信息。可以使用int型变量t记录:每次发送推文时,递增t;再用哈希表unordered_map<int,int>维护推文和它所发送的时间的关系。

考虑到时间复杂度,快速查找最新的10条推文是本题的难点。

在本题中,笔者使用了小根堆。具体而言,按照推文发布的时间对符合条件的推文进行排序,堆顶的推文是最早发布的。

为了达到排序的目的,没有使用哈希表维护推文和发布时间的关系,而是在“用户发布推文”时用pair<int,int>记录推文的发布时间和推文id,因此第二个功能所用的数据结构应是unordered_map<int,vector<pair<int,int>>>

按照上述设计,推特的第四个功能实现的具体步骤如下:

  1. 逆序遍历用户自己所发布的推文

    1. 若小根堆中推文的个数小于10,直接把推文加入小根堆
    2. 跳出循环(因为vector<int>维护的推文发布时间是一个有序序列)
  2. 遍历用户关注的用户列表

    1. 逆序遍历关注用户发布的推文
      1. 若小根堆推文个数小于10,直接把推文加入到小根堆中
      2. 若堆顶推文发布时间小于当前推文的发布时间,就把当前堆顶元素弹出,并把当前推文加入到堆中
      3. 若堆顶推文发布时间小于当前推文的发布时间,跳出循环
  3. 把堆中推文的id加入到结果res

  4. 翻转res(题目要求按时间顺序由最近到最远排序)


380.O(1)时间插入、删除和获取随机元素

类中共有三个操作:

  1. 插入
  2. 删除
  3. 等概返回一个随机数

其中,这三个操作都要求 O ( 1 ) O(1) O(1)的时间复杂度。对于 O ( 1 ) O(1) O(1)复杂度的插入和删除,很容易想起一个数据结构:哈希表。对于 O ( 1 ) O(1) O(1)复杂度的等概返回随机数的操作,理想情况是使用支持随机存取的数据结构来实现,如数组和vector(哈希表不支持随机存取)。

将两者结合,可以把哈希表设计为:以val为键,数组下标loc为值。

为了确保严格 O ( 1 ) O(1) O(1),需要申请一个足够大的数组,以及维护一个变量idxidx用于记录已使用的数组的最后一位的下一个为值。


432. 全 O(1) 的数据结构

思路和算法

双向链表 + 哈希表
  1. 链表结点用于存放单词key,以及其计数值count,而且双向链表按照计数值count升序排列。对于返回计数值最大字符串返回计数值最小字符串的操作来说,双向链表保证了 O ( 1 ) O(1) O(1)的时间复杂度。
  2. 哈希表用于存放单词key以及其在双向链表中的结点位置(Doubly_link_list*类型)。哈希表保证了查找操作的 O ( 1 ) O(1) O(1)的时间复杂度。
  3. 对于插入操作和删除操作,双向链表 和 哈希表共同保证了的 O ( 1 ) O(1) O(1)的时间复杂度。

小贴士

  1. 为了避免边界条件的判断,双向链表在实现时可以预设一个头结点和一个尾结点。
  2. 原来笔者认为用哈希表得到str在链表中的位置pos后,还需要从链表头结点开始一一对比,直到找到pos为止。我纯属多此一举。因为双向链表中的每一个结点都保存着其前一个结点和后一个结点的信息,因此只要知道结点位置,其在双向链表中的位置也就唯一确定了,不需要从头结点开始查找。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值