【LeetCode】Java版11-20

代码中用到的数据结构

  • 链表

public class ListNode {
    int val;
    ListNode next;

    ListNode(int val) {
        this.val = val;
    }
}
  • 二叉树

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }
}

11. 盛最多水的容器

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

思路:两线段之间形成的区域总是会受到其中较短那条长度的限制,两线段距离越远,得到的面积就越大。在由线段长度构成的数组中使用两个指针,一个放在开始,一个置于末尾。 此外,我们会使用变量 maxarea 来持续存储到目前为止所获得的最大面积。

代码实现


public int maxArea(int[] height) {
    int maxArea = 0;
    int l = 0;
    int r = height.length - 1;
    while (l < r) {
        maxArea = Math.max(maxArea, Math.min(height[l], height[r]) * (r - l));
        if (height[l] < height[r]) {
            l++;
        } else {
            r--;
        }
    }
    return maxArea;
}

12. 整数转罗马数字

题目描述:给定一个整数,将其转为罗马数字。输入确保在1到3999的范围内。

思路:把所有的组合列出来,因为罗马数字表示的大小就是把所有字母相加,所以每次 append罗马数字,再把对应的值减去。

代码实现


public String intToRoman(int num) {
    int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
    String[] reps = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

    StringBuilder result = new StringBuilder();
    for (int i = 0; i < values.length; i++) {
        while (num >= values[i]) {
            num -= values[i];
            result.append(reps[i]);
        }
    }
    return result.toString();
}

13. 罗马数字转整数

题目描述:给定一个罗马数字,将其转换成整数。输入确保在1到3999的范围内。

思路:使用map + 双指针。一个指针记录当前字符代表的值,另一个指针记录上一个字符代表的值,在累加过程中如果发现当前字符的值比上一个小了,那就说明发生了CM, CD, XL, XC之类的情况。于是减去上个字符的值即可。减两次原因:本来在上一个字符就该执行减法操作,但是却执行了加法操作,所以就和实际情况相差了2倍。

代码实现


public int romanToInt(String s) {
    Map<Character, Integer> map = new HashMap<>();
    map.put('I', 1);map.put('V', 5);map.put('X', 10);map.put('L', 50);
    map.put('C', 100);map.put('D', 500);map.put('M', 1000);
    int result = 0;
    char[] chars = s.toCharArray();
    int pre = 1000;
    for (char c : chars) {
        Integer current = map.get(c);
        result += current;
        if (current > pre) {
            result = result - (2 * pre);
        }
        pre = current;
    }
    return result;
}

14. 最长公共前缀

题目描述:编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串""。

思路:水平扫描:从前往后枚举字符串的每一列,先比较每个字符串相同列上的字符(即不同字符串相同下标的字符)然后再进行对下一列的比较。或使用二分查找算法。

代码实现

  • 解法一:水平扫描法,时间复杂度:O(S),空间复杂度:O(1),其中S指所有字符串中字符数量的总和。

public String longestCommonPrefix(String[] strs) {
    if (strs == null || strs.length == 0) {
        return "";
    }
    for (int i = 0; i < strs[0].length(); i++) {
        char curr = strs[0].charAt(i);
        for (int j = 1; j < strs.length; j++) {
            if (i == strs[j].length() || strs[j].charAt(i) != curr) {
                return strs[0].substring(0, i);
            }
        }
    }
    return strs[0];
}
  • 解法二:二分查找,时间复杂度:O(S⋅log(n)),空间复杂度:O(1)

    public String longestCommonPrefix2(String[] strs) {
        if (strs == null || strs.length == 0) {
            return "";
        }
        int minLen = Integer.MAX_VALUE;
        for (String str : strs) {
            minLen = Math.min(minLen, str.length());
        }

        int low = 1;
        int high = minLen;
        while (low <= high) {
            int middle = (low + high) / 2;
            if (isCommonPrefix(strs, middle)) {
                low = middle + 1;
            } else {
                high = middle - 1;
            }
        }
        return strs[0].substring(0, (low + high) / 2);
    }

    private boolean isCommonPrefix(String[] strs, int len) {
        String str1 = strs[0].substring(0, len);
        for (int i = 1; i < strs.length; i++) {
            if (!strs[i].startsWith(str1)) {
                return false;
            }
        }
        return true;
    }

15. 三数之和

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

思路:双指针法,时间复杂度:O(n²),空间复杂度:O(n)

代码实现


public List<List<Integer>> threeSum2(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    if (nums == null || nums.length < 3) {
        return result;
    }
    Arrays.sort(nums);

    for (int i = 0, length = nums.length; i < length - 2; i++) {
        if (i == 0 || nums[i] != nums[i - 1]) {
            int j = i + 1, k = length - 1;
            while (k > j) {
                if (j != i + 1 && nums[j] == nums[j - 1]) {
                    j++;
                    continue;
                }
                int sum = nums[i] + nums[j] + nums[k];
                if (sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    k--;
                    j++;
                } else if (sum > 0) {
                    k--;
                } else {
                    j++;
                }
            }
        }
    }
    return result;
}

16. 最接近的三数之和

题目描述:给定一个包括 n个整数的数组 nums和一个目标值 target。找出 nums中的三个整数,使得它们的和与 target最接近。返回这三个数的和。假定每组输入只存在唯一答案。

思路:双指针法,时间复杂度:O(n²),空间复杂度:O(1)

代码实现


public int threeSumClosest2(int[] nums, int target) {
    if (nums == null || nums.length < 3) {
        return 0;
    }
    Arrays.sort(nums);
    int largerNum = nums[0] + nums[1] + nums[2];
    for (int i = 0; i < nums.length - 2; i++) {
        int left = i + 1;
        int right = nums.length - 1;
        while (left < right){
            int threeSum = nums[left] + nums[right] + nums[i];
            largerNum = Math.abs(threeSum - target) < Math.abs(largerNum - target) ? threeSum : largerNum;
            if (threeSum > target) {
                right--;
            } else if (threeSum < target) {
                left++;
            } else {
                return target;
            }
        }
    }

    return largerNum;
}

17. 电话号码的字母组合

题目描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射与电话按键相同。注意 1不对应任何字母。

思路:先获取字符串所能表示的字母组合,然后递归查找所有的字母组合。

代码实现


public List<String> letterCombinations(String digits) {
    List<String> combineList = new ArrayList<>();
    if (digits == null || digits.length() == 0) {
        return combineList;
    }
    combinationRecursive(combineList, "", digits);
    return combineList;
}

private void combinationRecursive(List<String> combineList, String cur, String digits) {
    if (cur.length() == digits.length()) {
        combineList.add(cur);
        return;
    }

    int index = digits.charAt(cur.length()) - '0';
    String str = this.getStringByNumber(index);
    for (int i = 0; i < str.length(); i++) {
        combinationRecursive(combineList, cur + str.charAt(i), digits);
    }
}

private String getStringByNumber(int number) {
    switch (number) {
        case 2: return "abc";
        case 3: return "def";
        case 4: return "ghi";
        case 5: return "jkl";
        case 6: return "mno";
        case 7: return "pqrs";
        case 8: return "tuv";
        case 9: return "wxyz";
        default: return "";
    }
}

18. 四数之和

题目描述: * 给定一个包含 n个整数的数组 nums和一个目标值target,判断 nums中是否存在四个元素 a,b,c和 d, 使得 a+b+c+d的值与 target相等?找出所有满足条件且不重复的四元组。

思路:两个for循环加双指针,即三数和题的外层多加了一个for循环。时间复杂度:O(n³),空间复杂度:O(n)

代码实现


public List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);

    for (int i = 0; i < nums.length - 2; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            int left = j + 1;
            int right = nums.length - 1;

            while (left < right) {
                // 不满足条件或者重复的,继续遍历
                if ((left != j + 1 && nums[left] == nums[left - 1]) || nums[i] + nums[j] + nums[left] + nums[right] < target) {
                    left++;
                } else if ((right != nums.length - 1 && nums[right] == nums[right + 1]) || nums[i] + nums[j] + nums[left] + nums[right] > target) {
                    right--;
                } else {
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[j]);
                    list.add(nums[left]);
                    list.add(nums[right]);
                    if (!result.contains(list)) {
                        result.add(list);
                    }
                    // 满足条件则进入下一次遍历
                    left++;
                    right--;
                }
            }
        }
    }

    return result;
}

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

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

思路:使用双指针,一次遍历。时间复杂度:O(n),空间复杂度:O(1),其中 n=LostNode.length

代码实现


public ListNode removeNthFromEnd(ListNode head, int n) {
    if (head == null || n < 1) {
        return null;
    }

    ListNode result = new ListNode(0);
    result.next = head;
    ListNode fast = result;
    ListNode slow = result;
    while (n-- > 0) {
        if (fast.next == null) {
            return null;
        }
        fast = fast.next;
    }

    while (fast.next != null) {
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    return result.next;
}

20. 有效的括号

题目描述:给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。注:空字符串可被认为是有效字符串。

思路: 利用栈判断。时间复杂度:O(n),空间复杂度:O(n)

代码实现


public boolean isValid(String s) {
    if (s == null || s.length() == 0) {
        return true;
    }
    Stack<Character> stack = new Stack<>();
    char[] chars = s.toCharArray();
    for (char c : chars) {
        if (stack.size() > 0 && isSpecialParenthesis(stack.peek(), c)) {
            stack.pop();
        } else {
            stack.push(c);
        }
    }
    return stack.size() == 0;
}

private boolean isSpecialParenthesis(char c1, char c2) {
    return c1 == '(' && c2 == ')' || c1 == '{' && c2 == '}' || c1 == '[' && c2 == ']';
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页