day6 3/8

20 有效的括号

思路1:栈
遇到左括号则入栈,遇到右括号则 出 栈顶元素,看是否与栈顶括号匹配,若不匹配返回false。最后检查栈是否为空,空则返回true,否则返回false。

总而言之:判断括号的有效性可以使用「栈」这一数据结构来解决。
遍历给定的字符串 s。当我们遇到一个左括号时,将这个左括号入栈。
当遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 s 无效,返回 False。
!!为了快速判断括号的类型,我们可以使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。

在遍历结束后,如果栈中没有左括号,说明我们将字符串 s 中的所有左括号闭合,返回 True,否则返回 False。

!!!注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,我们可以直接返回False,省去后续的遍历判断过程。
自己:

class Solution {
    public boolean isValid(String s) {
        Stack<String> st=new Stack<>();
        for(char c:s.toCharArray()){
            if(c=='('||c=='{'||c=='['){
                st.push(String.valueOf(c));//String.valueOf方法:char->string
            }
            else{
                if(st.empty()){
                    return false;
                }
                else{
                    String top=st.peek();
                    st.pop();
                    if( (c==')'&&top.equals("(") ) || (c=='}'&&top.equals("{") ) || (c==']'&&top.equals("[")) ){
                        continue;
                    }
                    else{
                        return false;
                    }
                }
            }
        }
        return st.empty()?true:false;
    }
}

别人:

class Solution {
    public boolean isValid(String s) {
        int n = s.length();
        if (n % 2 == 1) {
            return false;
        }

        Map<Character, Character> pairs = new HashMap<Character, Character>() {{
            put(')', '(');
            put(']', '[');
            put('}', '{');
        }};//!!!
        Deque<Character> stack = new LinkedList<Character>();//!!!
        for (int i = 0; i < n; i++) {
            char ch = s.charAt(i);//!!!
            if (pairs.containsKey(ch)) {
                if (stack.isEmpty() || stack.peek() != pairs.get(ch)) {
                    return false;
                }
                stack.pop();
            } else {
                stack.push(ch);
            }
        }
        return stack.isEmpty();
    }
}

同样的思路,发现linklist也可以用来实现栈,也有push pop peek方法,一般都不用Stack!记住!

class Solution {
    private static final Map<Character,Character> map = new HashMap<Character,Character>(){{
        put('{','}'); put('[',']'); put('(',')');
    }};
    public boolean isValid(String s) {
        //LinkedList<Character> stack = new LinkedList<Character>() {{ add('?'); }};
       //不需要添加?啦,但是要知道这是一种常用的初始化方式
        LinkedList<Character> stack = new LinkedList<Character>();
        for(Character c : s.toCharArray()){
            if(map.containsKey(c)) stack.addLast(c);
            else if(stack.size()==0 || map.get(stack.removeLast()) != c) return false;
        }
        return stack.size() == 0;
    }
}

看看别人的简洁代码!!太nb了!!不用哈希表!

class Solution {
    public boolean isValid(String s) {
        Stack<Character>stack = new Stack<Character>();//!!
        for(char c: s.toCharArray()){
            if(c=='(')stack.push(')');
            else if(c=='[')stack.push(']');
            else if(c=='{')stack.push('}');
            else if(stack.isEmpty()||c!=stack.pop())return false;//妙
        }
        return stack.isEmpty();
    }
}

21 合并两个有序链表

思路1:双指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummyhead=new ListNode(),pre=dummyhead;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                pre.next=l1;
                l1=l1.next;
            }
            else if(l1.val>l2.val){
                pre.next=l2;
                l2=l2.next;
            }
            else{ //其实也可以不用特意写出==的情况
                pre.next=l1;
                l1=l1.next;

                pre=pre.next;

                pre.next=l2;
                l2=l2.next;
            }
            pre=pre.next;
        }
        if(l1!=null){
            pre.next=l1;
        }
        if(l2!=null){
            pre.next=l2;
        }
        return dummyhead.next;
    }
}

思路2:递归

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if(l1==null){
            return l2;
        }
        else if(l2==null){
            return l1;
        }
        if(l1.val<l2.val){
            l1.next=mergeTwoLists(l1.next,l2);
            return l1;
        }
        else{
            l2.next=mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

22 括号生成

思路1:利用dfs生成所有可能的括号组合,每生成一个就判断是否有效
13% 90% 很烂的样子。。

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans=new ArrayList<>();
        StringBuilder path=new StringBuilder();
        dfs(n,n,path,ans);
        return ans;
    }

    void dfs(int left,int right,StringBuilder path,List<String> ans){
        if(left==0&&right==0){
            String s=String.valueOf(path);
            if(isValid(s)){
                ans.add(s);
            }
            else return;
        }
        if(left>0){
            path.append("(");
            dfs(left-1,right,path,ans);
            path.deleteCharAt(path.length()-1);//加完还要减回去
        }
        if(right>0){
            path.append(")");
            dfs(left,right-1,path,ans);
            path.deleteCharAt(path.length()-1);//加完还要减回去
        }
    }
    boolean isValid(String s){
        LinkedList<Character> stack=new LinkedList();
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)=='('){
                stack.push('(');
            }
            else{
                if(stack.size()==0){
                    return false;
                }
                else{
                    stack.pop();
                }
            }
        }
        if(stack.size()!=0)
            return false;
        else
            return true;
    }
}

思路2:dfs+剪枝
体会一下别人的代码,加入了适当的剪枝
注意这里的判断条件: if(count1 >= count2){...}
保证生成的组合必然都是有效!

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<String>();
        generate(res, "", 0, 0, n);
        
        return res;
    }
        //count1统计“(”的个数,count2统计“)”的个数
    public void generate(List<String> res , String ans, int count1, int count2, int n){
        
        if(count1 > n || count2 > n) return;
        
        if(count1 == n && count2 == n)  res.add(ans);
 
        
        if(count1 >= count2){
            String ans1 = new String(ans);
            generate(res, ans+"(", count1+1, count2, n);
            generate(res, ans1+")", count1, count2+1, n);
            
        }
    }
}

模仿思路写的

class Solution {
    public List<String> generateParenthesis(int n) {
        List<String> ans=new ArrayList<>();
        StringBuilder path=new StringBuilder();
        dfs(0,0,n,path,ans);
        return ans;
    }

    void dfs(int left,int right,int n,StringBuilder path,List<String> ans){
        if(left==n&&right==n){
            ans.add(String.valueOf(path));
            return;
        }
        if(left>=right&&left<n){
            path.append("(");
            dfs(left+1,right,n,path,ans);
            path.deleteCharAt(path.length()-1);//加完还要减回去
        }
        if(left>right&right<n){
            path.append(")");
            dfs(left,right+1,n,path,ans);
            path.deleteCharAt(path.length()-1);//加完还要减回去
        }
    }
}
class Solution {
    List<String> res = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs(n, n, "");
        return res;
    }

    private void dfs(int left, int right, String curStr) {
        if (left == 0 && right == 0) { // 左右括号都不剩余了,递归终止
            res.add(curStr);
            return;
        }

        if (left > 0) { // 如果左括号还剩余的话,可以拼接左括号
            dfs(left - 1, right, curStr + "(");
        }
        if (right > left) { // 如果右括号剩余多于左括号剩余的话,可以拼接右括号
            dfs(left, right - 1, curStr + ")");
        }
    }

}
import java.util.ArrayList;
import java.util.List;

public class Solution {

    public List<String> generateParenthesis(int n) {
        List<String> res = new ArrayList<>();
        if (n == 0) {
            return res;
        }

        StringBuilder path = new StringBuilder();
        dfs(path, n, n, res);
        return res;
    }


    /**
     * @param path  从根结点到任意结点的路径,全程只使用一份
     * @param left  左括号还有几个可以使用
     * @param right 右括号还有几个可以使用
     * @param res
     */
    private void dfs(StringBuilder path, int left, int right, List<String> res) {
        if (left == 0 && right == 0) {
            // path.toString() 生成了一个新的字符串,相当于做了一次拷贝,这里的做法等同于「力扣」第 46 题、第 39 题
            res.add(path.toString());
            return;
        }

        // 剪枝(如图,左括号可以使用的个数严格大于右括号可以使用的个数,才剪枝,注意这个细节)
        if (left > right) {
            return;
        }

        if (left > 0) {
            path.append("(");
            dfs(path, left - 1, right, res);
            path.deleteCharAt(path.length() - 1);
        }

        if (right > 0) {
            path.append(")");
            dfs(path, left, right - 1, res);
            path.deleteCharAt(path.length() - 1);
        }
    }
}

23 合并k个升序链表

思路1:k个指针
每次取k个指针中最小的值,然后指针后移。直到指针都为null。
(思考一下,这其实就是小顶堆呀,没想到!!)
但是代码不会写 而且感觉很复杂 但实际并不复杂
别人写的

class Solution {
    public ListNode mergeKLists(ListNode[] lists) { 
        int k = lists.length;
        ListNode dummyHead = new ListNode(0);
        ListNode tail = dummyHead;
        while (true) {
            ListNode minNode = null;
            int minPointer = -1;
            for (int i = 0; i < k; i++) {
                if (lists[i] == null) {
                    continue;
                }
                if (minNode == null || lists[i].val < minNode.val) {
                    minNode = lists[i];
                    minPointer = i;
                }
            }
            if (minPointer == -1) {
                break;
            }
            tail.next = minNode;
            tail = tail.next;
            lists[minPointer] = lists[minPointer].next;
        }
        return dummyHead.next;
    }
}

思路2:堆、优先队列
用容量为K的最小堆优先队列,把链表的头结点都放进去,然后出队当前优先队列中最小的,挂上链表,然后让出队的那个节点的下一个入队,再出队当前优先队列中最小的,直到优先队列为空
ps: java优先队列默认是小顶堆,可以自己传入排序方法。大顶堆传相反的排序方法就好了。

一开始纠结空指针在队列里怎么办,因为想着队列大小恒定为k,也无法比较值。
但是你为什么要让空指针入队呢,因为队列大小并不恒为k。它容量应该是逐渐减小,而且我们只需要堆顶元素。

ps:优先队列常用方法
peek()//返回队首元素
poll()//返回队首元素,队首元素出队列
add()//添加元素
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false

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

        ListNode dummyHead = new ListNode(0);
        ListNode curr = dummyHead;
        PriorityQueue<ListNode> pq = new PriorityQueue<>(new Comparator<ListNode>() {//!!!
            @Override
            public int compare(ListNode o1, ListNode o2) {
                return o1.val - o2.val;
            }
        });

        for (ListNode list : lists) {
            if (list == null) {
                continue;
            }
            pq.add(list);
        }

        while (!pq.isEmpty()) {
            ListNode nextNode = pq.poll();
            curr.next = nextNode;
            curr = curr.next;
            if (nextNode.next != null) {
                pq.add(nextNode.next);
            }
        }
        return dummyHead.next;
    }
}

思路3:顺序合并
看链接的前置知识和方法1
https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/he-bing-kge-pai-xu-lian-biao-by-leetcode-solutio-2/

看链接的方法2
https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/4-chong-fang-fa-xiang-jie-bi-xu-miao-dong-by-sweet/

思路4:分治
实际是对思路3的优化
没理解

看方法2:分治合并
https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/he-bing-kge-pai-xu-lian-biao-by-leetcode-solutio-2/

看链接的最底部:两两合并
https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/4-chong-fang-fa-xiang-jie-bi-xu-miao-dong-by-sweet/
代码没看没实现

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

        int mid = lists.length/2;
        ListNode[] l1 = new ListNode[mid];
        for(int i = 0; i < mid; i++){
            l1[i] = lists[i];
        }

        ListNode[] l2 = new ListNode[lists.length-mid];
        for(int i = mid,j=0; i < lists.length; i++,j++){
            l2[j] = lists[i];
        }

        return mergeTwoLists(mergeKLists(l1),mergeKLists(l2));

    }
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;

        ListNode head = null;
        if (l1.val <= l2.val){
            head = l1;
            head.next = mergeTwoLists(l1.next, l2);
        } else {
            head = l2;
            head.next = mergeTwoLists(l1, l2.next);
        }
        return head;
    }
}
class Solution {
   public ListNode mergeKLists(ListNode[] lists) {
        if (lists == null || lists.length == 0) return null;
        return merge(lists, 0, lists.length - 1);
    }

    private ListNode merge(ListNode[] lists, int left, int right) {
        if (left == right) return lists[left];
        int mid = left + (right - left) / 2;
        ListNode l1 = merge(lists, left, mid);
        ListNode l2 = merge(lists, mid + 1, right);
        return mergeTwoLists(l1, l2);
    }

    private ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
}

32 最长有效括号

注意:题目要求找出最长有效(格式正确且连续)括号子串的长度。一开始只想到用栈找出格式正确的括号对数,忽略了要一直连续的条件。(栈多用于解决括号问题)
那么既然可以利用栈确定匹配正确的括号对,那么我们怎么求出最长有效(格式正确且连续)括号子串的长度?
思路:
将匹配的()都赋值为1,最后计算最长的连续1的长度
trick:将(的下标入栈,不用将(入栈,因为栈里面都是(

一句话总结:先找到所有可以匹配的索引号,然后找出最长连续数列!

44% 50%

class Solution {
    public int longestValidParentheses(String s) {
        LinkedList<Integer> stack=new LinkedList<>();
        char[] ss=s.toCharArray();
        for(int i=0;i<ss.length;i++){
            if(ss[i]=='('){
                stack.push(i);//将(的下标入栈
            }
            else{
                if(stack.size()==0){
                    ss[i]='0';                    
                }
                else{
                    ss[i]='1';
                    ss[stack.peek()]='1';
                    stack.pop();
                }
            }
        }
        //计算连续1的数目
        int len=0,max=0;
        for(int i=0;i<ss.length;i++){
            if(ss[i]=='1'){
                len++;
            }
            else if(ss[i]!='1'){//其实ss里面可能还有没匹配的'(' 但没关系,只要它不是1就好
                max=Math.max(max,len);//要小心如果最后没有进入到这里,就可能没更新max
                len=0;
            }
        }
        return Math.max(max,len);
    }
}

计算连续1的代码可以这样修改,这样绝对会更新max

//计算连续1的数目
        int len=0,max=0;
        for(int i=0;i<ss.length;i++){
            if(ss[i]!='1'){
                len=0;
                continue;
            }
            len++;
            max=Math.max(max,len);
        }
        return max;

ps:String.valueOf方法:可以将char等类型->string

思路2:倒序
没看懂 没实现
对字符串遍历,进行括弧有效性验证,记录最大的有效长度。同样的方式,倒序再来一次,取最大值。时间复杂度 2*s.length;速度极快,10ms超越100%的人群

public int longestValidParentheses(String s) {
    char[] chars = s.toCharArray();
    return Math.max(calc(chars, 0, 1, chars.length, '('), calc(chars, chars.length -1, -1, -1, ')'));
}
private static int calc(char[] chars , int i ,  int flag,int end, char cTem){
    int max = 0, sum = 0, currLen = 0,validLen = 0;
    for (;i != end; i += flag) {
        sum += (chars[i] == cTem ? 1 : -1);
        currLen ++;
        if(sum < 0){
            max = max > validLen ? max : validLen;
            sum = 0;
            currLen = 0;
            validLen = 0;
        }else if(sum == 0){
            validLen = currLen;
        }
    }
    return max > validLen ? max : validLen;
}

思路3:dp
动态规划,dp[i]表示以i结尾的最长有效括号长度
https://leetcode-cn.com/problems/longest-valid-parentheses/solution/dong-tai-gui-hua-si-lu-xiang-jie-c-by-zhanganan042/
讲解得非常清楚(代码没看 没实现)
注意下这段话:(加入了自己的理解)
情况2: s[i−1]==')' 这种情况下,如果前面有和s[i]组成有效括号对的字符,即形如((....)),这样的话,就要求s[i−1]位置必然是有效的括号对(意味着dp[i-1]>0),否则s[i]绝对无法和前面对字符组成有效括号对,也就意味着dp[i]==0。如果s[i−1]位置是有效的,也就是dp[i-1]>0,这时,我们只需要找到和s[i]配对对位置,并判断其是否是
(即可。和其配对对位置为:i−dp[i−1]−1。


这样的话,即使s[i-1]不是有效子字符串的一部分,也没关系。递推关系可以判断。

下面的代码也没看

class Solution(object):
    def longestValidParentheses(self, s):
        length = len(s)
        if length == 0:
            return 0
        dp = [0] * length
        for i in range(1,length):
                #当遇到右括号时,尝试向前匹配左括号
            if s[i] == ')':
                pre = i - dp[i-1] -1;
                #如果是左括号,则更新匹配长度
                if pre>=0 and s[pre] == '(':
                    dp[i] = dp[i-1] + 2
                    #处理独立的括号对的情形 类似()()、()(())
                    if pre>0:
                        dp[i] += dp[pre-1]
        return max(dp);

ps:动态规划题目分析的 4 个步骤:

  • 确定状态
    1.研究最优策略的最后一步
    2.化为子问题
  • 转移方程
    1.根据子问题定义得到
  • 初始条件和边界情况
  • 计算顺序
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 有效字符串需...
    Shimmer_阅读 167评论 0 1
  • 第六章:数字 第七章:序列,列表,元组 1.序列 序列类型有着相同的访问模式:它的每一个元素可以通过指定一个偏移量...
    m风满楼阅读 869评论 0 2
  • 08.03 1、《王道机试》—— 动态规划搬宿舍步骤:(1)将所有物品重量递增排序(2)用二维数组dp[i][j]...
    gufsicsxzf阅读 528评论 0 0
  • 1.链表 1.实现一个单向链表 2.找出链表相交节点,假设均没有环 3.判断链表是否有环思路:使用快慢两个指针,当...
    X1028阅读 649评论 0 0
  • 动态规划 111. 爬楼梯思路类似斐波那契数列注意考虑第 0 阶的特殊情况 272. 爬楼梯 II思路类似上题,只...
    6默默Welsh阅读 2,407评论 0 1