2025高频面试算法总结篇【其他】
文章目录
- 直接刷题链接直达
- LRU Cache
- 买卖股票的最佳时机系列
- 实现一个HashMap
- 环形链表
- 寻找重复数
- 缺失的第一个正数
- 螺旋矩阵
- 字符串相乘
- 分发糖果
直接刷题链接直达
- LRU Cache
- 头尾两个伪节点(避免判断) + 双向链表
- 146. LRU 缓存
- 买卖股票的最佳时机系列
- 121. 买卖股票的最佳时机
- 实现一个HashMap
- 要求:1. 定义内部存储结构; 2.实现 insert(key, value) 和 remove(key);3.不能使用任何Java集合框架。
- 706. 设计哈希映射
- 求给定区间内子区间的最大值(区间内最小值*区间元素相加)
- 要求时间复杂度O(n)
- 原题
- 斐波那契数列的尾递归实现
- 面试官原意是斐波那契的递归实现如何优化
- 尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数
- 递归与尾递归总结
- 1到10000有多少个数字7
- 答案 :4000
- 腾讯面试题-0到9999这1万个数中有多少个数字7
- 给定精度(如小数点后10位),写一个函数求根号2的具体值
- 牛顿法,详见 如何通俗易懂地讲解牛顿迭代法求开方?数值分析?
- 69. x 的平方根
- 给定一个乱序数组[0,100],替换其中一个数,找出这个被替换的数
- 类似 142. 环形链表 II 思路,重复数字会形成环,双指针寻找
- 287. 寻找重复数
- 缺失的第一个正数
- 41. 缺失的第一个正数
- 顺时针打印矩阵(星环)
- 54. 螺旋矩阵
- 实现一个int转中文表示的函数,同时设计测试用例
- 定界数据范围 + 梳理演变规则(以“万”为节,化繁为简,以及“零”如何处理)
- 数字(int型范围内正整数)和中文的相互转换
- 100盏灯问题
- 面试题—100盏灯问题
- 给定一个数组,一个闭区间[n,m],给出该数组中所有最大值在闭区间中的连续子数组数目
- 要求时间复杂度O(n)
- 如 [2,1,4,3],[2,3],有[2],[2,1],[3],返回3
- 字符串相乘
- 43. 字符串相乘
- 用Java实现一个String转Map(Map<String,Object>)的函数,不能使用String的split和第三方库
- 整数中1出现的次数(从1到n中1出现的次数)
- 233. 数字 1 的个数
- 单词接龙
- 广度优先 --> 队列 + 暴力搜索(遍历过的单词删除),视频讲解见 花花酱 LeetCode 127. Word Ladder - 刷题找工作 EP71
- 127. 单词接龙
- 分数到小数
- 将除法过程代码化
- 166. 分数到小数
- 只出现一次的数字
- 136. 只出现一次的数字
- 分糖果
- 从左至右扫一遍,再从右至左扫一遍,取两遍中的最大值
- 135. 分发糖果
- 实现一个命令行逆波兰计算器
- CommandLineRPN
- Largest M-aligned Subset
- Microsoft | OA 2020 | Largest M-aligned Subset
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; // 更新 valuereturn;}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 herereturn;}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;}}
字符串相乘
给定两个以字符串形式表示的非负整数 num1
和 num2
,返回 num1
和 num2
的乘积,它们的乘积也表示为字符串形式。
注意:不能使用任何内置的 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; // +=}}// 转换成字符串,去掉前导0StringBuilder 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;}
}