1、什么是栈
栈 ( S t a c k ) (Stack) (Stack)又称为堆栈,是一种受限制的线性表只允许从一端插入和删除数据。因为栈的插入和删除只能在栈顶进行,所以每次删除的元素都是栈中的最后一个元素,故栈的特征是先进后出 ( L I F O ) (LIFO) (LIFO)。
2、栈的相关概念
- 栈顶与栈底:允许元素插入与删除的一端称之为栈顶,另一端称之为栈底。
- 压栈:栈的插入操作称之为进栈,也称之为压栈,入栈。
- 弹栈:栈的删除操作称之为出栈。
- 空栈:不含有任何元素的空表。
3、栈的基本操作
- 弹栈:通常名为 p o p pop pop。
- 压栈:通常名为 p u s h push push。
- 求栈的大小。
- 判断栈是否为空。
- 获取栈顶元素的值。
4、栈的常见分类
1、顺序栈
采用顺序存储的栈称之为顺序栈。利用一组地址连续的存储单元存放从栈底到栈顶的元素,同时用一个指针指向当前栈顶元素的位置(也可以指向当前栈顶元素的前一个位置),以下都以指向栈顶元素为准。
顺序栈几乎是基于数组的栈——以数组为底层数据结构,通常以数组头为栈底,数组头到数组尾为栈顶的生长方向。
#include <iostream>
using namespace std;
#define endl '\n'
int st[100];//用数组存储
int top = -1;//指针始终指向栈顶
int main(){
for(int i = 1; i <= 10; i++){
st[top++] = i;//入栈
}
cout << "the size of the stack is " << top + 1 << endl;//栈的大小
while(top != -1){//判断栈是否为空
cout << st[--top] << " ";//出栈
}
return 0;
}
2、共享栈
利用栈底位置相对不变的特性,可让两个顺序栈共享一个数组空间,将两个栈的栈底分别置于数组的两端,两个栈顶向共享空间的中间生长。
两个栈的指针都指向当前栈顶元素, t o p 0 = − 1 top_0=-1 top0=−1为左侧栈为空, t o p 1 = M a x S i z e top_1=MaxSize top1=MaxSize为右侧栈为空;当且仅当两个栈顶指针相邻 ( t o p 0 + 1 = t o p 1 ) (top_0+1=top_1) (top0+1=top1)时,判断为栈满。
3、链栈
采用链式存储的栈称之为链栈,以链表为底层的数据结构时,以链表头为栈顶,便于节点的插入与删除,压栈产生的新节点将一直出现在链表的头部。优点:不存在栈满溢出的情况。
#include <iostream>
using namespace std;
template<class T>class stack{
private:
struct Node{
T data;
Node *next;
};
Node *head;
Node * p;
int length;
public:
stack(){
head = NULL;
length = 0;
}
void push(T n){
Node * q = new Node;
q->data = n;
if(head == NULL){
q->next = head;
head = q;
p = q;
}
else{
q->next = p;
p = q;
}
length++;
}
T pop(){
if(length <= 0){
abort(); // 中止程序执行,直接从调用的地方跳出
}
Node *q;
T data;
q = p;
data = p->data;
p = p->next;
delete q;
length--;
return data;
}
int size(){
return length;
}
T top(){
return p->data;
}
bool empty(){
if(length == 0) return true;
else return false;
}
void clear(){
if(length > 0) pop();
}
};
int main(){
stack<int>st; //定义一个整型栈
for(int i = 1; i <= 10; i++){
st.push(i); // 将元素压栈
}
cout << "the size of the stack is " << st.size() << endl; //判断栈的大小
while(!st.empty()){ //判断栈是否为空
cout << st.top() << " "; //返回栈顶元素的数值
st.pop(); // 弹出栈顶元素
}
system("pause");
return 0;
}
//the size of the stack is 10
//10 9 8 7 6 5 4 3 2 1
5、什么是队列
队列 ( q u e u e ) (queue) (queue)是只允许在一端进行插入操作,另一端进行删除操作的线性表。它是一种先进先出的线性表,简称 F I F O FIFO FIFO,允许插入的一端称为队尾,允许删除的一端称为队头。不包含任何元素的队列称之为空队列。
6、常见的基本操作
- 出队:通常名为 p o p pop pop。
- 入队:通常名为 p u s h push push。
- 求队列的大小。
- 判断队列是否为空。
- 获取栈顶元素的值。
- 获取队列第一个元素。
- 获取队列最后一个元素。
数组模拟队列
使用数组模拟队列,也称之为顺序队列,指用一块连续的存储单元存放队列中的元素,并用两个指针分别指向队列的收尾。
int q[Size], rear = front = 0;
- 插入元素: q [ + + r e a r ] = x q[++rear] = x q[++rear]=x
- 删除元素: f r o n t + + front++ front++
- 访问队首: q [ f r o n t ] q[front] q[front]
- 访问队尾: q [ r e a r ] q[rear] q[rear]
- 清空队列: r e a r = f r o n t = 0 rear = front = 0 rear=front=0
看完代码我们发现,当我们一直重复入队出队的操作时,我们终将会“用完”数组的所用空间,但是事实上,出队的元素的空间已经被空留出来的,这就出现了假溢出。
循环队列
解决假溢出的方法就是,我们将队列首尾相互连接形成一个循环,我们将这个循环称之为循环队列。
当队首指针 f r o n t = M a x S i z e − 1 front=MaxSize-1 front=MaxSize−1后,再前进一位就自动回到 0 0 0,这可以采用取模运算来实现。
- 初始化: f r o n t = r e a r = 0 front=rear=0 front=rear=0。
- 队首指针进 1 1 1位: f r o n t = ( f r o n t + 1 ) % M a x S i z e front=(front+1)\%MaxSize front=(front+1)%MaxSize。
- 队尾指针进 1 1 1位: r e a r = ( r e a r + 1 ) % M a x S i z e rear=(rear+1)\%MaxSize rear=(rear+1)%MaxSize。
- 队列长度: ( r e a r − f r o n t + M a x S i z e ) % M a x S i z e (rear-front+MaxSize)\%MaxSize (rear−front+MaxSize)%MaxSize。
- 队满条件: ( r e a r + 1 ) % M a x S i z e = = f r o n t (rear+1)\%MaxSize==front (rear+1)%MaxSize==front。
- 队空条件: f r o n t = r e a r front=rear front=rear