栈是一种只能在一端进行插入或删除的线性数据结构,栈的主要特点是后进先出(Last In First Out, FIFO),即后进栈的元素先出栈。栈的应用很广泛,常见的应用场景例如表达式的括号匹配、文本编辑器的撤销功能、以及一些需要将有序数据逆序的场景都需要用到栈。栈的实现较为简单,并且可以由两种数据结构:链表、数组来实现,这篇文章要讲的就是如何用数组和链表来实现一个栈,同时简单讲讲两种实现的优劣。
首先是数据定义,不出意外的话这个系列的文章我都会以数据结构教科书上的风格来实现,也就是 结构体定义数据结构+全局函数实现各种操作 的形式。栈的定义如下:
#define MAXSIZE 20
struct SqStack
{
int *data;
int top;
};
typedef SqStack SS; // SequenceStack = SS
其中top变量表示的是数组中最后一个元素的下标,也就是栈顶元素的下标,在需要获取栈的大小的时候也可以将top+1得到栈的大小。下面是一些基础函数的实现:
SS *createSS()//创建一个栈对象ss
{
SS *ss = new SS;
ss->data = new int[MAXSIZE];
ss->top = -1;//初始时栈为空,设为-1
return ss;
}
void destroySS(SS *ss)//销毁栈
{
delete[] ss->data;
delete ss;
}
void printSS(SS *ss)//输出整个栈
{
cout << "[";
for (int i = 0; i <= ss->top; i++)
{
cout << ss->data[i] << (i == ss->top ? "" : " ");
}
cout << "] top" << endl;//加个top标识栈顶
}
bool isEmpty(SS *ss)
{
return ss->top == -1;//当top为-1时返回真,表示栈为空
}
上面这三个函数都很好理解,我就不过多解释了,下面讲讲主要的入栈和出栈的实现。
顺序栈其实就是数组栈,这里和教科书上统一一下说法而已。先放出栈和入栈的实现代码:
void push(SS *ss, int data)
{
if (ss->top == MAXSIZE)//先判断栈是否满了
{
cout << "stack is full." << endl;
return;
}
ss->data[++ss->top] = data;
}
int pop(SS *ss)
{
if (ss->top == -1)//先判断栈是否为空
{
cout << "stack is empty." << endl;
return INT_MIN; //返回一个标志值代表出错了,无实际意义
}
return ss->data[ss->top--];
}
push和pop的实现都很简单,这里要提一下 ++ss->top 是先自加再引用值,而 ss->top-- 是先引用值再自减,弄清楚顺序。一般而言,栈的实现还会提供一个函数top(),查看栈顶元素:
int top(SS *ss)
{
if (ss->top == -1)
{
cout << "stack is empty." << endl;
return INT_MIN;
}
return ss->data[ss->top];//自返回,不自减
}
写个main函数简单测试一下:
int main()
{
SS *ss = createSS();
cout << isEmpty(ss) << endl;
for (int i = 1; i <= 10; i++)
push(ss, i);
printSS(ss);
for (int i = 1; i <= 10; i++)
pop(ss);
printSS(ss);
cout << isEmpty(ss) << endl;
return 0;
}
运行:
1
[1 2 3 4 5 6 7 8 9 10] top
[] top
1
大功告成。
这里贴下完整代码:
#include
using namespace std;
#define MAXSIZE 20
struct SqStack
{
int *data;
int top;
};
typedef SqStack SS; // SequenceStack = SS
SS *createSS()
{
SS *ss = new SS;
ss->data = new int[MAXSIZE];
ss->top = -1;
return ss;
}
void destroySS(SS *ss)
{
delete[] ss->data;
delete ss;
}
void printSS(SS *ss)
{
cout << "[";
for (int i = 0; i <= ss->top; i++)
{
cout << ss->data[i] << (i == ss->top ? "" : " ");
}
cout << "] top" << endl;
}
bool isEmpty(SS *ss)
{
return ss->top == -1;
}
void push(SS *ss, int data)
{
if (ss->top == MAXSIZE)
{
cout << "stack is full." << endl;
return;
}
ss->data[++ss->top] = data;
}
int pop(SS *ss)
{
if (ss->top == -1)
{
cout << "stack is empty." << endl;
return INT_MIN;
}
return ss->data[ss->top--];
}
int top(SS *ss)
{
if (ss->top == -1)
{
cout << "stack is empty." << endl;
return INT_MIN;
}
return ss->data[ss->top];
}
int main()
{
SS *ss = createSS();
cout << isEmpty(ss) << endl;
for (int i = 1; i <= 10; i++)
push(ss, i);
printSS(ss);
for (int i = 1; i <= 10; i++)
pop(ss);
printSS(ss);
cout << isEmpty(ss) << endl;
return 0;
}
对于数组栈来说,栈的空间是固定的,而基于链表实现的链栈则没有这个问题。我使用带头节点的单链表来实现链栈,以链表头为栈顶,这样每次出栈入栈都可以在O(1)的时间复杂度完成操作。数据定义:
struct Node
{
int data;
Node *next;
Node(int dd = -999, Node *nn = NULL) : data(dd), next(nn) {}
};
其实就是单链表的节点定义,为了简单起见就不再使用结构体包装成栈对象了。下面是一些基础函数的实现:
//LinkedStack = LS
Node *createLS()
{
return new Node;//由于定义了Node结构体的构造函数,这里只需要new一个头节点返回就ok
}
void destroyLS(Node *ls)//链表的销毁,双指针一前一后扫链表,每次移动时释放后面节点的内存
{
Node *cur = ls;
Node *tmp = NULL;
while (cur != NULL)
{
tmp = cur;
cur = cur->next;
delete tmp;
}
}
bool isEmpty(Node *ls)//如果头节点后面是NULL,则栈空
{
return !ls->next;
}
其实就是一些单链表的基础操作,不熟悉的朋友可以回去看看单链表的文章。
接下来就是链栈的Push和Pop的实现:
void push(Node *ls, int data)
{
ls->next = new Node(data, ls->next);
}
int pop(Node *ls)
{
if (isEmpty(ls))
{
cout << "stack is empty." << endl;
return INT_MIN;
}
Node *tmp = ls->next;
int res = tmp->data;
ls->next = ls->next->next;
delete tmp;
return res;
}
其实也没啥好讲的,push操作就是在链表头部插入一个新节点而已,而pop也就是把头节点移除而已,只不过pop需要返回删除掉的节点里储存的数据和释放内存,所以代码稍微长点。
写个main函数简单测试下:
int main()
{
Node *ls = createLS();
cout << isEmpty(ls) << endl;
printLS(ls);
for (int i = 1; i <= 10; i++)
push(ls, i);
cout << isEmpty(ls) << endl;
printLS(ls);
for (int i = 1; i <= 5; i++)
pop(ls);
printLS(ls);
return 0;
}
运行结果:
1
top [] tail
0
top [10 9 8 7 6 5 4 3 2 1 ] tail
top [5 4 3 2 1 ] tail
完成。下面贴下完整代码:
#include
using namespace std;
struct Node
{
int data;
Node *next;
Node(int dd = -999, Node *nn = NULL) : data(dd), next(nn) {}
};
//LinkedStack = LS
Node *createLS()
{
return new Node;
}
void destroyLS(Node *ls)
{
Node *cur = ls;
Node *tmp = NULL;
while (cur != NULL)
{
tmp = cur;
cur = cur->next;
delete tmp;
}
}
bool isEmpty(Node *ls)
{
return !ls->next;
}
void push(Node *ls, int data)
{
ls->next = new Node(data, ls->next);
}
int pop(Node *ls)
{
if (isEmpty(ls))
{
cout << "stack is empty." << endl;
return INT_MIN;
}
Node *tmp = ls->next;
int res = tmp->data;
ls->next = ls->next->next;
delete tmp;
return res;
}
void printLS(Node *ls)
{
Node *cur = ls->next;
cout << "top [";
while (cur != NULL)
{
cout << cur->data << " ";
cur = cur->next;
}
cout << "] tail" << endl;
}
int main()
{
Node *ls = createLS();
cout << isEmpty(ls) << endl;
printLS(ls);
for (int i = 1; i <= 10; i++)
push(ls, i);
cout << isEmpty(ls) << endl;
printLS(ls);
for (int i = 1; i <= 5; i++)
pop(ls);
printLS(ls);
return 0;
}