什么是栈
栈是一种数据结构, 满足先入后出.
一般栈支持以下几个操作:
1
2
3
4
5
| push(n); //数据入栈
a.pop(); //数据出栈
a.top(); //获取栈顶元素
a.size(); //获取栈中元素数量
a.empty(); //是否是空栈
|
C++中的stack容器
官方文档中, 定义如下
1
2
3
4
| template<
class T,
class Container = std::deque<T>
> class stack;
|
std::stack是一个C++模板类, 有两个模板参数T和Container, T代表容器元素的数据类型, Container则代表stack使用的容器, 默认使用std::deque这个容器. 这意味着, stack相当于是对已有容器的改装, 也可以使用用户自定义的容器.
Container必须提供以下几种方法:
- 支持back();
- 支持pop_back();
- 支持push_back();
相当于都是往Container的后面塞入或者弹出数据, 也就是Container也需要满足的stack的基本功能.
因此, 我们将自定义或者其他C++标准容器转换为stack容器. 以下是官方的demo, 我添加了vector的转换以加强理解.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #include <stack>
#include <deque>
#include <vector>
#include <iostream>
int main()
{
std::stack<int> c1;
c1.push(5);
std::cout << c1.size() << '\n';
std::stack<int> c2(c1);
std::cout << c2.size() << '\n';
std::deque<int> deq {3, 1, 4, 1, 5};
std::stack<int> c3(deq); //stack的Container默认就是deque, 所以无需再次声明
std::cout << c3.size() << '\n';
// int ds[3] = {1, 2, 3}; //使用数组是不行的, 因为普通数组没有实现Container要求的操作
std::vector<int> ds {1, 2, 3};
std::stack<int, std::vector<int>> c4(ds); //vector不是stack默认的Container类型, 所以需要声明
std::cout << c4.size() << '\n';
}
|
括号匹配问题
括号匹配问题一般描述是:
给定一个字符串S, 其中包含’{}’/’[]’/’()‘三种括号对, 例如S1 = “[]{}({})”, S2 = “[{(}])”, 其中S1是合法的, S2是不合法的. 设计一个函数, 判断输入的仅包含括号字符的字符串是否合法.
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| bool isBracketOk(const string &s)
{
const static char brackets_table[][2] = {
{'(', ')'},
{'[', ']'},
{'{', '}'}
};
auto is_match = [=] (const char &a, const char &b) -> bool {
bool match = false;
for (int i = 0; i < 3; i++)
{
match = ((a == brackets_table[i][0]) && (b == brackets_table[i][1]));
if (match)
{
return match;
}
}
return match;
};
auto is_left = [=] (const char &a) -> bool {
for (int i = 0; i < 3; i++)
{
if (a == brackets_table[i][0])
{
return true;
}
}
return false;
};
auto is_right = [=] (const char &a) -> bool {
for (int i = 0; i < 3; i++)
{
if (a == brackets_table[i][1])
{
return true;
}
}
return false;
};
stack<char> ss;
for (const char &c : s){
if (ss.empty())
{
if (is_left(c))
{
ss.push(c);
continue;
}
else
{
return false;
}
}
else
{
if (is_left(c))
{
ss.push(c);
continue;
}
if (is_right(c))
{
if (is_match(ss.top(), c))
{
ss.pop();
continue;
}
else
{
return false;
}
}
}
}
return ss.empty();
}
|
我们先定义一些函数, 用于判断给定字符是不是括号, 是左括号还是右括号, 判断给定字符对是不是匹配的括号.
对待判断的字符串, 如果是左括号则入栈, 如果是右括号, 则从判度栈顶字符和当前字符是不是匹配的括号对, 并弹出;
一个思考
曾经有一段时间, 我坚信一个函数应该且必须只有一个return.
但是渐渐也发现这样做的弊端, 代码会需要重构, 可能会有很多的临时变量, 也可能会有很深的嵌套逻辑, 而且多个return的可读性也不差.
所以, 一个函数该不该有多个return呢? 还有待更多的经验积累, 不能听风就是雨.