【Day3】每天三算法

【Day3】每天三算法

题目

WC134 设计 LRU

题目链接

题目

设计LRU(最近最少使用)缓存结构,该结构在构造时确定大小,假设大小为 k ,并有如下两个功能

1、 set(key, value):将记录(key, value)插入该结构

2、get(key):返回key对应的value值

提示:

1.某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的,然后都会刷新缓存。

2.当缓存的大小超过k时,移除最不经常使用的记录。

3.输入一个二维数组与k,二维数组每一维有2个或者3个数字,第1个数字为opt,第2,3个数字为key,value

若opt=1,接下来两个整数key, value,表示set(key, value)
若opt=2,接下来一个整数key,表示get(key),若key未出现过或已被移除,则返回-1
对于每个opt=2,输出一个答案

4.为了方便区分缓存里key与value,下面说明的缓存里key用""号包裹

要求:set和get操作复杂度均为O(1)

思考

LRU 中文全称是最近最少用算法

即在队列满的时候呀,将最远一次使用的空间剔除,放入新的空间

LRU 的实现是有固定套路的,我们从数据结构选型实现来讲讲

  • 数据结构选型:

我们需要 Hash 表存储数据,以达到 O(1)的访问时间复杂度

为了是我们的数据有序,还必须借助链表,为了方便增删,需要使用双向链表

为了让 Map 的 val 值形成双向链表,我们还要自己封装 val 值

class Node {
  public int key;
  public int val;
  public Node next,pre;

  public Node(int key, int val) {
    this.key = key;
    this.val = val;
  }

  public Node() {
  }
}

总结一下,设计 LRU 需要的数据结构为:哈希表双向链表(自定义类不是数据结构)

  • 实现方式:

双向链表头为最近访问,尾为最远访问

每当有元素被 set 或者 get 时,都要将该元素置于双向链表头

具体的方式,还是请看代码

题解
public class WC134_LRU {

    public static void main(String[] args) {
        WC134_LRU lru = new WC134_LRU();
        lru.LRU(null,3);
        lru.set(1,1);
        lru.set(2,2);
        lru.set(3,3);
        System.out.println(lru);
        lru.get(2);
        System.out.println(lru);
        lru.set(4,4);
        System.out.println(lru);
    }

    private Map<Integer, Node> map = new HashMap<>();
    private Node head = new Node(-1, -1);
    private Node tail = new Node(-1, -1);
    private int k;

    public int[] LRU (int[][] operators, int k) {
        this.k=k;
        head.next=tail;
        tail.pre=head;
        ArrayList<Integer> res = new ArrayList<>();
        // 1 为 set 2 为 get
        for (int[] operator : operators) {
            if (operator[0]==1) {
                set(operator[1],operator[2]);
            } else {
                res.add(get(operator[1]));
            }
        }
        int[] arr = new int[res.size()];
        for (int i = 0; i < arr.length; i++) {
            arr[i]=res.get(i);
        }
        return arr;
    }

    public void set(int key,int val) {
        // 当前 key 已存在
        if (get(key)!=-1) {
            // 更新 value 值
            map.get(key).val=val;
        } else {
            // 超出空间,移除最不常用
            if (k==map.size()) {
                removeLastUse();
            }
            Node node = new Node(key, val);
            map.put(key,node);
            makeRecent(node);
        }
    }

    public int get(int key) {
        Node node = map.get(key);
        if (node!=null) {
            node.pre.next=node.next;
            node.next.pre=node.pre;
            makeRecent(node);
            return node.val;
        }
        return -1;
    }

    // 去除最近不用的元素
    private void removeLastUse() {
        int rk = tail.pre.key;
        tail.pre.pre.next=tail;
        tail.pre=tail.pre.pre;
        map.remove(rk);
    }

    // 设置为最近访问
    private void makeRecent(Node node) {
        node.pre=head;
        node.next=head.next;
        head.next=node;
        node.next.pre=node;
    }

    class Node {
        public int key;
        public int val;
        public Node next,pre;

        public Node(int key, int val) {
            this.key = key;
            this.val = val;
        }

        public Node() {
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        Node tmpHead=head.next;
        while (tmpHead!=tail) {
            sb.append(tmpHead.val)
                    .append("->");
            tmpHead=tmpHead.next;
        }
        return sb.toString();
    }
}

结果

小结

以后碰到手撕 LRU 的问题,要注意一下几个点:

  • 数据结构选型为 : 哈希表,双向链表
  • val 值要自定义,以满足构建双向链表的条件

WC133 子数组的最大累加和问题

题目链接

题目

给定一个长度为 n 的数组 arr ,返回其中任意子数组的最大累加和

题目保证没有全为负数的数据

思考

我们可以使用暴力算法,两个 for 循环搞定

但是更好的办法是使用动态规划:

dp[i]表示的是包含当前位置的情况下,累加可以获得的最大值
状态转换方程 : dp[i]=max(dp[i],dp[i-1]+dp[i])
题解
public int maxsumofSubarray (int[] arr) {
        int max=arr[0];
        for (int i = 1; i < arr.length; i++) {
            arr[i]=Math.max(arr[i],arr[i]+arr[i-1]);
            max=Math.max(max,arr[i]);
        }
        return max;
    }

WC135 两个链表生成相加链表

题目

假设链表中每一个节点的值都在 0 - 9 之间,那么链表整体就可以代表一个整数。

给定两个这种链表,请生成代表两个整数相加值的结果链表。

例如:链表 1 为 9->3->7,链表 2 为 6->3,最后生成新的结果链表为 1->0->0->0。

思考

步骤如下:

1、两个链表反转

2、遍历相加,如果结果为两位数,保留十位(额外设置一个变量保存)

3、不断进行相加,同时要加上前一位相加后多出的位数

4、当有一方结束的时候,另一方继续相加

5、当所有链表都结束后,如果溢出保留为不为0,再创建一个节点进行尾插

6、链表反转

题解
public class WC135_addInList {

    public static void main(String[] args) {
        WC135_addInList wc135_addInList = new WC135_addInList();
        ListNode n1 = new ListNode(1);
        ListNode n2 = new ListNode(2);
        ListNode n3 = new ListNode(3);
        n1.next=n2;
        n2.next=n3;


        ListNode n4 = new ListNode(4);
        ListNode n5 = new ListNode(5);
        n4.next=n5;


        ListNode head = wc135_addInList.addInList(n1, n4);
        while (head!=null) {
            System.out.print(head.val+"   ");
            head=head.next;
        }

    }

    public ListNode addInList (ListNode head1, ListNode head2) {
        head1 = reverse(head1);
        head2 = reverse(head2);
        // 保存溢出位
        int cache = 0;
        ListNode head = new ListNode();
        ListNode tail = head;
        while (head1!=null && head2!=null) {
            int sum = head1.val + head2.val + cache;
            cache=sum/10;
            ListNode node = new ListNode();
            node.val=sum%10;
            // 尾插
            tail.next=node;
            tail=tail.next;
            head1=head1.next;
            head2=head2.next;
        }
        while (head1!=null) {
            int sum = head1.val + cache;
            cache=sum/10;
            ListNode node = new ListNode();
            node.val=sum%10;
            // 尾插
            tail.next=node;
            tail=tail.next;
            head1=head1.next;
        }
        while (head2!=null) {
            int sum = head2.val + cache;
            cache=sum/10;
            ListNode node = new ListNode();
            node.val=sum%10;
            // 尾插
            tail.next=node;
            tail=tail.next;
            head2=head2.next;
        }
        if (cache!=0) {
            ListNode node = new ListNode();
            node.val=cache;
            // 尾插
            tail.next=node;
            tail=tail.next;
        }
        ListNode tmp = head.next;
        head.next=null;
        return reverse(tmp);
    }

    public ListNode reverse(ListNode head) {
        ListNode pre=null,curr=head;
        while (curr!=null) {
            ListNode tmp = curr.next;
            curr.next=pre;
            pre=curr;
            curr=tmp;
        }
        return pre;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FARO_Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值