LeetCode Top100之20,21,22,23题

    xiaoxiao2022-07-06  204

    记录于2019年5月22日

    文章目录

    [20. 有效的括号](https://leetcode.com/problems/valid-parentheses/)① 题目描述② 使用堆栈(Stack) [21. 合并两个有序链表](https://leetcode.com/problems/merge-two-sorted-lists/)① 题目描述② 借助新的链表头 [22. 括号生成](https://leetcode.com/problems/generate-parentheses/)① 题目描述② 回溯法③ 暴力法 [23. 合并K个排序链表](https://leetcode.com/problems/merge-k-sorted-lists/)① 题目描述② 自己的笨办法③ 一列一列的比较(效果好像比自己的笨办法差很多)④ 使用优先队列(掌握优先队列的定义)

    20. 有效的括号

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

    输入: “()” 输出: true

    示例 2:

    输入: “()[]{}” 输出: true

    示例 3:

    输入: “(]” 输出: false

    示例 4:

    输入: “([)]” 输出: false

    示例 5:

    输入: “{[]}” 输出: true

    ② 使用堆栈(Stack)
    括号配对问题,典型的方法——使用stack。当stack为empty时,将括号压入stack;当栈顶和当前待扫描括号配对时,弹出栈顶的括号;如果都不满足直接压入stack,比如({[。时间复杂度:O(n),因为我们一次只遍历给定的字符串中的一个字符并在栈上进行 O(1) 的推入和弹出操作。空间复杂度:O(n),当我们将所有的开括号都推到栈上时以及在最糟糕的情况下,我们最终要把所有括号推到栈上。例如 ((((((((((。代码如下: public boolean isValid(String s) { int len = s.length(); if (len == 0) { return true; } if (len % 2 != 0) { return false; } Stack<Character> stack = new Stack<>(); for (int i = 0; i < len; i++) { if (stack.isEmpty()) { stack.push(s.charAt(i)); } else { if (stack.peek() == '(' && s.charAt(i) == ')') { stack.pop(); } else if (stack.peek() == '[' && s.charAt(i) == ']') { stack.pop(); } else if (stack.peek() == '{' && s.charAt(i) == '}') { stack.pop(); } else { stack.push(s.charAt(i)); } } } if (stack.isEmpty()) { return true; } return false; }

    21. 合并两个有序链表

    ① 题目描述
    将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。示例:

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

    ② 借助新的链表头
    通过比较l1和l2的val,决定是将l1加入新的链表,还是将l2加入新的链表,并且更新l1或l2的指针。注意特殊情况: ① l1或l2为null,直接返回另一条链表; ② 完成合并后,总有一条链表不在末尾,直接将其加入新的链表即可。 public ListNode mergeTwoLists(ListNode l1, ListNode l2) { //可以提前判断,也可以不用判断 if (l1 == null) { return l2; } if (l2 == null) { return l1; } ListNode result = new ListNode(0); ListNode cur = result; while (l1 != null && l2 != null) { if (l1.val < l2.val) { cur.next = l1; l1 = l1.next; } else { cur.next = l2; l2 = l2.next; } cur = cur.next; } if (l1 != null) { cur.next = l1; } else { cur.next = l2; } return result.next; }

    22. 括号生成

    ① 题目描述
    给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。例如,给出 n = 3,生成结果为:

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

    ② 回溯法
    public List<String> generateParenthesis(int n) { List<String> result = new ArrayList<>(); if (n == 0) { return result; } helper(result, "", 0, 0, n); return result; } public void helper(List<String> result, String s, int open, int close, int n) { if (s.length() == n * 2) { result.add(s); return; } if (open < n) { helper(result, s + "(", open + 1, close, n); } if (open > close) { helper(result, s + ")", open, close + 1, n); } }
    ③ 暴力法
    public List<String> generateParenthesis(int n) { List<String> result = new ArrayList<>(); if (n == 0) { return result; } helper(result, "", n); return result; } public void helper(List<String> result, String s, int n) { if (s.length() == n * 2) { if (isTrue(s)) { result.add(s); } return; } helper(result, s + "(", n); helper(result, s + ")", n); } public boolean isTrue(String s) { int count = 0; for (int i = 0; i < s.length(); i++) { if (s.charAt(i) == '(') { count++; } else { count--; } if (count < 0) { return false; } } return count == 0; }

    23. 合并K个排序链表

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

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

    ② 自己的笨办法
    先获取所有排序链表的节点值,重新排序后将其存入新的链表中。巧用List和Collections.sort(),实现快速从新排序。代码如下: public ListNode mergeKLists(ListNode[] lists) { ListNode result = new ListNode(0); ListNode cur = reuslt; if (lists.length == 1) { return lists[0]; } List<Integer> arr = new ArrayList<>(); for (int i = 0; i < lists.length; i++) { ListNode p = lists[i]; while (p != null) { arr.add(p.val); p = p.next; } } Collections.sort(arr); for (int i = 0; i < arr.size(); i++) { ListNode node = new ListNode(arr.get(i)); cur.next = node; cur = cur.next; } cur.next = null; return result.next; }
    ③ 一列一列的比较(效果好像比自己的笨办法差很多)
    将所有的链表对齐,一列一列的查找当前列的最小值,直到所有链表均为null。示意图如下: 代码如下: public ListNode mergeKLists(ListNode[] lists) { ListNode result = new ListNode(0); ListNode cur = reuslt; if (lists.length == 1) { return lists[0]; } while (true) { int min = Integer.MAX_VALUE; int min_index = 0; boolean flag = true; for (int i = 0; i < lists.length; i++) { if (lists[i] != null) { if (lists[i].val < min) { min = lists[i].val; min_index = i; } flag = false; } } if (flag) { break; } cur.next = lists[min_index]; cur = cur.next; lists[min_index] = lists[min_index].next; } cur.next = null; return result.next; }
    ④ 使用优先队列(掌握优先队列的定义)
    通过自定义比较器,实现针对ListNode的优先队列。整体思想与一列一列的比较一样的,只是将比较过程交给了优先队列自己完成。 Queue<ListNode> q = new PriorityQueue<ListNode>(cmp); 注意链表的特殊性,看起来队列中存储的是一个节点,其实他存储的是整条链表。初始化优先队列时,一定不要忘记判断lists[i]是否为null! Comparator<ListNode> cmp = new Comparator<ListNode>() { // 自定义比较器 @Override public int compare(ListNode o1, ListNode o2) { return o1.val - o2.val; } }; public ListNode mergeKLists(ListNode[] lists) { ListNode result = new ListNode(0); ListNode cur = result; if (lists.length == 1) { return lists[0]; } //建立队列,每个位置存储一个链表 Queue<ListNode> q = new PriorityQueue<ListNode>(cmp); for (int i = 0; i < lists.length; i++) { if (lists[i] != null) { q.add(lists[i]); } } while (!q.isEmpty()) { cur.next = q.poll(); // cur.next指向满足条件的待合并节点 cur = cur.next; // 更新cur指针,指向最新节点,这时整个链表与弹出的链表是一个整体 if (cur.next != null) { // 如果cur.next不为空,将剩余的链表重新加入优先队列 q.add(cur.next); } } cur.next = null; return result.next; }
    最新回复(0)