顺序栈的实现和使用数组实现原理一样,都是预先申请一段连续的地址块作为数据域,通过栈顶下标或指针移动完成压栈、出栈等操作。不同的是,使用指针的顺序栈支持栈满时扩容操作,原理更倾向于vector的实现。
顺序栈初始化时申请一块固定大小内存空间保存数据,栈顶指针在内存区域来回移动:
要注意的是,初始时栈为空,bottom和cursor指针都是指向同一个区域,每插入一个元素,给cursor所在的元素赋值,然后cursor后移一位。
不难发现,cursor指针所指向的区域是空的,它所在位置的前一个元素才是真正的栈顶元素,所以每次取栈顶元素或者执行出栈操作时,要先把指针前移一位,然后再弹出cursor所指向的元素。
所以根据这个原则,当栈满的时候,cursor实际所指向的地址已经越界,它位于最后一个元素地址空间的下一个:
此时虽然指针已经非法,但是实际上并不会取到这个地址上的值,所以也不会导致内存错误。这也要求在处理压栈、出栈操作时要小心,不要取向非法内存地址上的值。
对于这个问题也有一个解决的方法,就是在申请内存空间时多申请一个地址,最后多的一个数据块出来存放栈满后的cursor,相对来说这个方法更为安全保险。
类定义
#ifndef _UNSIGNED_INT_
#define uint unsigned int
#endif // !_UNSIGNED_INT_
template
class CSeqStack {
public:
CSeqStack();
~CSeqStack();
void push(const T& data);
T pop();
T& top() const;
uint size() const;
bool empty() const;
private:
T * bottom;
T* cursor;
uint cap;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef _UNSIGNED_INT_
#define uint unsigned int
#endif // !_UNSIGNED_INT_
template
classCSeqStack{
public:
CSeqStack();
~CSeqStack();
voidpush(constT&data);
Tpop();
T&top()const;
uintsize()const;
boolempty()const;
private:
T*bottom;
T*cursor;
uintcap;
};
类实现
template
inline CSeqStack::CSeqStack()
{
bottom = new T[N] + 1;
// bottom = new T[N];
cursor = bottom;
cap = N;
}
template
inline CSeqStack::~CSeqStack()
{
delete[]bottom;
}
template
inline void CSeqStack::push(const T & data)
{
// 容量不足了,重新申请
if (cursor - bottom == cap) {
uint newCap = cap + 1;
// 根据当前容量的两倍扩容
T* tmp = new T[newCap * 2 + 1]; // 多申请一个内存空间
// T* tmp = new T[newCap * 2];
// 拷贝内存
memcpy(tmp, bottom, sizeof(T)*cap);
// 对栈顶指针重新赋值
cursor = tmp + (cursor - bottom);
delete[]bottom;
// 重新赋值栈底指针
bottom = tmp;
cap = newCap * 2;
}
*(cursor++) = data;
}
template
inline T CSeqStack::pop()
{
// 栈顶元素时cursor指向的前一个元素
T tmp = *(--cursor);
return tmp;
}
template
inline T & CSeqStack::top() const
{
// 栈顶元素时cursor指向的前一个元素
return *(cursor - 1);
}
template
inline uint CSeqStack::size() const
{
// 两个指向同一数组的指针相减得到两个元素间的距离
return cursor - bottom;
}
template
inline bool CSeqStack::empty() const
{
return cursor == bottom;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
template
inlineCSeqStack::CSeqStack()
{
bottom=newT[N]+1;
// bottom = new T[N];
cursor=bottom;
cap=N;
}
template
inlineCSeqStack::~CSeqStack()
{
delete[]bottom;
}
template
inlinevoidCSeqStack::push(constT&data)
{
// 容量不足了,重新申请
if(cursor-bottom==cap){
uintnewCap=cap+1;
// 根据当前容量的两倍扩容
T*tmp=newT[newCap*2+1];// 多申请一个内存空间
// T* tmp = new T[newCap * 2];
// 拷贝内存
memcpy(tmp,bottom,sizeof(T)*cap);
// 对栈顶指针重新赋值
cursor=tmp+(cursor-bottom);
delete[]bottom;
// 重新赋值栈底指针
bottom=tmp;
cap=newCap*2;
}
*(cursor++)=data;
}
template
inlineTCSeqStack::pop()
{
// 栈顶元素时cursor指向的前一个元素
Ttmp=*(--cursor);
returntmp;
}
template
inlineT&CSeqStack::top()const
{
// 栈顶元素时cursor指向的前一个元素
return*(cursor-1);
}
template
inlineuintCSeqStack::size()const
{
// 两个指向同一数组的指针相减得到两个元素间的距离
returncursor-bottom;
}
template
inlineboolCSeqStack::empty()const
{
returncursor==bottom;
}