2025高频面试算法总结篇【其他】


直接刷题链接直达


LRU Cache

请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

class LRUCache {
    private final int capacity;
    // 双向链表 put 操作 会把数据放表尾-》最近最久未使用在表头(remove去掉表头)
    private final LinkedHashMap<Integer, Integer> data;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.data = new LinkedHashMap<>(capacity);
    }

    public int get(int key) {
        // 这个key存在 =》 删除,放表尾
        if (data.containsKey(key)) {
            Integer value = data.remove(key);
            data.put(key, value);
            return value;
        }

        return -1;
    }

    public void put(int key, int value) {
        if (data.containsKey(key)) {
            data.remove(key);
            data.put(key, value);
            return;
        }

        if (data.size() == capacity) {
            // 删除表头元素
            data.remove(data.keySet().iterator().next());
        }

        data.put(key, value);
    }
}

补充:

class LRUCache {
    private static class Node {
        int key, value;
        Node prev, next;

        Node(int k, int v) {
            key = k;
            value = v;
        }
    }

    private final int capacity;
    private final Node dummy = new Node(0, 0); // 哨兵节点
    private final Map<Integer, Node> keyToNode = new HashMap<>();

    public LRUCache(int capacity) {
        this.capacity = capacity;
        dummy.prev = dummy;
        dummy.next = dummy;
    }

    public int get(int key) {
        Node node = getNode(key); // getNode 会把对应节点移到链表头部
        return node != null ? node.value : -1;
    }

    public void put(int key, int value) {
        Node node = getNode(key);
        if (node != null) { // 有这本书
            node.value = value; // 更新 value
            return;
        }
        node = new Node(key, value); // 新书
        keyToNode.put(key, node);
        pushFront(node); // 放在最上面
        if (keyToNode.size() > capacity) { // 书太多了
            Node backNode = dummy.prev;
            keyToNode.remove(backNode.key);
            remove(backNode); // 去掉最后一本书
        }
    }

    // 获取 key 对应的节点,同时把该节点移到链表头部
    private Node getNode(int key) {
        if (!keyToNode.containsKey(key)) { // 没有这本书
            return null;
        }
        Node node = keyToNode.get(key); // 有这本书
        remove(node); // 把这本书抽出来
        pushFront(node); // 放在最上面
        return node;
    }

    // 删除一个节点(抽出一本书)
    private void remove(Node x) {
        x.prev.next = x.next;
        x.next.prev = x.prev;
    }

    // 在链表头添加一个节点(把一本书放在最上面)
    private void pushFront(Node x) {
        x.prev = dummy;
        x.next = dummy.next;
        x.prev.next = x;
        x.next.prev = x;
    }
}


买卖股票的最佳时机系列

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

class Solution {
    public int maxProfit(int[] prices) {
        int mp = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int p:prices) {
            if (p < mp) {
                mp = p;
            }
            max = Math.max(max, p - mp);

        }

        return max;
    }
}

实现一个HashMap

public class MyHashMap {
    int capacity = 16;

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

    Node[] bucket;

    public MyHashMap() {
        this.bucket = new Node[capacity];
    }

    public void put(int key, int value) {
        int i = key % capacity;
        if (bucket[i] == null) {
            bucket[i] = new Node(key, value);
            return;
        }

        Node temp = bucket[i];
        if (temp.key == key) {
            temp.value = value;
            return;
        }

        while (temp.next != null) {
            if (temp.next.key == key) {
                temp.next.value = value; // fix here
                return;
            }
            temp = temp.next;
        }

        temp.next = new Node(key, value); // insert at end
    }

    public int get(int key) {
        int i = key % capacity;
        Node temp = bucket[i];
        while (temp != null) {
            if (temp.key == key) {
                return temp.value;
            }
            temp = temp.next;
        }
        return -1; // not found
    }

    public void remove(int key) {
        int i = key % capacity;
        Node temp = bucket[i];

        if (temp == null) return;

        if (temp.key == key) {
            bucket[i] = temp.next;
            return;
        }

        while (temp.next != null) {
            if (temp.next.key == key) {
                temp.next = temp.next.next;
                return;
            }
            temp = temp.next;
        }
    }
}


环形链表

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

思路:

  • 快慢指针 判断是否有环。
  • 有环,快指针=第一个节点,然后两个指针向下遍历,相遇的点就是开始入环的第一个节点。
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null) return null;

        ListNode fast = head;
        ListNode slow = head;

        // 快慢指针:判断是否有环
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                break;
            }
        }

        // 没有环
        if (fast == null || fast.next == null) {
            return null;
        }

        // 有环:重新让 fast 从头走,和 slow 一起一步一步走,相遇点就是环的入口
        fast = head;
        while (fast != slow) {
            fast = fast.next;
            slow = slow.next;
        }

        return slow;
    }
}


寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

将数组视为链表,nums[i] 表示“指针”,指向 nums[nums[i]]。

💡 思路:
把 nums 看成链表,数组下标是节点编号,值是指针指向的下一个节点;

因为有重复值,所以一定有“环”;

问题转化为:找这个链表的“环入口”,这就是“重复的数”。

public class Solution {
    public int findDuplicate(int[] nums) {
        // 初始化快慢指针
        int slow = nums[0];
        int fast = nums[0];

        // 第一步:快慢指针找相遇点(在环中)
        do {
            slow = nums[slow];
            fast = nums[nums[fast]];
        } while (slow != fast);

        // 第二步:找环的入口(即重复的数字)
        fast = nums[0];
        while (fast != slow) {
            fast = nums[fast];
            slow = nums[slow];
        }

        return slow; // or fast
    }
}


缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

思想:把每个数字放到它“该去的位置”,然后再检查哪个位置缺少那个数字。

class Solution {
    public int firstMissingPositive(int[] nums) {
        // 把每个数字放到它“该去的位置”,然后再检查哪个位置缺少那个数字。
        int i = 0;
        while (i < nums.length) {
            if (nums[i] != i+1 && nums[i] > 0 && nums[i] <= nums.length && nums[nums[i] - 1] != nums[i]) {
                int temp = nums[nums[i]-1];
                nums[nums[i]-1] = nums[i];
                nums[i] = temp; 
            }else {
                i++;
            }

            
        }

        for (int k = 0; k < nums.length; k++) {
            if (nums[k] != k + 1) {
                return k+1;
            }
        }

        return nums.length + 1;
    }
}

螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        List<Integer> ans = new ArrayList<>();
        int m = matrix.length;
        int n = matrix[0].length;
        // 边界
        int l = 0, r = n - 1, t = 0, b = m - 1;
        while (l <= r && t <= m) {
            // 左 到 右
            for (int i = l; i <= r; i++) {
                ans.add(matrix[t][i]);
            }
            // 边界判断
            if (++t > b) break;
            // 上 到 下
            for (int i = t; i <= b; i++) {
                ans.add(matrix[i][r]);
            }
            if (--r < l) break;
            // 右 到 左
            for (int i = r; i >= l; i--) {
                ans.add(matrix[b][i]);
            }
            if (--b < t) break;
            // 从 下 到 上
            for (int i = b; i >= t;i--) {
                ans.add(matrix[i][l]);
            }
            if (++l > r) break;
        }

        return ans;
    }

}

字符串相乘

给定两个以字符串形式表示的非负整数 num1num2,返回 num1num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

class Solution {
    public String multiply(String num1, String num2) {
        if ("0".equals(num1) || "0".equals(num2)) return "0";

        int m = num1.length();
        int n = num2.length();
        int[] res = new int[m + n]; // 最多 m+n 位

        for (int i = m - 1; i >= 0; i--) {
            int a = num1.charAt(i) - '0';
            for (int j = n-1; j >= 0; j--) {
                int b = num2.charAt(j) - '0';
                int sum = res[i+j+1] + a*b;
                res[i+j+1] = sum % 10;
                res[i+j] += sum/10; // +=
            }
        }

        // 转换成字符串,去掉前导0
        StringBuilder sb = new StringBuilder();
        for (int num : res) {
            if (sb.length() == 0 && num == 0) continue;
            sb.append(num);
        }

        return sb.toString();

        

    }
}

分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] candys = new int[n];
        // 每个孩子至少分配到 1 个糖果
        Arrays.fill(candys, 1);

        // 从 左 到 右 遍历:相邻两个孩子评分更高的孩子会获得更多的糖果
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i-1]) {
                candys[i] = candys[i-1]+1;
            }
        }

        // 从 右 到 左 遍历:相邻两个孩子评分更高的孩子会获得更多的糖果
        for (int i = n-2; i >= 0; i--) {
            if (ratings[i] > ratings[i+1]) {
                candys[i] = Math.max(candys[i], candys[i+1]+1);
            }
        }

        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += candys[i];
        }

        return sum;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值