LeetCode Top100之20,21,22,23题

20. 有效的括号

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

输入: “()”
输出: true

  • 示例 2:

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

  • 示例 3:

输入: “(]”
输出: false

  • 示例 4:

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

  • 示例 5:

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

② 使用堆栈(Stack)
  • 括号配对问题,典型的方法——使用stack。当stackempty时,将括号压入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

② 借助新的链表头
  • 通过比较l1l2的val,决定是将l1加入新的链表,还是将l2加入新的链表,并且更新l1l2的指针。
  • 注意特殊情况:
    l1l2为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

② 自己的笨办法
  • 先获取所有排序链表的节点值,重新排序后将其存入新的链表中。
  • 巧用ListCollections.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;
}

猜你喜欢

转载自blog.csdn.net/u014454538/article/details/90449974