35.数据结构-栈的概念与实现(下)

137 阅读2分钟

一.静态栈的缺陷

当存储的元素为类类型的时候,静态栈会的对象在创建的时候会多次调用元素类型的构造函数,影响效率,当使用原生数组作为存储空间,在创建创建栈的时候会调用泛指类型T的构造函数,当函数退出的时候又调用析构函数

1.1 测试代码

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

using namespace std;
class Test
{
public:
	Test()
	{
		cout << "Test()" << endl;
	}

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

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

	cout << stack.size() << endl;
}

结果为: 在这里插入图片描述 从结果来看,此时栈中没有任何元素,但是却调用了5次构造函数和析构函数

二.链式栈的实现

实际上就是单链表,定义一个top指针,始终指向链表的首元素,当入栈的时候将新结点的next指针指向top指针,并移动top指针,出栈的直接摧毁首结点即可

2.1 设计要点

  1. 抽象父类Stack的直接子类
  2. 在内部组合使用LinkList类,即链表类,实现栈的链式存储
  3. 只在单链表的头部进行操作 在这里插入图片描述

2.2 入栈

单链表的头插法

	void push(const T& e)
	{
		//直接使用头插法插入链表元素
		m_list.insert(0, e);
	}

2.3 出栈

移除下标为0的元素

	void pop()
	{
		//出栈直接弹出下标为0的结点
		if (m_list.length() > 0)
		{
			m_list.remove(0);

		}
		else
		{
			cout << "no node to pop" << endl;
		}

	}

2.4 获取栈顶元素

	T top()const
	{
		if (m_list.length() > 0)
		{
			m_list.get(0);

		}
		else
		{
			cout << "no node to pop" << endl;
		}
	}

2.5 清空栈

	void clear()
	{
		m_list.clear();
	}

2.6 栈成员个数

	int size()
	{
		return m_list.length();
	}

三.完整代码

3.1 LinkList.h

#ifndef  __LINK_LIST_
#define  __LINK_LIST_

#include <iostream>
using namespace std;

template<class T>
class LinkList
{
public:
	struct Node
	{
		T	  value;
		Node* next;
	};

	mutable struct
	{
		char reverse[sizeof(T)];
		Node* next;
	} m_header;

	int m_length;
	int m_step;
	Node* m_current;

	virtual Node* create()
	{
		return new Node();
	}


	void destroy(Node* pn)
	{
		delete pn;
	}


public:
	
	LinkList()
	{
		m_header.next = NULL;
		m_length = 0;
		m_step = 0;
		m_current = NULL;
	}

	Node* position(int i)const
	{
		Node* pre = reinterpret_cast<Node*>(&m_header);
		for (int pos = 0; pos < i; pos++)
		{
			pre = pre->next;
		}

		return pre;
	}


	
	int find(const T& e)const
	{
		Node* pre = reinterpret_cast<Node*>(&m_header);
		int i = 0;

		while (e != pre->next->value)
		{
			pre = pre->next;
			i++;
		}

		return i;
	}


	bool end()
	{
		return m_current == NULL;
	}


	
	bool move(int i, int step = 1)
	{
		bool ret = (i >= 0) && (i <= m_length);
		if (ret)
		{
			m_current = position(i)->next;
			m_step = step;
		}

		return ret;
	}



	T current()
	{

		if (!end())
		{
			return m_current->value;
		}
		else
		{
			cout << "current end()" << endl;
			return -1;//不知道写啥值
		}
	}

	
	bool next()
	{
		int i = 0;
		while (!end() && i < m_step)
		{
			m_current = m_current->next;
			i++;
		}

		return i == m_step;
	}


	
	bool insert(int i, const T& e)
	{
		bool ret = (i >= 0) && (i <= m_length);
		if (ret)
		{
			Node* node = create();

			if (node != NULL)
			{
				Node* pre = reinterpret_cast<Node*>(&m_header);

				pre = position(i);

				node->value = e;
				node->next = pre->next;
				pre->next = node;
			}
			else
			{
				cout << "no memery to malloc" << endl;
			}
		}

		m_length++;
		cout << "m_l=" << m_length << endl;
		return ret;
	}


	
	bool remove(int i)
	{
		bool ret = (i >= 0) && (i <= m_length);

		if (ret)
		{
			Node* pre = reinterpret_cast<Node*>(&m_header);

			pre = position(i);

			Node* toDel = pre->next;
			pre->next = toDel->next;
			delete toDel;
			m_length--;
		}

		return ret;
	}

	
	bool set(int i, T& e)
	{
		bool ret = (i >= 0) && (i <= m_length);

		if (ret)
		{
			Node* pre = reinterpret_cast<Node*>(&m_header);

			pre = position(i);

			pre->next->value = e;
		}

		return ret;
	}


	T get(int i) const
	{
		bool ret = (i >= 0) && (i <= m_length);
		T e;

		if (ret)
		{
			Node* pre = reinterpret_cast<Node*>(&m_header);

			pre = position(i);

			e = pre->next->value;
		}

		return e;
	}



	int length() const
	{
		return m_length;
	}

	
	void clear()
	{

		while (m_header.next != NULL)
		{
			Node* toDel = m_header.next;
			m_header.next = toDel->next;
			destroy(toDel);
		}

		m_length = 0;
	}

	
	~LinkList()
	{
		clear();
	}

};



#endif

3.2 Stack.h

#ifndef STACK_H
#define STACK_H


template<class T>
class Stack
{
public:
	virtual void push(const T& e) = 0;//入栈
	virtual void pop()			  = 0;//出栈
	virtual T    top() const	  = 0;//返回栈顶元素
	virtual void clear()		  = 0;//清空栈
	virtual int  size()			  = 0;//返回栈的大小
};

#endif

3.3 linkStack.h

#pragma once
#include "LinkList.h"
#include "Stack.h"

template<class T>
class linkStack :public Stack<T>
{
protected:
	LinkList<T> m_list;
public:
	void push(const T& e)
	{
		//直接使用头插法插入链表元素
		m_list.insert(0, e);
	}

	void pop()
	{
		//出栈直接弹出下标为0的结点
		if (m_list.length() > 0)
		{
			m_list.remove(0);

		}
		else
		{
			cout << "no node to pop" << endl;
		}

	}

	T top()const
	{
		if (m_list.length() > 0)
		{
			return m_list.get(0);
		}
		else
		{
			cout << "no node to pop" << endl;
		}
	}


	void clear()
	{
		m_list.clear();
	}

	int size()
	{
		return m_list.length();
	}
};

3.4 测试程序

#include <iostream>

#include "linkStack.h"

using namespace std;


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

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

int main()
{
	linkStack<Test> stack;

	cout << stack.size() << endl;
}

3.5 结果

在这里插入图片描述 从结果看到没有再调用构造函数

四:栈实践-符号检测

编译器是如何实现符号检测的呢,下面的实现思路

4.1 实现思路

  • 从第一个字符开始扫描
  • 当遇到普通字符则不管,遇到左符号则压栈,遇到右符号则弹出栈顶符号,并与右符号进行匹配
  • 成功:所有的字符都扫描,且栈为空
  • 失败:匹配失败且所有字符都扫描成功当栈为空

4.2 代码实现

#include <iostream>

#include "linkStack.h"

using namespace std;


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

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

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

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


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

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

	while (code[i] != '\0')
	{
		//左符号则入栈
		if (is_left(code[i]))
		{
			stack.push(code[i]);
		}
		else if (is_rigth(code[i]))
		{
			//遇见右符号,则弹出栈顶符号与其进行匹配
			if ((stack.size() > 0 && is_match(stack.top(), code[i])))
			{
				stack.pop();
			}
			else
			{
				ret = false;
			}
		}
		else if (is_quot(code[i]))
		{
			//如果栈是空,或者当前的引号是左字符(也就是和栈顶不匹配),则入栈
			if ((stack.size() == 0) || (!is_match(stack.top(),code[i])))
			{
				
				stack.push(code[i]);
			}
			else if(is_match(stack.top(), code[i]))
			{
				//匹配上了
				stack.pop();
			}
		}
		i++;
	}

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


int main()
{

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

结果: 在这里插入图片描述

五.小结

  • 栈后进先出的特性适合检测成对出现的符号
  • 栈适合就近匹配的场合