栈的相关概念
栈是仅在表尾进行插入,删除操作的线性表
表尾称为栈顶Top,表头称为栈底Base
插入元素到栈顶,称为入栈;从栈顶删除最后一个元素,称为出栈
栈的运算规则:先进后出
一.顺序栈
顺序栈的表示
利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端
为了方便操作,通常Top指示真正的栈顶元素之上的下标地址,所以Top指向内存空间不存放元素
- 空栈:top==base
- 栈满:top-base==stacksize
- 上溢:栈已满还要压入元素
- 下溢:栈已空还要弹入元素
- 栈中元素个数:top-base
顺序栈的定义
#define MAXSIZE 100
typedef char Elemtype; //栈的数据类型
typedef struct Sqstack
{
Elemtype* base; //栈底指针
Elemtype* top; //栈顶指针
int stacksize; //栈的最大容量
}Sqstack;
顺序栈的初始化
bool Initstack(Sqstack& S) //构造一个空栈
{
S.base = new Elemtype[MAXSIZE]; //在堆区开辟内存
if (!S.base) //存储分配失败
{
cout << "顺序栈不存在" << endl;
return false;
}
S.top = S.base; //栈顶指针等于栈底指针,空栈的标志
S.stacksize = MAXSIZE;
return true;
}
判断顺序栈是否为空
bool Isempty(Sqstack& S)
{
if (S.base == S.top)
{
return true;
}
return false;
}
顺序栈长度
int Stacklength(Sqstack& S)
{
return S.top - S.base;
//top指示真正的栈顶元素之上的下标地址
//base指示的元素的下标地址为0
}
顺序栈的清空和销毁
bool Clearstack(Sqstack& S)
{
if (S.base)
{
S.base = S.top; //空栈定义
return true;
}
return false;
}
void Destroystack(Sqstack& S)
{
if (S.base) //栈还存在的条件判断
{
delete S.base; //删除栈底指针
S.stacksize = 0; //栈的容量置为0
S.base = S.top = nullptr; //栈顶和栈底指针均为空
}
}
顺序栈的入栈
- 判断是否栈满,若满则出错(上溢)
- 元素e压入栈顶
- 栈顶指针加1
bool Push(Sqstack& S,const Elemtype& e)
{
if ((S.top - S.base) == S.stacksize)
{
cout << "栈已满,无法入栈" << endl;
return false;
}
*S.top = e; //将元素e压入栈顶
++S.top; //栈顶指针加1
return true;
}
顺序栈的出栈
- 判断是否栈空,若栈为空则出错(下溢)
- 获取栈顶元素e
- 栈顶指针减1
bool Pop(Sqstack& S,Elemtype& e)
{
if (S.base == S.top)
{
cout << "空栈,无法出栈!" << endl;
return false;
}
--S.top; //栈顶指针减1
e = *S.top; //获取栈顶元素e
return true;
}
进制转换
void Change(int x)
{
cout << "八进制转换后为:" << endl;
Sqstack S;
Initstack(S);
int temp = x;
while (temp) {
Push(S, temp % 8);
temp = temp / 8;
}
while (!Isempty(S)) {
int i;
//Pop(S, i);
//cout << i;
}
}
括号匹配
//输入括号()[],判断括号是否匹配成功, # 字符表示输入结束
//例: ([()]) 成功[(())]] 失败
bool Match(void)
{
Sqstack S;
Initstack(S);
char ch, pop;
int flag = 1;
cout << "输入符号(以#为结束标志):" << endl;
cin >> ch;
while (flag && ch != '#') {
switch (ch)
{
case '(':
Push(S, ch);
break;
case'[':
Push(S, ch);
break;
case')':
if (!Isempty(S) && *(S.top-1) == '(')
{
Pop(S, pop);
}
else {
flag = 0;
}
break;
case']':
if (!Isempty(S) && *(S.top - 1) == '[')
{
Pop(S, pop);
}
else {
flag = 0;
}
break;
}
cin >> ch;
}
if (flag && Isempty(S))
{
cout << "匹配成功!" << endl;
return true;
}
else
{
cout << "匹配失败!" << endl;
return false;
}
}
二.链栈
链栈的表示
链栈是运算受限的单链表,只能在链表的头部进行操作
链表的头指针就是栈顶,不需要头结点(在下面代码中,我新建了一个头结点,头结点的数据域用来存放链栈中元素的个数)
基本不存在栈满的情况,空栈就相当于头指针指向空
插入和删除操作仅在栈顶处执行
链栈的定义
typedef char Elemtype;
typedef struct Stacknode
{
Elemtype data;
Stacknode* next;
}*Linkstack
链栈的初始化
void Initstack(Linkstack& L)
{
//创建一个头结点,在头结点的数据域中保存栈中元素的个数
L = new Stacknode;
L->next = nullptr;
L->data = 0;
}
判断链栈是否为空
bool Isempty(Linkstack& L)
{
if (L->next == nullptr)
{
return true;
}
return false;
}
链栈的入栈
void Push(Linkstack& L, const Elemtype& e)
{
Stacknode *p = new Stacknode; //生成一个新结点p
p->data = e; //新结点的数据域置为e
p->next = L->next; //将新结点插入栈顶
L->next = p; //修改栈顶指针
++L->data; //栈中元素个数加1
}
链栈的出栈
void Pop(Linkstack& L, Elemtype& e)
{
if (L->next == nullptr)
{
cout << "栈为空,无法出栈!" << endl;
return ;
}
else {
Stacknode* p = L->next; //生成一个新结点p
e = p->data;
L->next = p->next;
--L->data;
delete p;
}
}
获取链栈的元素的个数
int Stacklength(Linkstack& L)
{
return L->data;
}
创建一个链栈(元素批量入栈)
#define count 6
void Creatstack(Linkstack& L)
{
Elemtype e;
for(int i=1;i<=count;i++)
{
cin >> e;
Push(L, e);
}
}
队列的相关概念
队列是仅在表尾进行插入操作,在表头进行删除操作的线性表
队列的运算规则:先进先出
一.顺序队列
顺序队列的定义
#define MAXQSIZE 100 //定义数组大小
typedef char Elemtype;
typedef struct SqQueue
{
Elemtype* elem;
int front; //队头下标,指向队列头元素
int rear; //队尾下标,指向队尾元素下一个位置
}SqQueue;
循环队列
初始时:front=rear=0
空队标志:front==rear
- 若front=0,rear=MAXQSIZE时,再入队–真溢出
- 若front≠0,rear=MAXQSIZE时,再入队–假溢出
解决假上溢的方法:
将队空间设想成一个循环的表,即分配给队列的m个存储单元可以循环使用,当rear=MAXQSIZE时,若向量的开始端空着,又可以从头使用空着的空间。当front=MAXQSIZE时也一样。
base[0]接在base[MAXQSIZE-1]之后,若rear+1==MAXQSIZE,则令rear=0
(实现方法:利用模运算%)
插入元素:
Q.base[Q.rear]=x
Q.rear=(Q.rear+1)%MAXQSIZE
删除元素:
Q.base[Q.front]=x
Q.front=(Q.front+1)%MAXQSIZE
但是此时会出现一个新的问题,就是队空和队满的标志都是front==rear,要解决这个问题,可以在队列中少用一个元素空间,此时
队空: front== rear
队满: (rear+1)%MAXQSIZE==front
元素个数: (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE
顺序队列的初始化
void Initqueue(SqQueue& Q)
{
Q.elem = new Elemtype[MAXQSIZE];
Q.front = Q.rear = 0; //头指针和尾指针置为0,队列为空的标志
}
判断顺序队列是否为空/满
bool Isempty(SqQueue& Q)
{
if (Q.front == Q.rear)
return true;
return false;
}
//判断是否满队
//如果队尾+1等于队头,表明队已经满了
//该队列是少用一个空间的循环队列,满队和空队的判断条件不一致
bool Isfull(SqQueue& Q)
{
auto rear_next = (Q.rear + 1) % MAXQSIZE;
if (rear_next == Q.front)
return true;
else
return false;
}
进队&批量进队
bool Insertqueue(SqQueue& Q, const Elemtype& e)
{
if (Isfull(Q)) {
cout << "队已满,无法进队!" << endl;
return false;
}
Q.elem[Q.rear] = e; //新元素加入队尾
Q.rear = (Q.rear + 1) % MAXQSIZE;
//队尾指针加1(当成是循环队列)
return true;
}
//批量进队
#define n 6
void Creatqueue(SqQueue& Q)
{
cout << "请输入"<<n<<"个队列元素:" << endl;
Elemtype e;
for (int i = 1; i <= n; i++) {
cin >> e;
Insertqueue(Q, e);
}
}
出队
void Outqueue(SqQueue& Q)
{
if (Isempty(Q))
//如果队头等于队尾,表明队里没有元素,不执行该程序
{
cout << "队列为空,没有元素可以出队" << endl;
}
Elemtype e;
e = Q.elem[Q.front]; //保存队头元素
Q.front = (Q.front + 1) % MAXQSIZE; //队头指针加1
}
获取队列中元素的个数
int Length(SqQueue& Q)
{
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
打印队列
void Printqueue(SqQueue& Q)
{
for (auto i = Q.front; i != Q.rear; i = (i + 1) % MAXQSIZE)
{
cout << Q.elem[i] << " ";
}
}
测试代码
#include <iostream>
using namespace std;
int main(void) {
SqQueue Q;
Initqueue(Q);
Creatqueue(Q);
cout << "此队列为:" << endl;
Printqueue(Q);
Outqueue(Q);
cout <<endl<< "一个元素出队后队列为:" << endl;
Printqueue(Q);
int n1 = Length(Q);
cout<<endl<< "队列中元素的个数为:" << n1 << endl;
cout << "元素出队为:" << endl;
while (!Isempty(Q))
{
cout <<Q.elem[Q.front] << " ";
Outqueue(Q);
}
int n2 = Length(Q);
cout <<endl<< "队列中元素的个数为:" << n2<< endl;
system("pause");
return 0;
}
二.链队
链队的表示
若用户无法估计所用队列的长度,则适合采用链队列
链队的定义
typedef char Elemtype;
typedef struct Qnode
//定义结点,每个结点包括数据域data和指向下一个结点的指针域next
{
Elemtype data;
Qnode* next;
}Qnode;
//再定义一个抽象数据类型,表示队列,建立两个Qnode指针
typedef struct Linkqueue
{
Qnode* front; //链队的头指针
Qnode* rear; //链队的尾指针
}Linkqueue;
链队的初始化
void Initqueue(Linkqueue &L)
{
L.front = L.rear = new Qnode;
L.front->data = 0; //用于保存链队的元素个数
L.rear->next = nullptr; //尾指针的指针域置为空
}
链队的销毁
从队头结点开始,依次释放所有的结点
bool Destroyqueue(Linkqueue& L)
{
while (L.front)
{
Qnode* p = L.front->next;
delete L.front;
L.front = p;
}
return true;
}
链队入队
void Insertqueue(Linkqueue& L, const Elemtype& e)
//把元素插在队尾
{
Qnode* p = new Qnode;
p->data = e;
p->next = nullptr;
L.rear->next = p;
L.rear = p;
++L.front->data; //队头指针数据域储存元素个数,加1
}
链队出队
bool Outqueue(Linkqueue& L, Elemtype &e)
{
if (L.front->next == nullptr)
{
cout << "空队列,无法出队!" << endl;
return false;
}
Qnode* p = L.front->next;
L.front->next = p->next;
e = p->data;
delete p;
--L.front->data;
return true;
}
创建&打印链队
void Creatqueue(Linkqueue& L)
{
cout << "请输入"<<count<<"个链队元素:" << endl;
for (int i = 1; i <= count; i++) {
Elemtype e;
cin >> e;
Insertqueue(L, e);
}
}
void Printqueue(Linkqueue& L)
{
Qnode* p = L.front->next;
while(p)
{
cout << p->data <<" ";
p = p->next;
}
}
测试代码
#include <iostream>
using namespace std;
int main(void)
{
Linkqueue L;
Initqueue(L);
Creatqueue(L);
cout << "此链队为:" << endl;
Printqueue(L);
//int m = L.front->data;
//cout <<endl<< "此时链队中的元素个数为:" << m << endl;
char e1 = L.front->next->data;
cout <<endl<< "此时链队的队头元素为:" << e1 << endl;
Elemtype x;
Outqueue(L,x);
cout << "出队元素为:" << x << endl;
cout << "头元素出队后的链队为:" << endl;
Printqueue(L);
int n = L.front->data;
cout << endl << "此时链队中的元素个数为:" << n << endl;
char e2 = L.front->next->data;
cout << "此时链队的队头元素为:" << e2 << endl;
system("pause");
return 0;
}