前言
本学期学习了C语言版本的数据结构,也算对栈和队列初步的有了一点总结。(C语言的比起C++使用栈和队列尽管更加繁琐,c++可以直接使用STL容器中自带的栈和队列,以及有一系列模板函数,十分便捷,但用C语言重新写每一个功能的函数还是能利于我们更好的掌握栈和队列这两个非常实用的结构)。
初步介绍
栈和队列是两种重要且实用的线性结构,从数据结构的角度来看,可以把栈和队列看成特殊的线性表(操作受限),尽管从数据类型的角度来看,他们是和线性表不同的抽象数据类型。
栈
栈的基本概念
这里是引用数据结构(C语言版)严蔚敏 吴伟民 编著
栈(stack)是限定仅在表尾进行插入或删除操作的线性表。因此,对栈来说,表尾端有特殊含义,称为栈顶(top),相应地,表头端称为栈底(bottom)。不含元素的空表称为空栈。
栈的存储结构
栈有两种存储方式,一种是基于数组的顺序存储结构,一种是基于链表的链式存储结构,以下代码是顺序存储结构。
{}
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct
{
int *base;
int *top;
int stacksize;
}sxstack;
sxstack s;
栈的基本操作
Initstack():创建栈
void initstack(sxstack&s) //创建栈
{
s.base=(int*)malloc(STACK_INIT_SIZE*sizeof(int));
s.top=s.base;
s.stacksize=STACK_INIT_SIZE;
return;
}
Destorystack( ) : 销毁栈
void destorystack(sxstack&s) //销毁栈
{
free(s.base);
s.base=s.top=NULL;
return;
}
clearstack( ) :置栈为空栈
void clearstack(sxstack&s) //置栈为空栈
{
s.top=s.base;
return;
}
stackempty( ) : 判空
int stackempty(sxstack s) //判空
{
return s.base==s.top;
}
stacklenth( ) : 栈长度
int stacklenth(sxstack s) //栈长度
{
return s.top-s.base;
}
gettop( ) : 取栈顶元素
int gettop(sxstack s) //取栈顶元素
{
return *(s.top-1);
}
push( ) : 入栈
void push(sxstack&s,int e) //入栈
{
if (s.top-s.base>=s.stacksize)
{
s.base=(int*)realloc(s.base,(s.stacksize+STACKINCREMENT)*sizeof(int));
s.top=s.base+s.stacksize;
}
*s.top++=e;
return;
}
pop( ) : 出栈
int pop(sxstack&s) //出栈
{
return *--s.top;
}
int main()
{
initstack(s);
return 0;
}
栈的应用
(前两个程序都是课下作业比较粗糙,见谅)
1.括弧匹配
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
typedef struct
{
char *base;
char *top;
int stacksize;
}sxstack;
sxstack s;
void initstack(sxstack&s) //创建栈
{
s.base=(char*)malloc(STACK_INIT_SIZE*sizeof(int));
s.top=s.base;
s.stacksize=STACK_INIT_SIZE;
return;
}
void destorystack(sxstack&s) //销毁栈
{
free(s.base);
s.base=s.top=NULL;
return;
}
void clearstack(sxstack&s) //置栈为空栈
{
s.top=s.base;
return;
}
int stackempty(sxstack s) //判空
{
return s.base==s.top;
}
int stacklenth(sxstack s) //栈长度
{
return s.top-s.base;
}
int gettop(sxstack s) //取栈顶元素
{
return *(s.top-1);
}
void push(sxstack&s,int e) //入栈
{
if (s.top-s.base>=s.stacksize)
{
s.base=(char*)realloc(s.base,(s.stacksize+STACKINCREMENT)*sizeof(int));
s.top=s.base+s.stacksize;
}
*s.top++=e;
return;
}
int pop(sxstack&s) //出栈
{
return *--s.top;
}
void matchbracket(sxstack &s)
{
char ch,e;
printf("请输入括弧串进行匹配:");
do
{
ch = getchar();
if(ch=='('||ch=='{'||ch=='[') /*是否为左括号*/
push(s,ch);
else if((ch==')'&&(gettop(s)=='('))||(ch=='}'&&(gettop(s)=='{'))||(ch==']'&&(gettop(s)=='['))) /*为右括号是是否与栈顶元素匹配*/
pop(s);
else
break;
} while (ch!='\n'); /*最后一个字符*/
if(s.top==s.base&&ch=='\n') /*栈空?*/
printf("匹配成功\n");
else
printf("匹配不成功\n");
}
int main()
{
initstack(s);
matchbracket(s);
return 0;
}
2.算术表达式
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
char prior[7][7]={{'>','>','<','<','<','>','>'},{'>','>','<','<','<','>','>'},{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},{'<','<','<','<','<','=','!'},{'>','>','>','>','!','>','>'},
{'<','<','<','<','<','!','='}};
typedef struct
{
char *base;
char *top;
int stacksize;
}sxstack;
void initstack(sxstack &s) //创建栈
{
s.base=(char*)malloc(STACK_INIT_SIZE*sizeof(int));
s.top=s.base;
s.stacksize=STACK_INIT_SIZE;
return;
}
void destorystack(sxstack&s) //销毁栈
{
free(s.base);
s.base=s.top=NULL;
return;
}
void clearstack(sxstack&s) //置栈为空栈
{
s.top=s.base;
return;
}
int stackempty(sxstack s) //判空
{
return s.base==s.top;
}
int stacklenth(sxstack s) //栈长度
{
return s.top-s.base;
}
int gettop(sxstack s) //取栈顶元素
{
return *(s.top-1);
}
void push(sxstack&s,int e) //入栈
{
if (s.top-s.base>=s.stacksize)
{
s.base=(char*)realloc(s.base,(s.stacksize+STACKINCREMENT)*sizeof(int));
s.top=s.base+s.stacksize;
}
*s.top++=e;
return;
}
int pop(sxstack&s) //出栈
{
return *--s.top;
}
int index(char c)
{
switch(c)
{
case '+':return 0;
case '-':return 1;
case '*':return 2;
case '/':return 3;
case '(':return 4;
case ')':return 5;
case '#':return 6;
default:return 7;
}
}
char priority(char a,char b)
{
int x,y;
x=index(a);
y=index(b);
if (x!=7&&y!=7)
return prior[x][y];
else
return '!';
}
int reckon(int a,char c,int b)
{
switch(c)
{
case '+':return a+b;
case '-':return a-b;
case '*':return a*b;
case '/':return a/b;
}
}
int judge(char c)
{
return (c>='0'&&c<='9');
}
void getexperssionvalue()
{
sxstack opnd,optr;
initstack(optr);
initstack(opnd);
push(optr,'#');
printf("请输入算术表达式:");
char c=getchar();
while (c!='#'||gettop(optr)!='#')
{
if (judge(c))
{
push(opnd,c);
c=getchar();
}
else
{
char a,b,exp;
switch(priority(gettop(optr),c))
{
case '<':push(optr,c);c=getchar();break;
case '>':b=(int)(pop(opnd))-48;a=(int)(pop(opnd))-48;exp=pop(optr);push(opnd,(char)(reckon(a,exp,b)+48));break;
case '=':exp=pop(optr);c=getchar();break;
default:break;
}
}
}
printf("结果是:%d\n",(int)(pop(opnd))-48);
}
int main()
{
getexperssionvalue();
return 0;
}
3.表达式的转换
顺便附上一道近期刚完成的一道同时运用了栈和队列的C++程序
这是一道在校oj(在洛谷上也能找到)上的题目(运用了栈和列表)
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<sstream>
#include<cmath>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<list>
#define pb push_back
#define ll long long
using namespace std;
const int maxn=2e5+50;
const int mod=1e6+7;
const long long INF=2147483647;
stack<char>a;
list<int>cnt;
string s,ans;
int judge(char ch)
{
int x;
switch (ch)
{
case '+':x=1;break;
case '-':x=1;break;
case '*':x=2;break;
case '/':x=2;break;
case '^':x=3;break;
case '(':x=0;break;
case ')':x=0;break;
}
return x;
}
int work(int a,int b,char ch)
{
int sum;
switch (ch)
{
case '+':sum=a+b;break;
case '-':sum=a-b;break;
case '*':sum=a*b;break;
case '/':sum=a/b;break;
case '^':sum=(int)(pow(a,b));break;
}
return sum;
}
int main()
{
cin>>s;
//ans="";
for (int i=0;i<s.size();i++)
{
// cout<<s[i]<<endl;
if (isdigit(s[i])) ans+=s[i];
else if (s[i]=='(')
{
a.push(s[i]);
// cout<<"asdjaoifj"<<endl;
// cout<<i<<endl;
// cout<<s[i]<<endl;
// cout<<a.size()<<endl;
}
else if (s[i]==')')
{
// cout<<"asdasda"<<endl;
while (a.top()!='(')
{
// cout<<"size"<<a.size()<<endl;
// cout<<"top"<<a.top()<<endl;
ans+=a.top();
a.pop();
}
// cout<<"234534"<<endl;
a.pop();
}
else
{
while (!a.empty()&&judge(s[i])<=judge(a.top()))
{
ans+=a.top();
a.pop();
}
a.push(s[i]);
}
// cout<<i<<endl;
}
// cout<<2<<endl;
while (!a.empty())
{
ans+=a.top();
a.pop();
// cout<<1<<endl;
}
for (int i=0;i<ans.size();i++)
cout<<ans[i]<<" ";
cout<<endl;
for (int i=0;i<ans.size();i++)
{
if (isdigit(ans[i]))
{
cnt.pb(ans[i]-'0');
}
else
{
int a,b;
a=cnt.back();
cnt.pop_back();
b=cnt.back();
cnt.pop_back();
// cout<<a<<" "<<b<<" "<<endl;
cnt.pb(work(b,a,ans[i]));
for (list<int>::iterator it=cnt.begin();it!=cnt.end();++it)
cout<<*it<<" ";
for (int j=i+1;j<ans.size();j++)
cout<<ans[j]<<" ";
cout<<endl;
}
}
return 0;
}
队列
队列的基本概念
这里是引用数据结构(C语言版)严蔚敏 吴伟民 编著
与栈相反,队列(queue)是一种先进先出(first in first out,缩写为FIFO)的线性表。它只允许在表的一端进行插入,而在另一端删除元素。这和我们日常生活中的排队是一致的,最早进入队列的元素最早离开。在队列中,允许插入的一端叫做队尾(rear),允许删除的一端则成为队头(front)。
队列的存储结构
同样队列也有两种存储结构,一种是基于数组的顺序存储结构,一种是基于链表的链式存储结构。
以下代码是链节点定义
typedef struct QNode
{ Qelemtype data;
struct QNode *next;
}Qnode, *QueuePtr;
typedef struct{
QueuePtr front;
QueeuPtr rear;
} LinkQueue ;
队列也分单向队列和双向队列,双向队列与前者相比多了一个前驱。
队列的基本操作
鉴于老师没有留队列的代码作业😂,所以直接复制粘贴了一下课件
ADT Queue {
数据对象:
D={ai | ai∈ElemSet, i=1,2,…,n, n≥0}
数据关系:
R1={ <a i-1,ai > | ai-1, ai ∈D, i=2,…,n}
约定其中a1 端为队列头, an 端为队列尾
} ADT Queue
InitQueue(&Q)构造一个空队列Q
status InitQueue(LinkQueue &Q) {
Q.front=Q.rear=(QueuePtr)malloc(sizeof(Qnode));
if (!Q.front) exit(overflow);
Q.front->next=NULL;
return OK;
}
DestroyQueue(&Q)队列Q被销毁, 不再存在
status DestoryQueue(LinkQueue&Q) {
while (Q.front) {
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
}
return OK;
}
QueueEmpty(Q)若Q为空队列,则返回TRUE,否则返回FALSE
QueueLength(Q)返回Q的元素个数,即队列的长度。
GetHead(Q, &e)用e返回Q的队头元素
EnQueue(&Q, e)插入元素e为Q的新的队尾元素
status EnQueue(LinkQueue&Q,QElemType e)
{ p=(QueuePtr)malloc(sizeof(QNode));
if (!p) exit(overflow);
p->data=e; p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return OK;
}
DeQueue(&Q, &e)删除Q的队头元素,并用e返回其值
status DeQueue(LinkQueue&Q,QElemType&e) {
if (Q.front==Q.rear) return(error);
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(Q.rear==p) Q.rear=Q.front;//尾结点被删
free(p);
return OK;
}
ClearQueue(&Q)将Q清为空队列
队列的应用
(因为老师没有留队列代码作业,所以附上很久以前用c++做的一道利用队列的BFS)
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int n,m;
int flag[1000][1000];
char s[1000][1000];
struct node
{
int x;
int y;
int step;
};
void bfs(int ex, int ey)
{
queue<node> q;
node temp;
temp.x=ex;
temp.y=ey;
temp.step=0;
flag[ex][ey]=0;
q.push(temp);
while (!q.empty())
{
temp=q.front();
if (temp.x<1||temp.x>n||temp.y<1||temp.y>m)
{
printf("%d step(s) to exit\n",temp.step);
return;
}
q.pop();
node now=temp;
int x=temp.x,y=temp.y,step=temp.step+1;;
switch (s[x][y])
{
case 'N':x--;break;
case 'S':x++;break;
case 'E':y++;break;
case 'W':y--;break;
}
if (flag[x][y]!=-1)
{
printf("%d step(s) before a loop of %d step(s)\n",flag[x][y],step-flag[x][y]);
return;
}
flag[x][y]=step;
now.x=x;
now.y=y;
now.step=step;
q.push(now);
}
return;
}
int main()
{
int nx,ny,ex,ey;
while (scanf("%d%d%d",&n,&m,&ey)!=EOF)
{
if (n==0&&m==0&&ey==0) break;
ex=1;
ey;
memset(s,0,sizeof(s));
for (int i=1;i<=n;i++)
{
scanf("%s",s[i]);
for (int j=m;j>=1;j--)
s[i][j]=s[i][j-1];
s[i][0]=0;
}
memset(flag,-1,sizeof(flag));
bfs(ex,ey);
}
return 0;
}