顺序栈的问题

当存储的元素类型为类类型,StaicStack 的对象在创建时会多次调用元素类型的构造函数,影响效率!

文件:main.cpp

#include <iostream>
#include "StaitcStack.h"

using namespace std;
using namespace DTLib;

class Test : public Object
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    StaticStack<Test, 5> stack;

    cout << "stack.size() = " << stack.size() << endl;

    return 0;
}

输出:

Test()
Test()
stack.size() = 0
~Test()
~Test()
~Test()
~Test()
~Test()
原因: T m_space[N];

链式栈的存储实现

image.png

链式栈的设计要点

  • 类模板,抽象父类 Stack 的直接子类
  • 在内部组合使用 LinkList 类,实现栈的链式存储
  • 只在单链表成员对象的头部进行操作

image.png

编程实验:基于链式存储结构的栈

文件:LinkStack.h

#ifndef LINKSTACK_H
#define LINKSTACK_H

#include "Stack.h"
#include "LinkList.h"
#include "Exception.h"

namespace DTLib
{

template <typename T>
class LinkStack : public Stack<T>
{
public:
    void push(const T &e) override  // O(1)
    {
        m_list.insert(0, e);
    }

    void pop() override  // O(1)
    {
        if (m_list.length() > 0)
        {
            m_list.remove(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No element in current LinkStack ...");
        }
    }

    T top() const override  // O(1)
    {
        if (m_list.length() > 0)
        {
            return m_list.get(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOpertionExcetion, "No element in current LinkStack ...");
        }
    }

    void clear() override  // O(n)
    {
        m_list.clear();
    }

    int size() const override  // O(1)
    {
        return m_list.length();
    }

    ~LinkStack()  // O(n)
    {
        clear();
    }

protected:
    LinkList<T> m_list;
};

}

#endif // LINKSTACK_H

文件:main.cpp

#include <iostream>
#include "LinkStack.h"

using namespace std;
using namespace DTLib;

class Test : public Object
{
public:
    Test()
    {
        cout << "Test()" << endl;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    LinkStack<Test> stack_1;

    cout << "stack_1.size() = " << stack_1.size() << endl;

    cout << "-------" << endl;

    LinkStack<int> stack_2;

    for (int i=0; i<5; ++i)
    {
        stack_2.push(i);
    }

    while (stack_2.size() > 0)
    {
        cout << stack_2.top() << endl;
        stack_2.pop();
    }

    return 0;
}

输出:

stack_1.size() = 0
-------
4
3
2
1
0

栈的应用实践

符号匹配问题

在 C 语言中有一些成对匹配出现的符号

括号: (), [], {}, <>
引号: '', ""

问题: 如何实现编译器中的符号成对检测?

算法思路

  • 从第一字符开始扫描

    • 当遇见普通字符时忽略
    • 当遇见左符号时压入栈中
    • 当遇见右符号时弹出栈顶符号,并进行匹配
  • 结束

    • 成功:所有字符扫描完毕,且栈为空
    • 失败:匹配失败或所有字符扫描完毕但栈非空

编程实验:符号匹配问题

文件:main.cpp

#include <iostream>
#include "LinkStack.h"

using namespace std;
using namespace DTLib;

bool is_left(char c)
{
    return (c == '(') || (c == '{') || (c == '[') || (c == '<');
}

bool is_right(char c)
{
    return (c == ')') || (c == '}') || (c == ']') || (c == '>');
}

bool is_quot(char c)
{
    return (c == '\'') || (c == '\"');
}

bool is_macth(char l, char r)
{
    return ((l == '(') && (r == ')')) ||
           ((l == '{') && (r == '}')) ||
           ((l == '[') && (r == ']')) ||
           ((l == '<') && (r == '>')) ||
           ((l == '\'') && (r == '\'')) ||
           ((l == '\"') && (r == '\"'));
}

bool scan(const char *code)
{
    bool ret = true;
    int i = 0;
    LinkStack<char> stack;

    code = (code == nullptr) ? "" : code;

    while (ret && code[i] != '\0')
    {
        if (is_left(code[i]))
        {
            stack.push(code[i]);
        }
        else if (is_right(code[i]))
        {
            if ((stack.size() > 0) && is_macth(stack.top(), code[i]))
            {
                stack.pop();
            }
            else
            {
                ret = false;
            }
        }
        else if (is_quot(code[i]))
        {
            if (stack.size() == 0 || !is_macth(stack.top(), code[i]))
            {
                stack.push(code[i]);
            }
            else if (is_macth(stack.top(), code[i]))
            {
                stack.pop();
            }
        }

        ++i;
    }

    return ret && (stack.size() == 0);
}

int main()
{
    cout << scan("<a{b(\'x\')c}d>") << endl;

    return 0;
}

输出:

1

小结

  • 链式栈的实现组合使用了单链表对象
  • 在单链表的头部进行操作能够实现高效的入栈和出栈操作
  • 栈 “后进先出” 的特性适用于检测成对出现的括号
  • 栈非常适合于需要 “就近匹配” 的场合

以上内容整理于狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧