算法学习笔记:
part1 : http://blog.csdn.net/minghe_uestc/article/details/10416809
全文下载地址见part 1
1 利用栈解决实际问题
1.1 编写一个栈
只要符合“后入先出”规则的数据结构都可以叫做栈,所以在实现栈的时候有很多方式:
Ø 可以利用数组、链表来组织数据;
Ø 使用链表也可以使用单向链表和双向链表;
Ø 存储的元素可以是指针、基本数据类型和复杂数据类型;
Ø 可以将栈抽象成一个复杂数据类型,也可以不抽象。
在C++/Java中都提供了栈的抽象。提供的这些数据结构是十分好用的,个人觉得十分推荐。
在C++中,stack底层使用dqueue数据结构,即stack是对dqueue的一个简单封装。dqueue其实是几段不连续的数组经过一个统一管理器管理后抽象出来的一个双向队列,参考《STL源码剖析》,书中给出了很详细的说明。
另外需要注意的就是,使用标准模板库的时候,不要在容器中存储复杂数据类型的“值对象”,因为这会引起大量的构造与析构,也不要试图用auto_ptr对容器中的指针进行自动堆管理,因为auto_ptr不能进行值传递。所以标准容器中一般是保存基本数据类型和指针,当然一些小的复杂数据也可以保存一下。但如果保存指针,那对这些对象就需要有效的堆管理机制。
表 16 在C++中使用标准模板库中的栈
#include<iostream> #include<assert.h> #include<stack> usingnamespacestd; typedefintItem; intmain(){
stack<Item>s; Itema=10;Itemb=20; s.push(a); cout<<s.top(); s.push(b); cout<<s.top(); s.pop(); cout<<s.top(); return0; } |
表 17是使用数组实现的栈,在看网上那些恶心的算法代码的时候,他们基本选择这种方式去做。还有很多其他的实现方式,只要最后符合“后入先出”的游戏规则就可以了。
表17 使用数组实现栈
typedefintItem; #defineMAXSIZE 100 Items[MAXSIZE]; inttop=-1; intmain(){
Itema=10; s[++top]=a;//将元素插入栈顶 if(top>=0)//判断栈不为空 cout<<s[top];//查看栈顶元素 if(top>=0) top--;//将栈顶元素出栈 top=-1;//清空栈 return0; } |
1.2 符号最大匹配子串
1.2.1 问题描述
给定一个字符串”[[(()]]{ {[[()()]]}}”,找到最长匹配子串,返回子串的起始位置与长度。
1.2.2 解决思路
利用一个栈进行维护所有待考察的“左符号”,栈顶元素放置的是最当前考察的“左符号”,idx表示当前需要考察的符号,这时候的情况就有:
Ø idx指向“左符号”,则将符号压入栈,然后idx右移。
Ø idx指向“右符号”,这时候分成两种情况
n 如果栈为空,则什么都不做,idx右移
n 如果栈不为空,则比较栈顶元素与idx指向元素,这时候又分为两种情况
u 匹配,栈顶元素推出,idx右移
u 不匹配,清空栈
根据这个思路,不难得到表 18中的代码实现:
表18 求解最长匹配子串
#defineleftSymbol(x)(x=='['||x=='('||x=='{') boolmatch(charl,charr){
if(l=='['&&r==']')returntrue; if(l=='('&&r==')')returntrue; if(l=='{'&&r=='}')returntrue; returnfalse; } #defineMAXSIZE100 charsk[MAXSIZE]; inttop=-1; voidmaxMatch(charp[]){
for(intidx=0;p[idx]!='\0';idx++){
if(leftSymbol(p[idx])){
sk[++top]=p[idx]; continue; } if(top>=0&&match(sk[top],p[idx])){
top--; }else{
top=-1; } } } |
然后开始维护答案,因为需要维护最长匹配子串的起始位置,新增一个栈skidx[]用于维护所有左符号的下标,这个栈与sk[]进行同步。
然后利用两个临时变量记录临时解,对比临时解与已知解的情况来更新最终的解。
表 19 最长匹配子串的答案维护
structAns{
intidx;intlen; Ans():idx(0),len( |