一、括号匹配问题
程序设计语言是用来编敲代码的工具。计算机硬件能够识别的语言是机器语言,通常人们编写的计算机程序所用的高级语言必须被转换成机器语言才干够被计算机理解。
将高级语言编写的源程序翻译为等价的计算机能够理解的程序的过程称为编译。而实现编译功能的程序称为编译程序。编译程序是计算机中很复杂的一种系统程序。在通常的集成开发环境中都。编译程序都是最精细、最复杂、也最重要的部分。
一个典型的编译程序一般都含有八个部分,即词法分析、语法分析、语义分析、中间代码生成、代码优化程序、目标代码生成、错误检查和处理以及各种信息表格的管理。它们相互协同配合完毕编译任务。当中在语法分析阶段,语法分析程序以词法分析程序所输出的用内部编码格式表示的单词序列作为输入。其任务是分析源程序的结构。推断它是否为对应程序设计语言中的一个合法程序。即判定程序的合法性。
本题要求编写一个程序。该程序能够对输入的一段字符串进行括号匹配检查。若语句中左括号与右括号的数目相等,且能够完整地匹配成对。即表示本语句不存在括号匹配上的错误,此时输出“OK!
”字样。否则表示语句中存在语法错误。程序输出“Wrong!”字样。显然,这是用“栈”这种数据结构来解决的一道典型问题。它的特点能够简单的概括为“后进先出”。假设把握住了这一特点。那么就是抓住了解题的关键。
以下几条语句是存在括号匹配错误的样例:
)(
((())
以下几条语句是不存在括号匹配错误的样例:
()
(a)((b)((c)(d)))
以下给出程序实现的源码。
#include "stdafx.h"
#include <iostream>
#include "LinkStack.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"请输入括号序列(以0结束):";
LinkStack<char> small;
char a;
do{
cin>>a;
switch(a){
case '(':small.Push(a);break;
case ')':
if(!small.IsEmpty()){
small.Pop();
break;
}
if(small.IsEmpty()){
cout<<"Wrong!"<<endl;
system("PAUSE");
return 0;
}
}
}while(a!='0');
if(small.IsEmpty())
cout<<"OK!"<<endl;
else
cout<<"Wrong!"<<endl;
system("PAUSE");
return 0;
}
完毕编码后。编译并执行程序就可以。
---------------------------------
以上内容本来收录在我的新书《算法之美——隐匿在数据结构背后的原理(C++版)》中。该书眼下仍在编辑过程中。还未上市销售。小秀一下出版社给的封面方案先
兴许我还会陆续把该书中的源码上传到此博客,与众位网友分享。
---------------------------------
二、两道经典面试题解析
Leetcode 是一个美国的在线编程站点,上面主要收集了各大IT公司的笔试面试题,对于应届毕业生找工作是一个不可多得的好帮手。我相信许多有求职需求的读者都刷过Leetcode上面的题目。然而,在我写作《算法之美》一书时,我还不知道这个站点的存在。说来也巧,近期有朋友推荐我看下Leetcode站点。我竟然发现了上面许多题目事实上和我书中所举的样例很相似。我前面也已经push了几篇博文,介绍了《算法之美》中例题的解答。以及与之相相似的Leetcode题型。以下这道Valid Parentheses题目显然就是上面括号匹配问题的变种。先来看一下原题:
这里与前面例题的主要区别主要是引入了[ ]和{ },但利用栈结构来解决仍然是很easy的。并且,虽然题目没有明白地表明。但我们应该知道相似{ [ ( ) ] }这种形式也是正确的括号匹配模式。演示样例代码例如以下:
class Solution {
public:
bool isValid(string s) {
stack<char> charStack;
int i = 0;
while(i != s.length()){
if (s[i] != ')' && s[i] != '}' && s[i] != ']')
charStack.push(s[i]);
else{
if (charStack.empty()) return false;
char c = charStack.top();
charStack.pop();
if ((c == '(' && s[i] != ')') || (c == '[' && s[i] != ']') || (c == '{' && s[i] != '}'))
return false;
}
i++;
}
if (charStack.size() == 0)
return true;
else return false;
}
};
Leetcode中另外还提供了一道与此相似的变型题,我们应当举一反三,趁热打铁,一举攻克全部该类型的题目。例如以下,此时我们不再要求判定输入字符串的括号匹配是否正确。而是要检測当中最长的匹配系列的长度。
从Leetcode上所给的提示(图中红框标出)能够看出。该题须要用到动态规划策略来解决。并且Leetcode给出的题目难度等级为Hard。
事实上,Leetcode上全部涉及动态规划的问题都是Hard级别的!
可是。假设我们有捷径。当然不必墨守成规。这道题事实上有个很轻巧的解决的方法,用一个bool数组来标记已经匹配过的字符。找到最长的连续标记的长度就是所求的结果。採用该种方法。我们仅仅要遍历两遍数组就可以,时间复杂度为O(n)。而採用动态规划算法,复杂度则是O(n^2)。
既然我们的方法更易于实现(和理解),复杂度又更低,何乐而不为?以下是演示样例代码。
class Solution {
public:
int longestValidParentheses(string s) {
vector <bool> a(s.length(), false);
stack<int> st;
int i = 0;
while(i != s.length()){
if (s[i] == '(' )
st.push(i);
else if (!st.empty() && s[i] == ')'){
a[i] = true;
a[st.top()] = true;
st.pop();
}
i++;
}
int max_len = 0, cur_len = 0;
i = 0;
while(i != s.length()){
if (a[i]) cur_len++;
else cur_len = 0;
max_len = max(max_len, cur_len);
i++;
}
return max_len;
}
};
须要说明的是。我们使用了一个vector来储存bool数组。
假设你用new来动态生成一个数组,由于数组訪问是基于指针的,所以程序的执行效率会进一步提升,可是注意别忘了用delete来释放内存。