1.栈的性质:后进先出(LIFO),只能从栈顶删除(pop)或添加元素(push)。
2.栈的实现:
实现一个栈主要是实现push(下压) pop(弹出) 两个操作,数据结构我们选择用STL 的 vector 或 原始数组。
i.利用STL 的vector 我们可以容易地实现一个栈,而不用去考虑关于vector实现的细节。
具体实现如下:
stack.h
#ifndef STACK_H
#define STACK_H
#include <vector>
template<class T>
class MyStack
{
public:
MyStack();
~MyStack();
void push(T item);
T pop();
bool isEmpty();
int size();
private:
std::vector<T>* myStackVectorPointer;
int sizeOfStack;
};
#endif
stack.cpp
#include "stack.h"
template <class T>
MyStack<T>::MyStack() : sizeOfStack(0)
{
myStackVectorPointer = new std::vector<T>();
}
template <class T>
MyStack<T>::~MyStack(){}
template <class T>
void MyStack<T>::push(T item)
{
myStackVectorPointer->push_back(item); //直接利用push_back函数来完成push操作
++sizeOfStack;
}
template <class T>
T MyStack<T>::pop()
{
if (sizeOfStack == 0)
{
return NULL;
}
else
{
return myStackVectorPointer->at(--sizeOfStack);
}
}
template <class T>
bool MyStack<T>::isEmpty()
{
return sizeOfStack == 0;
}
main.cpp
#include <iostream>
#include <string>
#include "stack.h"
#include "stack.cpp"
using namespace std;
int main(int argc, char** argv)
{
MyStack<double> myStack;
for (int i = 0; i != 10; ++i)
{
myStack.push(i + 0.5);
}
double result = 0;
while (!myStack.isEmpty())
{
result += myStack.pop();
}
cout << result << endl;return 0;
}
ii.使用原始数组来实现:
stack.h
#ifndef STACK_H
#define STACK_H
#include <vector>
#include <iostream>
using namespace std;
template<class T>
class MyStack
{
public:
MyStack();
~MyStack();
void push(T item);
T pop();
bool isEmpty();
int size();
private:
void resize(int size);
int sizeOfStack; //栈的大小
int currentStackTop; //当前栈顶的位置
T* pointerOfStackArray; //内置数据结构数组的首地址
};
#endif
stack.cpp:
#include "stack.h"
template <class T>
MyStack<T>::MyStack() : sizeOfStack(0),currentStackTop(0)
{
pointerOfStackArray = new T[1]; //在堆上面分配一个长度为1的数组
}
template <class T>
MyStack<T>::~MyStack(){}
template <class T>
void MyStack<T>::push(T item)
{
if (isEmpty()) //如果当前栈为空,那么就把栈的长度设置为2
{
resize(2);
}
//如果栈已经满了,那么就要动态调整栈的大小,把栈大小设置为当前的两倍。
else if (sizeOfStack == currentStackTop)
{
resize(sizeOfStack * 2);
}
pointerOfStackArray[currentStackTop++] = item;
}
template <class T>
T MyStack<T>::pop()
{
if (isEmpty()) //判空,如果在空栈上面进行pop操作的话,那么输出提示,并结束调用。
{
cout << "The Stack Is Empty" << endl;
return NULL;
}
T currentItem = pointerOfStackArray[--currentStackTop];
//如果pop之后,栈内的元素小于等于栈大小的四分之1,那么就把栈的大小缩短为当前的二分之1。
if (currentStackTop <= sizeOfStack / 4)
{
resize(sizeOfStack / 2);
}
return currentItem;
}
//调整栈大小的函数,在堆上面重新分配一个数组,复制元素,最后修改类里面的数组的首地址。
template <class T>
void MyStack<T>::resize(int size)
{
T* currentArray = new T[size];
for (int i = 0; i != sizeOfStack; ++i)
{
currentArray[i] = pointerOfStackArray[i];
}
pointerOfStackArray = currentArray;
sizeOfStack = size;
}
//判空函数。
template <class T>
bool MyStack<T>::isEmpty()
{
return currentStackTop == 0;
}
main.cpp:
#include <iostream>
#include <string>
#include "stack.h"
#include "stack.cpp"
int main(int argc, char** argv)
{
MyStack<string> myStack;
for (int i = 0; i != 10; ++i)
{
myStack.push(std::to_string(i) + 'a');
}
while (!myStack.isEmpty())
{
cout << myStack.pop() << " ";
}
cout << endl;
return 0;
}
3.总结:
对于这两种实现方式,都是使用了数组来作为内部的数据结构,并且无论是vector 或者自己写的原始数组,在数组满的时候,和数组元素较小的时候,都要动态地调整数组的大小,调用相应的函数(vector的自增长和原始数组的重新分配空间并移动元素 resize() )对于栈这种数据结构,关心的只是当前栈顶的元素(只对栈顶元素进行操作),所以我们可以使用另外的数据结构来当作栈的内置数据结构 --链表。