Luna Tech | 有效的括号(Leetcode)

December 29, 2020 字数 850 2 min


1. 题目

https://leetcode-cn.com/problems/valid-parentheses/

https://leetcode.com/problems/valid-parentheses/

判断一个字符串里面的括号是否从左到右成对出现。

  • {[()]} - true
  • ((()))[[[]]] - true
  • {[} - false
  • {()}] - false
  • (( - false
  • ([)] - false

2. 思路

  1. 遍历每个字符,用 stack 来存放左括号;
  2. 遇到右括号的时候,对比 stack 最近添加的符号,假如正好配成一对,就把这个左括号移出 stack,否则直接返回 false;
  3. 遍历结束之后 stack 应该为空;
  4. 特殊情况:
    • 字符串的数量是奇数(不可能配成有效对);
    • stack 为空时,看到了一个右括号;

选择的数据结构

  • Stack - 后进先出
  • Dictionary - 根据右括号找左括号

踩过的坑

  • 尝试了把每个符号转换成一个数字,最终验证求和是否为 0 (这个解法不满足配对顺序的要求,只能满足左右符号数量相等)
  • 用 Regex 来匹配字符的效率很低
  • 用 switch case 来找到配对的左括号不如用 Dictionary 来的快
  • 需要先把 string 变成 char array,这样比直接用 string 来得快

结果

70-80ms 左右。


3. 我的解法(C#)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class Solution {
    private Dictionary<char, char> mappings = new Dictionary<char, char>
    {
        {'}', '{'},
        {']', '['},
        {')', '('}
    };

    public bool IsValid(string s)
    {
        Stack<char> charStack = new Stack<char>();
        char[] arr = s.ToCharArray(); // faster than using string

        // odd number
        if (arr.Count() % 2 == 1)
            return false;

        for (int i = 0; i < arr.Count(); i++)
        {
            char c = arr[i];
            if (charStack.Count() == 0 && mappings.ContainsKey(c))
                return false;
            else
            {
                if(mappings.ContainsKey(c))
                    if (charStack.Peek() == mappings[c])
                        charStack.Pop();
                    else
                        return false;
                else
                    charStack.Push(c);
            }
        }
        return charStack.Count() > 0 ? false : true;
    }
}

4. 优化

  1. 把 for loop 里面的 if-else 逻辑改写成了另一种形式,但是最终的速度差不多;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 1. change if-else logic
public class Solution
{
	private Dictionary<char, char> mappings = new Dictionary<char, char>
	{
		{'}', '{'},
		{']', '['},
		{')', '('}
	};

	public bool IsValid(string s)
	{
		Stack<char> charStack = new Stack<char>();
		char[] arr = s.ToCharArray(); // faster than using string

		// odd number
		if (arr.Count() % 2 == 1)
			return false;

		for (int i = 0; i < arr.Count(); i++)
		{
			char c = arr[i];
			// check 2 scenarios
			if ((charStack.Count() == 0 && mappings.ContainsKey(c)) || (mappings.ContainsKey(c) && charStack.Peek() != mappings[c]))
				return false;
			else if (mappings.ContainsKey(c) && charStack.Peek() == mappings[c])
				charStack.Pop();
			else
				charStack.Push(c);
		}
		return charStack.Count() > 0 ? false : true;
	}
}
  1. 把 for loop 改成 foreach,最终速度也差不多;
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 2. use for loop instead of foreach loop
public class Solution
{
	private Dictionary<char, char> mappings = new Dictionary<char, char>
	{
		{'}', '{'},
		{']', '['},
		{')', '('}
	};

	public bool IsValid(string s)
	{
		Stack<char> charStack = new Stack<char>();
		char[] arr = s.ToCharArray(); // faster than using string

		// odd number
		if (arr.Count() % 2 == 1)
			return false;

		foreach (char c in arr)
		{
			// check 2 scenarios
			if ((charStack.Count() == 0 && mappings.ContainsKey(c)) || (mappings.ContainsKey(c) && charStack.Peek() != mappings[c]))
				return false;
			else if (mappings.ContainsKey(c) && charStack.Peek() == mappings[c])
				charStack.Pop();
			else
				charStack.Push(c);
		}
		return charStack.Count() > 0 ? false : true;
	}
}
  1. 另外还看到有人说为了避免 Peek 报错,可以在创建 stack 的时候给一个初始值,我试了之后发现速度也差不多。

结语

欢迎大家分享更好的解法~


Talk to Luna


Support Luna