设计一个验证系统[LRU思想--双向链表+hash]

前言

设计一个验证码系统,给定生成的验证码和时间,主要设计精力集中在有效验证码的数据结构设计,要求存储好生成的验证码;可重置任何未过期的验证码;统计未过期的验证码数量。

一、验证码系统

在这里插入图片描述

二、双向链表+hash

1、idea

由于generate需求,所以需要Node数据结构存储验证码和生成时间;
由于renew会随机删除中间节点,所以采用链表存储,为了降低删除时间复杂度,结合hash;

2、go

type AuthenticationManager struct {
    timeToLive int
    size int
    dummyHead *ListNode
    dummyTail *ListNode
    tokens map[string]*ListNode
}


func Constructor(timeToLive int) AuthenticationManager {
    head := &ListNode{}
    tail := &ListNode{}
    head.Next = tail
    tail.Prev = head
    return AuthenticationManager{
        timeToLive:timeToLive,
        dummyHead:head,
        dummyTail:tail,
    }
}


func (this *AuthenticationManager) Generate(tokenId string, currentTime int)  {
    ln := &ListNode{
        Val:Node{
            tokenId:tokenId,
            time:currentTime,
        },
    }
    this.link(ln)
    // 数据同步
    this.tokens[tokenId] = ln
    this.size++
}


func (this *AuthenticationManager) Renew(tokenId string, currentTime int)  {
    if ln,ok := this.tokens[tokenId];ok{
        if ln.Val.time + this.timeToLive > currentTime {
            this.unlink(ln)
            this.link(ln)
        }
    }
}

// 清理数据
func (this *AuthenticationManager) CountUnexpiredTokens(currentTime int) int {
    p := this.dummyHead.Next
    for ;p != this.dummyTail; {
        if p.Val.time + this.timeToLive > currentTime {
            break
        }
        delete(this.tokens,p.Val.tokenId)
        this.size--

        p = p.Next
        this.unlink(p.Prev)
    }
    return this.size
}
func(this *AuthenticationManager) link(ln *ListNode){
    front := this.dummyTail.Prev

    front.Next = ln;
    ln.Prev = front
    ln.Next = this.dummyTail
    this.dummyTail.Prev = ln
}
func(this *AuthenticationManager) unlink(ln *ListNode){
    front := ln.Prev

    front.Next = ln.Next
    ln.Next.Prev = front
}
// currentTime严格递增,所以不用优先队列排序;
// 由于renew的问题,需要将中间节点剔除放到后面保持有序,所以以队列的特点无法做到。
// 采用双向链表 + hash来做到快速删除。

type Node struct{
    tokenId string
    time int
}
type ListNode struct{
    Val Node
    Prev *ListNode
    Next *ListNode
}
/**
 * Your AuthenticationManager object will be instantiated and called as such:
 * obj := Constructor(timeToLive);
 * obj.Generate(tokenId,currentTime);
 * obj.Renew(tokenId,currentTime);
 * param_3 := obj.CountUnexpiredTokens(currentTime);
 */

总结

1)分析问题的过程,主要目的是存储有效时间内的验证码,联想到优先队列存储验证码和生成时间;由于currentTime的严格递增,所以自然有序,不需要优先队列,采用普通队列;由于需要重置有效时间内的任何验证码,就会涉及到中间节点的删除,这不符合队列的特点,所以转向易删除的链表,这就很像LRU思想,所以采用经典的双向链表+hash快速访问节点。
2)go的结构体字段就可以比作Java的属性,但是不像Java的class那样特别,在方法中可以直接访问变量,省略this,go的结构体方法和函数本质一样,所有需要的参数都要传递。
3)go不像Java new完,自动调用构造函数初始化,而是自己主动调用自定义方法来初始化。

参考文献

[1] LeetCode 设计一个验证系统

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值