概念
- 栈是限定仅在表尾进行插入或删除操作的线性表。
- 对栈来说,表尾称为栈顶,表头称为栈底,不含元素的空表称为空栈。
- 栈的修改是按照后进先出的原则进行的,也称为LIFO
基本操作
- InitStack(),构造一个空栈
- ClearStack(),将栈清空
- isEmpty(),判断栈是否为空,是返回true,否则返回false
- StackLength(),返回栈的长度
- Top(&elem),返回栈顶元素
- Push(&elem),向栈顶添加元素
- Pop(),删除栈顶元素
- Traverse(),遍历元素一遍
实现(顺序表和链接表)
#define STACK_INIT_SIZE 5
#define INCREMENT 2
template<typename T>
class myStack
{
public:
myStack();
~myStack();
void ClearStack();
bool isEmpty();
int StackSize();
T Top();
void Push(T elem);
T Pop();
void StackTraverse(void (*visit)(T));
private:
T* base;
T* top;
int count;
int stackSize;
};
template<typename T>
myStack<T>::myStack()
{
base = new T[STACK_INIT_SIZE];
top = base;
count = 0;
stackSize = STACK_INIT_SIZE;
}
template<typename T>
myStack<T>::~myStack()
{
delete[] base;
}
template<typename T>
void myStack<T>::ClearStack()
{
delete[] base;
base = new T[STACK_INIT_SIZE];
top = base;
count = 0;
}
template<typename T>
bool myStack<T>::isEmpty()
{
if (count == 0 && top == base)
return true;
else
return false;
}
template<typename T>
int myStack<T>::StackSize()
{
return count;
}
template<typename T>
T myStack<T>::Top()
{
return *(top-1);
}
template<typename T>
void myStack<T>::Push(T elem)
{
if (count == stackSize)
{
T* newBase = new T[stackSize + INCREMENT];
for (int i = 0; i < count; i++)
{
newBase[i] = base[i];
}
top = &(newBase[stackSize]);
*top = elem;
top++;
count++;
delete[]base;
base = newBase;
stackSize += INCREMENT;
}
else
{
*top = elem;
top++;
count++;
}
}
template<typename T>
T myStack<T>::Pop()
{
count--;
top--;
return *top;
}
template<typename T>
void myStack<T>::StackTraverse(void(*visit)(T))
{
for (int i = count - 1; i >= 0; i--)
{
visit(base[i]);
}
}
template<class T>
struct Node
{
T elem;
Node* next;
Node()
{
next = NULL;
}
};
template<class T>
class myStack2
{
public:
myStack2();
~myStack2();
void ClearStack();
bool isEmpty();
int StackSize();
T Top();
void Push(T elem);
T Pop();
void StackTraverse(void(*visit)(T));
private:
Node<T>* top;
int count;
};
template<class T>
myStack2<T>::myStack2()
{
top = NULL;
}
template<class T>
myStack2<T>::~myStack2()
{
Node<T>* tmp = NULL;
while (top != NULL)
{
tmp = top;
top = top->next;
delete tmp;
}
}
template<class T>
void myStack2<T>::ClearStack()
{
Node<T>* tmp = NULL;
while (top != NULL)
{
tmp = top;
top = top->next;
delete tmp;
}
count = 0;
}
template<class T>
bool myStack2<T>::isEmpty()
{
return top == NULL;
}
template<class T>
int myStack2<T>::StackSize()
{
return count;
}
template<class T>
T myStack2<T>::Top()
{
return top->elem;
}
template<class T>
void myStack2<T>::Push(T elem)
{
Node<T>* tmp = top;
top = new Node<T>;
top->elem = elem;
top->next = tmp;
count++;
}
template<class T>
T myStack2<T>::Pop()
{
T ret;
if (top != NULL)
{
Node<T>* tmp = top;
ret = tmp->elem;
top = top->next;
delete tmp;
count--;
}
return ret;
}
template<class T>
void myStack2<T>::StackTraverse(void(*visit)(T))
{
Node<T>* cur = top;
while (cur != NULL)
{
visit(cur->elem);
cur = cur->next;
}
}
ps:写代码时候遇到的问题
- 用template模板写类的时候需要把类的h文件和cpp文件写在一起,否则会报链接错误
- 顺序表实现要难一点,因为地址是已经分好了,所以不能用判断top和NULL是否相等来判断是否空栈,所以需要top指针指向下一个push进来的,如空栈top指向分配的第一个地址,取top和pop的时候需要返回的是top-1的地址指向的元素,与链表很直观的理解不同。
- 自己的问题:模板的格式和传递函数的写法有点生疏,算是复习了一遍。
栈的应用
- 数制转换:将10进制数x转成n进制,只需不断让x%n的值push进栈,再让x/=x,直到x为0,然后顺序把栈中元素pop出并顺序输出即可完成进制转换。
- 括号匹配:遇到左括号push进去,遇到右括号判断栈顶元素是否对应的左括号,是则pop出,否则括号不匹配。
- 行编辑软件:设立一个输入缓冲区,接受用户输入的一行字符,然后逐行存入用户数据区。用户发现输入一个错误字符的时候可以用一个退格符“#”表示前一个字符无效,“@”表示之前的字符无效。栈实现:每遇到一个输入就push进去,遇到“#”就pop,遇到“@”就clear。