栈和队列知识点

本文介绍了C语言中栈和队列的基础概念、存储结构、基本操作,以及实际应用中的括弧匹配、算术表达式处理和表达式转换。通过实例演示,帮助读者深入理解这两种数据结构的使用和实战技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

本学期学习了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(在洛谷上也能找到)上的题目(运用了栈和列表)

题目链接,来自校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)

题目链接,来自校oj
题目
在这里插入图片描述

#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;	
} 
### 数据结构中的队列 #### 的概念与特性 是一种遵循后进先出(LIFO, Last In First Out)原则的线性数据结构。这意味着最后被压入的元素会最先弹出[^1]。通常支持两个基本操作:`push` `pop`,分别用于向添加元素以及移除元素。 以下是的一个简单实现示例: ```python class Stack: def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): if not self.is_empty(): return self.items.pop() def is_empty(self): return len(self.items) == 0 def peek(self): if not self.is_empty(): return self.items[-1] def size(self): return len(self.items) ``` #### 队列的概念与特性 队列则是一种先进先出(FIFO, First In First Out)的数据结构。最早进入队列的元素会被优先处理。队列的主要操作包括 `enqueue` `dequeue`,前者用来在队尾插入新元素,后者用来从队首移除元素。 下面是一个基于列表实现的队列例子: ```python class Queue: def __init__(self): self.items = [] def enqueue(self, item): self.items.append(item) def dequeue(self): if not self.is_empty(): return self.items.pop(0) def is_empty(self): return len(self.items) == 0 def front(self): if not self.is_empty(): return self.items[0] def size(self): return len(self.items) ``` #### 双端队列的特点 双端队列(Deque)结合了队列的功能,允许在一端或两端执行插入删除操作[^2]。这种灵活性使得双端队列成为一种非常强大的工具,在许多算法场景下都能发挥重要作用。 #### 存储方式 无论是还是队列,它们都可以通过顺序存储或者链式存储的方式来实现。对于顺序存储而言,通常是利用数组作为底层容器;而链式存储则是借助指针连接各个节点形成链条状结构[^3]。 需要注意的是,尽管队列属于线性表范畴,但由于其特定的操作约束条件限制了随机访问能力——也就是说无法像普通线性表那样随意获取其中间位置上的任意元素。 #### 应用领域 - **的应用**: 表达式求值、括号匹配检测、函数调用管理等。 - **队列的应用**: 打印任务调度、广度优先搜索(BFS)、银行排队系统模拟等问题解决过程中经常需要用到队列机制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值