Leetcode Top100题目和答案(Java完整版 面试必备)

二刷完剑指Offer后又刷了一遍Leetcode Top 100专栏的题目,听说基本上能涵盖面试的算法题,总体来说收获还是很大的,下面贴出答案,又不懂的可以给我留言,博主会及时解答。
我的github
准备把春招复习的知识都整理到github上,一边是自己做个总结,一边也能供大家参考
——leetcode数据库 19道题
——剑指Offer 66道题

以下摘自leetcode Top100精选题目

文章目录

1.两数之和

题目描述:

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

Solution:

class Solution {
   
    public int[] twoSum(int[] nums, int target) {
   
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
   
            if (map.containsKey(nums[i])) {
   
                return new int[]{
   map.get(nums[i]), i};
            }
            map.put(target - nums[i], i);
        }
        return null;
    }
}

2.两数相加

题目描述:

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

节点结构:

public class ListNode {
   
    int val;
    ListNode next;
    ListNode(int x) {
    val = x; }
}

Solution:

class Solution {
   
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
   
        ListNode dummy = new ListNode(0);
        
        int sum = 0; // 结果
        int more = 0; // 进位
        ListNode pre = dummy;
        while (l1 != null || l2 != null || more > 0) {
   
            sum = (l1 == null ? 0 : l1.val) + (l2 == null ? 0 : l2.val) + more;
            more = sum / 10;
            sum %= 10;
            ListNode node = new ListNode(sum);
            pre.next = node;
            pre = node;
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;
        }
        return dummy.next;
    }
}

3.无重复字符的最长子串

题目描述:

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

Solution:

class Solution {
   
    public int lengthOfLongestSubstring(String s) {
   
        if (s == null || s.length() < 1) {
   
            return 0;
        }
        
        int[] map = new int[256];
        int l = 0;
        int r = 0; // 滑动窗口为[l, r),其间为不重复的元素
        int res = 0;
        while (l < s.length()) {
   
            if (r < s.length() && map[s.charAt(r)] == 0) {
   
                map[s.charAt(r++)]++;
                res = Math.max(res, r - l);
            } else {
   
                map[s.charAt(l++)]--;
            }
        }
        return res;
    }
}

4.寻找两个有序数组的中位数

题目描述:

给定两个大小为 m 和 n 的有序数组 nums1nums2

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1nums2 不会同时为空。

示例 1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例 2:

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

Solution:

public class Solution {
   
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
   
        // 保证nums1不是最长的,时间复杂度可转化为O(log(Min(m, n)))
        if (nums1.length > nums2.length) {
   
            return findMedianSortedArrays(nums2, nums1);
        }

        int left = 0;
        int right = nums1.length;
        int halfLen = (nums1.length + nums2.length + 1) >> 1;

        while (left <= right) {
   
            int i = (left + right) >> 1; // nums1[i, nums1.length)为要分割的右半部分
            int j = halfLen - i; // nums2[j, nums2.length)为要分割的右半部分
            if (i < right && nums2[j - 1] > nums1[i]) {
    // nums1分割点此时需要右移
                left++;
            } else if (i > left && nums1[i - 1] > nums2[j]) {
    // nums1 分割点此时需要左移
                right--;
            } else {
   
                int leftMax = (i == 0) ? nums2[j - 1] :
                        (j == 0 ? nums1[i - 1] : Math.max(nums1[i - 1], nums2[j - 1]));
                if (((nums1.length + nums2.length) & 1) == 1) {
   
                    return leftMax * 1.0;
                }
                int rightMin = (i == nums1.length) ? nums2[j] :
                        (j == nums2.length ? nums1[i] : Math.min(nums1[i], nums2[j]));
                return (leftMax + rightMin) / 2.0;
            }
        }
        return 0.0;
    }
}

5.最长回文子串

题目描述:

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。

示例 1:

输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。

示例 2:

输入: "cbbd"
输出: "bb"

Solution:

/**
 * 中心扩展法
 */
public class Solution {
   
    private int left;
    private int len;

    public String longestPalindrome(String s) {
   
        if (s == null || s.length() < 2) {
   
            return s;
        }

        for (int i = 0; i < s.length(); i++) {
   
            find(s, i, i); // 奇数长度
            find(s, i, i + 1); // 偶数长度
        }
        return s.substring(left, left + len);
    }

    private void find(String s, int left, int right) {
   
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
   
            if (right - left + 1 > len) {
   
                len = right - left + 1;
                this.left = left;
            }
            right++;
            left--;
        }
    }
}

10.正则表达式匹配

题目描述:

给定一个字符串 (s) 和一个字符模式 (p)。实现支持 '.''*' 的正则表达式匹配。

'.' 匹配任意单个字符。
'*' 匹配零个或多个前面的元素。

匹配应该覆盖整个字符串 (s) ,而不是部分字符串。

说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 .*

示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

示例 2:

输入:
s = "aa"
p = "a*"
输出: true
解释: '*' 代表可匹配零个或多个前面的元素, 即可以匹配 'a' 。因此, 重复 'a' 一次, 字符串可变为 "aa"。

示例 3:

输入:
s = "ab"
p = ".*"
输出: true
解释: ".*" 表示可匹配零个或多个('*')任意字符('.')。

示例 4:

输入:
s = "aab"
p = "c*a*b"
输出: true
解释: 'c' 可以不被重复, 'a' 可以被重复一次。因此可以匹配字符串 "aab"。

示例 5:

输入:
s = "mississippi"
p = "mis*is*p*."
输出: false

Solution:

public class Solution {
   
    public boolean isMatch(String s, String p) {
   
        if (s == null || p == null) {
   
            return false;
        }

        return isMatch(s, p, 0, 0);
    }

    private boolean isMatch(String str, String pattern, int s, int p) {
   
        // 正则表达式已用尽,如果字符串还未匹配完,则返回false
        if (p == pattern.length()) {
   
            return str.length() == s;
        }
        // 正则表达式下一位为*,此时考虑两种情况
        if (p + 1 < pattern.length() && pattern.charAt(p + 1) == '*') {
   
            // 若正则表达式当前位字符与字符串当前位置相匹配,则匹配1位或者0位
            if (s < str.length() && (str.charAt(s) == pattern.charAt(p) || pattern.charAt(p) == '.')) {
   
                return isMatch(str, pattern, s, p + 2) || isMatch(str, pattern, s + 1, p);
            }
            // 若正则表达式当前位字符与字符串当前位置不匹配,则匹配0位
            return isMatch(str, pattern, s, p + 2);
        }

        // 匹配1位
        if (s < str.length() && (str.charAt(s) == pattern.charAt(p) || pattern.charAt(p) == '.')) {
   
            return isMatch(str, pattern, s + 1, p + 1);
        }
        return false;
    }
}

11.盛最多水的容器

题目描述:

给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

**说明:**你不能倾斜容器,且 n 的值至少为 2。

img

图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:

输入: [1,8,6,2,5,4,8,3,7]
输出: 49

Solution:

/**
* 利用滑动窗口解决
*/
public class Solution {
   
    public int maxArea(int[] height) {
   
        int res = 0;
        int left = 0;
        int right = height.length - 1;
        
        while (left < right) {
   
            res = Math.max(res, Math.min(height[left], height[right]) * (right - left));
            if (height[left] < height[right]) {
   
                left++;
            } else {
   
                right--;
            }
        }
        
        return res;
    }
}

15.三数之和

题目描述:

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 *a,b,c ,*使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

**注意:**答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

Solution:

/**
 * 采用滑动窗口,时间复杂度为:O(n log(n))
 */
public class Solution {
   
    public List<List<Integer>> threeSum(int[] nums) {
   
        List<List<Integer>> list = new ArrayList<>();
        if (nums == null || nums.length < 3) {
   
            return list;
        }

        // 先排序,同时避免求重复解
        Arrays.sort(nums);

        for (int i = 0; i < nums.length - 2 && nums[i] <= 0;) {
   
            int l = i + 1;
            int r = nums.length - 1;
            while (l < r) {
   
                int sum = nums[i] + nums[l] + nums[r];
                if (sum == 0) {
   
                    list.add(Arrays.asList(nums[i], nums[l++], nums[r--]));
                    while (l < r && nums[l] == nums[l - 1]) {
   
                        l++;
                    }
                    while (r > l && nums[r] == nums[r + 1]) {
   
                        r--;
                    }
                } else if (sum < 0) {
   
                    l++;
                } else {
   
                    r--;
                }
            }
            i++;
            while (i < nums.length - 2 && nums[i] == nums[i - 1]) {
   
                i++;
            }
        }
        return list;
    }
}

17.电话号码的字母组合

题目描述:

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

img

示例:

输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

Solution:

public class Solution {
   
    private List<String> res = new ArrayList<>();
    private String[] map = {
   "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

    public List<String> letterCombinations(String digits) {
   
        if (digits == null || digits.length() < 1) {
   
            return res;
        }

        dfs(digits, 0, "");
        return res;
    }

    private void dfs(String digits, int index, String str) {
   
        if (index == digits.length()) {
   
            res.add(str);
            return;
        }
        
        String dict = map[digits.charAt(index) - '0'];
        for (int i = 0; i < dict.length(); i++) {
   
            dfs(digits, index + 1, str + dict.charAt(i));
        }
    }
}

19.删除链表的倒数第N个节点

题目描述:

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

说明:

给定的 n 保证是有效的。

进阶:

你能尝试使用一趟扫描实现吗?

Solution:

/*
* 双指针
*/
public class Solution {
   
    public ListNode removeNthFromEnd(ListNode head, int n) {
   
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode fast = head;
        ListNode slow = dummy;
        for (int i = 0; i < n; i++) {
   
            fast = fast.next;
        }
        while (fast != null) {
   
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return dummy.next;
    }
}

20有效的括号

题目描述:

给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true

示例 2:

输入: "()[]{}"
输出: true

示例 3:

输入: "(]"
输出: false

示例 4:

输入: "([)]"
输出: false

示例 5:

输入: "{[]}"
输出: true

Solution:

public class Solution {
   
    public static boolean isValid(String s) {
   
        if (s == null || (s.length() & 1) == 1) {
   
            return false;
        }

        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
   
            if (s.charAt(i) == '[' || s.charAt(i) == '{' || s.charAt(i) == '(') {
   
                stack.push(s.charAt(i));
            } else if (s.charAt(i) == ']' && (stack.isEmpty() || stack.pop() != '[')) {
   
                return false;
            } else if (s.charAt(i) == '}' && (stack.isEmpty() || stack.pop() != '{')) {
   
                return false;
            } else if (s.charAt(i) == ')' && (stack.isEmpty() || stack.pop() != '(')) {
   
                return false;
            }
        }
        return stack.isEmpty();
    }
}

21.合并两个有序链表

题目描述:

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

Solution:

public class Solution {
   
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
   
        ListNode dummy = new ListNode(0);
        ListNode pre = dummy;
        
        while (l1 != null && l2 != null) {
   
            if (l1.val < l2.val) {
   
                pre.next = l1;
                l1 = l1.next;
            } else {
   
                pre.next = l2;
                l2 = l2.next;
            }
            pre = pre.next;
        }
        pre.next = l1 == null ? l2 : l1;
        
        return dummy.next;
    }
}

22.生成括号

题目描述:

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

Solution:

public class Solution {
   
    private List<String> list = new ArrayList<>();
    
    public List<String> generateParenthesis(int n) {
   
        if (n < 1) {
   
            return list;
        }
        
        generate(n, 0, 0, "");
        return list;
    }
    
    private void generate(int n, int left, int right, String str) {
   
        if (left == right && left == n) {
   
            list.add(str);
        }
        if (left < n) {
   
            generate(n, left + 1, right, str + "(");
        }
        if (left > right) {
   
            generate(n, left, right + 1, str + ")");
        }
    }
}

23.合并K个排序链表

题目描述:

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

Solution:

public class Solution {
   
    public ListNode mergeKLists(ListNode[] lists) {
   
        if (lists == null || lists.length == 0) {
   
            return null;
        }

        ListNode dummy = new ListNode(0);
        ListNode pre = dummy;

        // 使用小顶堆,每次取出的都是最小的节点
        Queue<ListNode> minHeap = new PriorityQueue<>(Comparator.comparingInt(node -> node.val));
        for (ListNode list : lists) {
   
            if (list != null) {
   
                minHeap.offer(list);
            }
        }

        while (!minHeap.isEmpty()) {
   
            pre.next = minHeap.poll();
            pre = pre.next;
            if (pre.next != null) {
   
                minHeap.offer(pre.next);
            }
        }

        return dummy.next;
    }
}

31.下一个排列

题目描述:

实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须原地修改,只允许使用额外常数空间。

以下是一些例子,输入位于左侧列,其相应输出位于右侧列。
1,2,31,3,2
3,2,11,2,3
1,1,51,5,1

Solution:

/**
 * 求下一个全排列,可分为两种情况:
 * 1.例如像 5 4 3 2 1这样的序列,已经是最大的排列,即每个位置上的数非递增,这时只需要翻转整个序列即可
 * 2.例如像 1 3 5 4 2这样的序列,要从后往前找到第一个比后面一位小的元素的位置,即第二个位置的3,然后与其后第一个比它大的元素交换位置,得到 1 4 5 3 2,再将 5 3 2翻转得到 1 4 2 3 5即可
 */

public class Solution {
   
    public void nextPermutation(int[] nums) {
   
        if (nums == null || nums.length == 0) {
   
            return;
        }

        int firstSmall = -1;
        for (int i = nums.length - 2; i >= 0; i--) {
   
            if (nums[i] < nums[i + 1]) {
   
                firstSmall = i;
                break;
            }
        }

        if (firstSmall == -1) {
   
            reverse(nums, 0, nums.length - 1);
            return;
        }

        for (int i = nums.length - 1; i > firstSmall; i--) {
   
            if (nums[i] > nums[firstSmall]) {
   
                swap(nums, i, firstSmall);
                reverse(nums, firstSmall + 1, nums.length - 1);
                return;
            }
        }
    }

    private void swap(int[] nums, int i, int j) {
   
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

    private void reverse(int[] nums, int start, int end) {
   
        while (start < end) {
   
            swap(nums, start++, end--);
        }
    }
}

32.最长有效括号

题目描述:

给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"

示例 2:

输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"

Solution:

public class Solution {
   
    public int longestValidParentheses(String s) {
   
        if (s == null || s.length() < 2) {
   
            return 0;
        }

        int res = 0;
        int start = 0;
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
   
            if (s.charAt(i) == '(') {
   
                stack.push(i);
            } else {
   
                if (stack.isEmpty()) {
   
                    start = i + 1;
                } else {
   
                    stack.pop();
                    res = stack.isEmpty() ? Math.max(res, i - start + 1) : Math.max(res, i - stack.peek());
                }
            }
        }
        return res;
    }
}

33.搜索旋转排序数组

题目描述:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1

你可以假设数组中不存在重复的元素。

你的算法时间复杂度必须是 O(log n) 级别。

示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4

示例 2:

输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

Solution:

public class Solution {
   
    public int search(int[] nums, int target) {
   
        if (nums == null 
            || nums.length < 1 
            || (target < nums[0] && target > nums[nums.length - 1])) {
   
            return -1;
        }
        
        int low = 0;
        int high = nums.length - 1;
        while (low <= high) {
   
            int mid = (low + high) >> 1;
            if (nums[mid] == target) {
   
                return mid;
            }
            
            if (nums[mid] >= nums[low]) {
   // 左边有序
                if (nums[mid] > target && nums[low] <= target) {
   // 在有序边
                    high = mid - 1;
                } else{
   // 在无序边
                    low = mid + 1;
                }
            } else {
   // 右边有序
                if (nums[mid] < target && nums[high] >= target) {
   // 在有序边
                    low = mid + 1;
                } else {
   // 在无序边
                    high = mid - 1;
                }
            }
        }
        
        return -1;
    }
}

34.在排序数组中查找元素的第一个和最后一个位置

题目描述:

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

Solution:

public class Solution {
   
    public int[] searchRange(int[] nums, int target) {
   
        if (nums == null || nums.length < 1) {
   
            return new int[]{
   -1, -1};
        }
        
        int low = 0;
        int high = nums.length - 1;
        while (low <= high) {
   
            int mid 
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值