Java面试高频算法题总结:从入门到精通
大家好呀!今天我们来聊聊Java面试中那些让人头疼却又不得不面对的算法题。这些题目就像考试里的压轴大题一样,总是出现在各种大厂面试中。别担心,我今天就带大家梳理一下常见的高频算法题,而且我会用一种轻松愉快的方式让大家快速掌握它们!
首先,我们来认识一下今天的主要角色——数组、链表、字符串和树。这四个家伙就像是算法题中的四大天王,几乎每场面试都会轮番登场。
数组篇:稳扎稳打的基础
1. 两数之和
这个题目可以说是数组界的“明星”,几乎所有程序员的第一道算法题都是它。给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的两个整数,并返回它们的数组下标。
public int[] twoSum(int[] nums, int target) {
Map map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
这段代码利用了哈希表来存储已经遍历过的元素及其索引,这样可以做到一次遍历就能找到答案,时间复杂度为O(n)。
2. 最接近的三数之和
继续深挖数组的潜力,接下来是三数之和的变种——最接近的三数之和。给定一个包括 n 个整数的数组 nums 和一个目标值 target,请找出三数之和最接近目标值的整数。
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int closestSum = nums[0] + nums[1] + nums[nums.length - 1];
for (int i = 0; i < nums.length - 2; i++) {
int left = i + 1, right = nums.length - 1;
while (left < right) {
int currentSum = nums[i] + nums[left] + nums[right];
if (Math.abs(currentSum - target) < Math.abs(closestSum - target)) {
closestSum = currentSum;
}
if (currentSum < target left else if currentsum> target) {
right--;
} else {
return closestSum;
}
}
}
return closestSum;
}
这里采用了排序+双指针的方法,时间复杂度为O(n^2)。
链表篇:灵活多变的高手
1. 反转链表
链表类的题目总能让人感到一丝紧张,但其实它们并不那么可怕。比如反转链表,就是一个非常经典的例子。给定单向链表的头节点 head,将其反转。
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
这段代码使用迭代的方法,逐个将当前节点的next指向前一个节点,直到整个链表反转完成。
2. 合并两个有序链表
再来看一个稍微复杂一点的题目,合并两个有序链表。假设这两个链表已经按升序排列,我们需要将它们合并成一个新的有序链表。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode curr = dummy;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
curr.next = l1;
l1 = l1.next;
} else {
curr.next = l2;
l2 = l2.next;
}
curr = curr.next;
}
if (l1 != null) {
curr.next = l1;
} else {
curr.next = l2;
}
return dummy.next;
}
这段代码创建了一个虚拟头节点,然后依次比较两个链表的节点值,选择较小的那个接入新链表中。
字符串篇:文字游戏的乐趣
1. 最长回文子串
现在让我们进入字符串的世界,看看最长回文子串是如何工作的。给定一个字符串 s,你需要找到其中最长的回文子串。
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length i int len1='expandAroundCenter(s,' i i int len2='expandAroundCenter(s,' i i 1 int len='Math.max(len1,' len2 if len> end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
return right - left - 1;
}
这段代码通过中心扩展法,分别考虑奇数长度和偶数长度的回文串,最终找到最长的那个。
2. 最小覆盖子串
最后一个字符串相关的题目是寻找最小覆盖子串。给定一个字符串 S 和 T,找到 S 中包含 T 的所有字符且长度最短的子串。
public String minWindow(String s, String t) {
if (t.length() > s.length()) return "";
Map dictT = new HashMap<>();
for (char c : t.toCharArray()) {
dictT.put(c, dictT.getOrDefault(c, 0) + 1);
}
int required = dictT.size();
int left = 0, right = 0;
int formed = 0;
Map windowCounts = new HashMap<>();
int[] ans = {-1, 0, 0};
while (right < s.length()) {
char c = s.charAt(right);
windowCounts.put(c, windowCounts.getOrDefault(c, 0) + 1);
if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
formed++;
}
while (left <= right && formed == required) {
c = s.charAt(left);
if (ans[0] == -1 || right - left + 1 < ans[0]) {
ans[0] = right - left + 1;
ans[1] = left;
ans[2] = right;
}
windowCounts.put(c, windowCounts.get(c) - 1);
if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
formed--;
}
left++;
}
right++;
}
return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
}
这段代码利用滑动窗口技术,动态调整窗口大小,找到满足条件的最小覆盖子串。
树篇:结构之美
1. 二叉树的最大深度
最后我们来看看树相关的题目,首先是二叉树的最大深度。给定一个二叉树,找到它的最大深度。
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
递归方法简单明了,直接计算左右子树的最大深度并加一即可。
2. 二叉搜索树验证
另一个经典题目是判断一棵二叉树是否为二叉搜索树。这里需要确保左子树的所有节点都小于根节点,右子树的所有节点都大于根节点。
public boolean isValidBST(TreeNode root) {
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean isValidBST(TreeNode node, long lower, long upper) {
if (node == null) return true;
if (node.val <= lower node.val>= upper) return false;
return isValidBST(node.left, lower, node.val) && isValidBST(node.right, node.val, upper);
}
这段代码通过递归检查每个节点是否符合二叉搜索树的性质。
好了,今天的分享就到这里啦!希望这些算法题目的总结能帮到你们,在未来的面试中游刃有余。记住,练习是提高的关键,多做题多思考,相信你一定能在面试中脱颖而出!如果有任何疑问或者想要了解更多内容,随时欢迎来找我交流哦~
本文暂时没有评论,来添加一个吧(●'◡'●)