zoukankan      html  css  js  c++  java
  • 数据结构与算法之美-散列表(下)

    散列表和链表组合使用

    LRU缓存淘汰算法

    借助散列表,我们可以把LRU缓存淘汰算法的时间复杂度降为O(1)。

    一个缓冲cache系统主要包含以下操作

    • 往缓存中添加一个数据;
    • 从缓存中删除一个数据;
    • 在缓存中查找一个数据。

    单纯采用链表,时间复杂度只能是O(n)。

    将散列表和双向链表结合,就可以降为O(1),其结构如下图所示:

    其中,我们使用双向链表存储数据,data存储数据,prev前驱指针,next后继指针。

    此外,新增加了hnext指针,这个指针就是链表法散列表中的拉链的后继指针。

    如何做到O(1)

    查找

    因为是散列表所以查找一个数据的操作时间复杂度就接近于O(1)。

    删除

    删除一个数据,我们借助散列表再O(1)的时间复杂度里找到该结点,

    而双向链表有前驱指针,可以直接删除该节点,时间复杂度为O(1)。

    添加

    添加一个数据比较复杂,首先要看其是否已经在缓存中,

    如果在就将其移动到双向链表的尾部,如果不在就检查缓存满了没,

    满了就删除双向链表的头结点,再将数据放到双向链表的尾部,

    如果没有满就直接将数据放大双向链表的尾部。

    以上操作中,设计查找的操作是散列表完成的,删除节点、插入节点是双向链表完成的,所以时间复杂度是O(1)。

    Redis有序集合

     在有序集合中,每个成员对象有两个重要的属性,键key和分值score。

    我们不仅需要key来查找数据,还会需要用score查找数据。

    细化一下Redis有序集合的操作:

    • 添加一个成员对象;
    • 按照键值来删除一个成员对象;
    • 按照键值来查找一个成员对象;
    • 按照分值区间查找数据,比如查找积分在[100, 356] 之间的成员对象;
    • 按照分值从小到大排序成员变量;

    如果只按照分支将成员对象组织成跳表的结构,那么按照键值删除、查询对象就会很慢。

    我们可以按照键值构建一个散列表,这样按照key来删除、查找一个对象的时间复杂度就都变成了O(1)。

    Java中的LinkedHashMap

    Jva中的LinkedHashMap中的Linked并不是链表法表示散列表的意思,而是双向链表和散列表结合。

    LinkedHashMap本身就是一个支持LRU缓存淘汰策略的缓存系统,其数据的存取移动删除规则和LRU一样。

    思考

    今天讲的几个散列表和链表结合使用的例子里,我们用的都是双向链表。如果把双向链表改成单链表,还能否正常工作呢?为什么呢?

    假设猎聘网有 10 万名猎头,每个猎头都可以通过做任务(比如发布职位)来积累积分,然后通过积分来下载简历。

    假设你是猎聘网的一名工程师,如何在内存中存储这 10 万个猎头 ID 和积分信息,让它能够支持这样几个操作:

    • 根据猎头的 ID 快速查找、删除、更新这个猎头的积分信息;
    • 查找积分在某个区间的猎头 ID 列表;
    • 查找按照积分从小到大排名在第 x 位到第 y 位之间的猎头 ...
  • 相关阅读:
    (转)编写高质量高效率的SharePoint应用程序
    转:我眼中的Visual Studio 2010架构工具
    Windows 7 x64/Windows 2008 : The ‘Microsoft.Jet.OLEDB.4.0′ provider is not registered on the local machine.
    转:构建高性能ASP.NET站点之一 剖析页面的处理过程(前端)
    HTML5 到底是什么?
    使用eval()解析JSON格式字符串应注意的问题
    使用HTML5进行地理位置定位。误差在+500m
    LAST DAY
    javacript获取obj的长度
    通过 JSON 字符串来创建对象
  • 原文地址:https://www.cnblogs.com/errornull/p/9952162.html
Copyright © 2011-2022 走看看