牛客Top100热题宝典--第5节

本文介绍了贪心算法在分糖果问题中的应用,以及主持人调度问题的排序+贪心解决方案。同时涵盖了模拟操作如旋转数组和螺旋矩阵,以及LRU和LFU缓存结构的设计与实现,展示了如何在IT技术中运用这些概念解决实际问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

09 贪心算法

BM95 分糖果问题

要求: 时间复杂度为 O(n),空间复杂度为 O(n)

思想:

从左向右遍历,如果a[i+1]>a[i],那么left[i+1]=left[i]+1;

从右向左遍历,如果a[i]>a[i+1],那么right[i]=right[i+1]+1;

之后取max(left[i],right[i])即可。
在这里插入图片描述
在这里插入图片描述

import java.util.*;
public class Solution {
    public int candy (int[] arr) {
        int n = arr.length;
        int res = 0;
        int[] left = new int[n];
        int[] right = new int[n];
        Arrays.fill(left, 1);
        Arrays.fill(right, 1);
        //从小到大
        for (int i = 0; i < n - 1; i++) {
            if (arr[i + 1] > arr[i])
                left[i + 1] = left[i] + 1;
        }
        res =left[n-1];
        //从大到小
        for (int j = n - 2; j >= 0; j--) {
            if (arr[j] > arr[j + 1])
                right[j] = right[j + 1] + 1;
            res += Math.max(left[j], right[j]);
        }
        return res;
    }
}

BM96 主持人调度(二) 【× 难!!!】

复杂度要求:时间复杂度O(nlogn) ,空间复杂度 O(n)

方法:排序+贪心

思想:

  • 首先建立两个数组分别存储开始时间(记为start)和结束时间(记为end)。
  • 然后分别对start和end数组进行排序
  • 接着遍历start数组,判断当前开始时间是否大于等于最小的结束时间,
    • 如果是,则说明当前主持人就可以搞定;
    • 如果否,则需要新增一个主持人,并将end数组下标后移
import java.util.*;
// 这道题没懂??
public class Solution {

    public int minmumNumberOfHost (int n, int[][] startEnd) {
        int[] start = new int[n];
        int[] end = new int[n];
        for (int i = 0; i < n; i++) {
            start[i] = startEnd[i][0];
            end[i] = startEnd[i][1];
        }
        Arrays.sort(start);
        Arrays.sort(end);

        int inx = 0, res = 0;
        for (int i = 0; i < n; i++) {
//             这里如何比较的??
            if (start[i] >= end[inx])
                inx++;
            else
                res++;
        }
        return res;
    }
}

10 模拟

BM97 旋转数组

进阶:空间复杂度 O(1),时间复杂度 O(n)

import java.util.*;
public class Solution {

    public int[] solve (int n, int m, int[] a) {
        if(n<m)
            m%=n;
        reverse(a,0,n-1);
        reverse(a,0,m-1);
        reverse(a,m,n-1);
        return a;
    }

    public void reverse(int[] a,int start,int end){
        while(start<end){
            int tmp=a[start];
            a[start]=a[end];
            a[end]=tmp;
            start++;
            end--;
        }
    }
}

BM98 螺旋矩阵

import java.util.ArrayList;
public class Solution {
    public ArrayList<Integer> spiralOrder(int[][] matrix) {
        ArrayList<Integer> res = new ArrayList<>();
        if (matrix.length == 0) return res;
        int rows = matrix.length;
        int cols = matrix[0].length;
        int left = 0, right = cols - 1, top = 0, bottom = rows - 1;
        int numEle = matrix.length * matrix[0].length;
        while (numEle >= 1) {
            //向右
            for (int i = left; i <= right && numEle >= 1; i++) {
                res.add(matrix[top][i]);
                numEle--;
            }
            top++;
            //向下
            for (int i = top; i <= bottom && numEle >= 1; i++) {
                res.add(matrix[i][right]);
                numEle--;
            }
            right--;
            //向左
            for (int i = right; i >= left && numEle >= 1; i--) {
                res.add(matrix[bottom][i]);
                numEle--;
            }
            bottom--;
            //向上
            for (int i = bottom; i >= top && numEle >= 1; i--) {
                res.add(matrix[i][left]);
                numEle--;
            }
            left++;
        }
        return res;
    }
}

BM99 顺时针旋转矩阵

类似矩阵的转置

要求:空间复杂度 O(n^2),时间复杂度 O(n^2)

进阶:空间复杂度 O(1),时间复杂度 O(n^2)

方法一:使用辅助数组
方法二:用翻转代替旋转

​ 上下翻转,然后沿着主对角线翻转,得到结果
在这里插入图片描述

import java.util.*;
public class Solution {
    public int[][] rotateMatrix(int[][] mat, int n) {
        for (int col = 0; col < n; col++) {
            for (int row = 0; row < n / 2; row++) {
                int tmp = mat[row][col];
                mat[row][col] = mat[n - 1 - row][col];
                mat[n - 1 - row][col] = tmp;
            }
        }
        for (int row = 0; row < n; row++) {
            for (int col = 0; col < row; col++) {
                int tmp = mat[row][col];
                mat[row][col] = mat[col][row];
                mat[col][row] = tmp;
            }
        }
        return mat;
    }
}

BM100 设计LRU缓存结构

【这是非常经典的算法题!! 必须会!!】

方式一:哈希表+双向链表(推荐使用) 【后端面试必须掌握这个】

方式二:LinkedHashMap 【前端面试偷懒写法!!】

import java.util.*;
public class Solution {
    int capacity;
    DLinkedNode head, tail;
    HashMap<Integer, DLinkedNode> map = new HashMap<>();

    //初始化参数
    public Solution(int capacity) {
        this.capacity = capacity;
        //虚head和虚tail
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.pre = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        DLinkedNode node = map.get(key);
        removeNode(node);
        addToHead(node);

        return node.val;
    }

    public void set(int key, int value) {
        if (!map.containsKey(key)) {
            if(map.size()>=capacity){
                DLinkedNode tail = removeTail();
                map.remove(tail.key);
            }
            DLinkedNode newNode = new DLinkedNode(key, value);
            addToHead(newNode);
            map.put(key, newNode);
        } else {
            DLinkedNode node = map.get(key);
            node.val=value;
            removeNode(node);
            addToHead(node);
            map.put(key,node);
        }
    }

    void removeNode(DLinkedNode node) {
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }
    DLinkedNode removeTail(){
        //注意写法!!
        DLinkedNode deadNode = tail.pre;
        removeNode(deadNode);
        return deadNode;
    }

    void addToHead(DLinkedNode node) {
        //注意四个指针的顺序,先右后左,先pre,再next!!!
        node.next = head.next;
        head.next.pre = node;
        head.next = node;
        node.pre = head;
    }

    //定义链表节点
    class DLinkedNode {
        int key;
        int val;
        DLinkedNode next;
        DLinkedNode pre;

        DLinkedNode() {
        }
        DLinkedNode(int key, int value) {
            this.key = key;
            this.val = value;
        }
    }
}

//方式二
import java.util.*;
public class Solution {
    LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>();
    int capacity;
    public Solution(int capacity) {
        this.capacity = capacity;
    }

    public int get(int key) {
        if(!map.containsKey(key)) {
            return -1;
        }
        int val = map.get(key);
        map.remove(key);
        map.put(key, val);
        return val;
    }

    public void set(int key, int value) {
        if (!map.containsKey(key)) {
            if (map.size() >= capacity) {
                map.remove(map.keySet().iterator().next());
            }
            map.put(key, value);
        } else {
            map.remove(key);
            map.put(key, value);
        }
    }
}

BM101 设计LFU缓存结构

函数 getput 必须以 O(1) 的平均时间复杂度运行。

要求:get和set的时间复杂度都是 O(logn),空间复杂度是 O(n)

方法一:哈希表 + 平衡二叉树
在这里插入图片描述

import java.util.*;
class Solution {

    int time;
    int capacity;
    //map只是存储数据
    Map<Integer, Node> map = new HashMap<>();
    //TreeSet存储失效信息,包括当前时间cnt,
    TreeSet<Node> set = new TreeSet<>();

    public  int get(int key) {
        if (!map.containsKey(key)) return -1;
        Node cache = map.get(key);
        //更新set,并且更新map中key对应的value
        set.remove(cache);
        cache.freq += 1;
        cache.time = ++time;
        set.add(cache);

        map.put(key, cache);
        return cache.value;
    }

    public  void put(int key, int value, int k) {
        if (!map.containsKey(key)) {
            if (map.size() == k) {
                //如果map已经满了,那么删除tree中最近最少使用的缓存
                map.remove(set.first().key);
                set.remove(set.first());
            }
            Node cache = new Node(1, ++time, key, value);
            map.put(key, cache);
            set.add(cache);
        } else {
            Node cache = map.get(key);
            set.remove(cache);
            cache.freq += 1;
            cache.time = ++time;
            cache.value = value;
            set.add(cache);
            map.put(key, cache);
        }

    }

    class Node implements Comparable<Node> {
        //freq 表示缓存使用的频率,time 表示缓存的使用时间
        int freq, time, key, value;

        Node(int freq, int time, int key, int value) {
            this.freq = freq;
            this.time = time;
            this.key = key;
            this.value = value;
        }

        @Override
        public boolean equals(Object o) {
       Node node = (Node) o;
            return freq == node.freq && time == node.time;
        }

        @Override
        public int hashCode() {
            return freq * 1000000007 + time;
        }

        @Override
        public int compareTo(Node node) {
            return freq == node.freq ? time - node.time : freq - node.freq;
        }
    }

    public  int[] LFU(int[][] operators, int k) {
        capacity = k;
        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < operators.length; i++) {
            if (operators[i][0] == 1) {
                put(operators[i][1], operators[i][2], k);
            } else {
                res.add(get(operators[i][1]));
            }
        }
        int[] ans = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            ans[i] = res.get(i);
        }
        return ans;
    }
}

方法二:双哈希表

O(1) 解法

import java.util.*;
public class Solution {
     int min;  // 存储当前最小频次
    // 存储缓存的内容
     HashMap<Integer, Node> map = new HashMap<>();
    // 存储每个频次对应的双向链表
     HashMap<Integer, LinkedHashSet<Node>> freqMap = new HashMap<>();  

    public  int get(int key) {
        if (!map.containsKey(key))
            return -1;
        Node node = map.get(key);
        //更新set
        freqInc(node);

        return node.value;
    }

    public void set(int key, int value, int k) {
        if (!map.containsKey(key)) {
            if (map.size() >= k) {
                Node deadNode = removeNode();
                map.remove(deadNode.key);
            }
            Node newNode = new Node(key, value);
            map.put(key, newNode);
            addNode(newNode);
        } else {
            Node node = map.get(key);
            node.value = value;
            freqInc(node);
        }
    }

    public void addNode(Node newNode) {
        LinkedHashSet<Node> set = freqMap.get(1);
        if (set == null) {
            set = new LinkedHashSet<>();
            freqMap.put(1, set);
        }
        set.add(newNode);
        min = 1;
    }

    //删除freqMap的LinkedHashSet对应的freq最小的节点
     Node removeNode() {
        LinkedHashSet<Node> set = freqMap.get(min);
        Node deadNode = set.iterator().next();
        set.remove(deadNode);
        return deadNode;
    }

     public void freqInc(Node node) {
        //从原set中删除节点,更新freq,加入新的set中
        int freq = node.freq;
        LinkedHashSet<Node> set = freqMap.get(freq);
        set.remove(node);
        //???
        if (freq == min && set.size() == 0) {
            min = freq + 1;
        }
        node.freq++;
        LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
        if (newSet == null) {
            newSet = new LinkedHashSet<>();
            freqMap.put(freq + 1, newSet);
        }
        newSet.add(node);
    }

     class Node {
        int key;
        int value;
        int freq = 1;

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

    public int[] LFU(int[][] operators, int k) {
        ArrayList<Integer> res = new ArrayList<>();
        for (int i = 0; i < operators.length; i++) {
            if (operators[i][0] == 1) {
                set(operators[i][1], operators[i][2], k);
            } else {
                res.add(get(operators[i][1]));
            }
        }
        int[] ans = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            ans[i] = res.get(i);
        }
        System.out.println(Arrays.toString(ans));
        return ans;
    }
}

力扣

class LFUCache {
    //存储全部的数据
    HashMap<Integer, Node> map;
    //将数据根据频率来分类存储
    HashMap<Integer, LinkedHashSet<Node>> freqMap;
    int min;
    int size;
    int capacity;

    public LFUCache(int capacity) {
        map = new HashMap<>();
        freqMap = new HashMap<>();
        this.capacity=capacity;
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            return -1;
        }
        Node node = map.get(key);
        freqInc(node);
        return node.value;
    }

    public void put(int key, int value) {
        if(capacity==0) return;
        
        if (!map.containsKey(key)) {
            if (map.size() >= capacity) {
                Node deadNode = removeNode();
                map.remove(deadNode.key);
            }
            Node newNode = new Node(key, value);
            map.put(key, newNode);
            addNode(newNode);
        } else {
            Node node = map.get(key);
            node.value = value;
            freqInc(node);
        }
    }

    public void addNode(Node newNode) {
        LinkedHashSet<Node> set = freqMap.get(1);
        if (set == null) {
            set = new LinkedHashSet<>();
            freqMap.put(1, set);
        }
        set.add(newNode);
        min=1;
    }

    //删除freqmap最小频率对应的节点
    public Node removeNode() {
        LinkedHashSet<Node> set = freqMap.get(min);
        Node deadnNode = set.iterator().next();
        set.remove(deadnNode);
        return deadnNode;
    }

    public void freqInc(Node node) {
        int freq = node.freq;
        //从原set中删除对应节点
        LinkedHashSet<Node> set = freqMap.get(freq);
        set.remove(node);
        if (freq == min && set.isEmpty()) {
            min = freq + 1;
        }
        //获取频次+1对应的set,将节点加入
        node.freq++;
        LinkedHashSet<Node> newSet = freqMap.get(freq + 1);
        if (newSet == null) {
            newSet = new LinkedHashSet<>();
            freqMap.put(freq + 1, newSet);
        }
        newSet.add(node);
    }

    class Node {
        int key;
        int value;
        int freq = 1;

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

整理不易🚀🚀,关注和收藏后拿走📌📌欢迎留言🧐👋📣
欢迎专注我的公众号AdaCoding 和 Github:AdaCoding123
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值