第三章 栈和队列

第三章 栈和队列

栈和队列是两种重要的数据结构。
从数据结构的角度看,栈和队列都是线性表
其特殊性在于,栈和队列是线性表操作的子集,
它们是操作受限的线性表,因此可称为限定性数据结构
从数据类型角度看,它们是和线性表大不相同的两类重要的抽象数据结构。

3.1栈

3.1.1抽象数据类型栈的定义

栈(stack)是限定仅在表尾进行插入或删除操作的线性表,表尾称为栈顶,表头称为栈底。不含元素的栈称为空栈栈是后进先出的线性表
在这里插入图片描述

ADT Stack{
	数据对象:
	数据关系:
	基本操作:
		InitStack(&S);
		DestroyStack(&S);
		ClearStack(&S);
		StackEmpty(S);
		StackLength(S);
		GetTop(S,&e);
		Push(&S,e);
		Pop(&S,&e);
		StackTraverse(S,visit());
}ADT Stack;

在这里插入图片描述

3.1.2栈的表示和实现
//栈的顺序存储表示
#define STACK_INIT_SIZE 100//存储空间初始分配量
#define STACKINCREAMENT 10//存储空间分配增量
typedef struct{
	SElemType *base;	//在栈构造之前和销毁之后,base的值为NULL
	SElemType *top;		//栈顶指针
	int stacksize;		//当前分配的存储空间,以元素为单位
}SqStack;
Stack InitStack(SqStack &S)
{
	//构造一个空栈
	S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
	if(!S.base) exit(OVERFLOW);//存储分配失败
	S.top=S.base;
	S.stacksize=STACK_INIT_SIZE;
	return OK;
}// InitStack
Stack GetTop(SqStack S,ElemType &e)
{
	if(S.top==S.base) return ERROR;
	e=*(S.top-1);
	return OK;
}//GetTop
Status Push(SqStack &S,SElemType e)
{
	//插入元素e为新的栈顶元素
	if(S.top-S.base>=S.stacksize)
	{
		S.base=(SElemType *)realloc(S.base,(S.stacksize+STACKINCREAMENT)*sizeof(SElemType));
		if(!(S.base) exit(OVERFLOW);
		S.top=S.base+S.stacksize;
		S.stacksize+=STACKINCREAMENT;
	}
	*S.top++=e;	//先取值,再自加1
	return OK;
}//Push
Status Pop(SqStack &S,SElemType &e)
{
	//若栈不空,则删除栈顶元素,用e返回其值
	if(S.top==S.base) return ERROR;
	e=*--S.top;	//先自减1,再取值
	return OK;
}

栈的链式表示如图所示,由于栈的操作是线性表操作的特例,则
链栈的操作易于实现,在此不作详细讨论

在这里插入图片描述

//栈的链式存储表示
typedef  struct  Stack_Node
{ 	ElemType   data ;
	struct Stack_Node  *next ;
}Stack_Node ;

链栈基本操作的实现

//(1) 栈的初始化
Stack_Node  *Init_Link_Stack(void)
{   
	Stack_Node  *top ;
	top=(Stack_Node  *)malloc(sizeof(Stack_Node )) ;
	top->next=NULL ; 
	return(top) ;
}
//(2)  压栈(元素进栈)
Status push(Stack_Node *top , ElemType  e)
{  
	Stack_Node  *p ;
	p=(Stack_Node  *)malloc(sizeof(Stack_Node)) ; 
	if (!p)  return  ERROR; 	/*  申请新结点失败,返回错误标志 */
	p->data=e ; 
	p->next=top->next  ; 
	top->next=p ;    /*  钩链  */
	return OK;
}
//(3)  弹栈(元素出栈)/*  将栈顶元素出栈  */
Status pop(Stack_Node  *top , ElemType *e)
{  
	Stack_Node  *p ;
	ElemType  e ;
	if  (top->next==NULL )
	return ERROR ;    /*  栈空,返回错误标志    */
	p=top->next ; e=p->data ;    /*  取栈顶元素  */
	top->next=p->next ;     /*  修改栈顶指针  */
	free(p) ; 
	return OK ;
}

3.2栈的应用举例

3.2.1数制转换

在这里插入图片描述

3.2.1数制转换
十进制数N转化为其他d进制数的转化
//算法3.1
void conversion(){
	InitStack(S);
	scanf("%d",N);
	while(N)
	{
		Push(S,N%8);
		N=N/8;
	}
	while(!StackEmpty(S))
	{
		Pop(S,e);
		printf("%d",e);
	}
}//conversion
3.2.2括号匹配检测[([][])]12345678
#define TRUE  0
#define  FLASE  -1
SqStack  S ; 
S=Init_Stack() ;  /*堆栈初始化*/
int Match_Brackets( )
{  
	char ch , x ;
	scanf(%c” , &ch) ;
	while (asc(ch)!=13)
	{  
		if  ((ch==()||(ch==[))  push(S , ch) ; 
		else if  (ch==]) 
			{  
				x=pop(S) ; 
				if (x!=[)
                {  
					printf("’[’括号不匹配") ; 
                    return FLASE  ;  
				}  
			}
		else if  (ch==)) 
		{  
			x=pop(S) ;
			if (x!=() 
            {
				 printf("’(’括号不匹配") ; 
                 return FLASE  ;
			}
		} 
	}//while
	if  (S.top!=0) 
	{    
		 printf("括号数量不匹配!") ;
		 return FLASE  ;
	}
	else  return TRUE  ; 
}

3.2.3行编辑程序
3.2.4迷宫求解

在这里插入图片描述

typedef struct
{
	int ord;	//通道块在路径上的“序号”
	PosType seat;//通道快在迷宫中的“坐标”
	int di;     //从此通道快走向下一个通道快的“方向”
}SElemTypde;

Status MazePath(MazeType maze,PosType start,PosType end)
{
	//若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中
	//并返回TRUE,否则返回FALSE
	InitStack(S); curpos=start;//设置当前位置为入口地址
	curstep=1;				//探索第一步
	do{ 
		if(Pass(curpos))//当前位置可以通过,则是未曾走过的通道快
		{
			FootPrint(curpose);//留下足迹
			e=(curstep,curpos,1);
			push(S,e)      //加入路径
			if(curstep==end) return TRUE;//到达终点
			curpos=NextPos(curpos,1)//下一个位置是当前位置的东邻
			curstep++;		//探索下一步
		}else		//当前位置不可通过
		{
			if(!StackEmpty(S)
			{
				Pop(S,e);
				while(e.di==4&&!StackEmpty(S))
				{
					MarkPrint(e.seat); Pop(S,e);//留下不能通过的标记,并退回一步
				}//while
				if(e.di<4)
				{
					e.di++; Push(S,e);//换下一个方向探索]
					curpos=NextPos(e.seat,e.di);
				}//if
			}//if
		
		}//else
	}while(!StackEmpty(S));
	return FALSE;
}//MazePath;
3.2.5表达式求值

为了实现算符优先算法,可以使用两个工作栈,一个称作OPTR,
用来寄存运算符,一个称做OPND,用来寄存操作数或运算结果
算法的基本思想是:
(1)首先置操作数栈为空栈,表达式起始符"#"为运算符的栈底元素
(2)依次读入表达式中的每个字符,操作数进OPND,运算符则和OPTR
栈顶元素进行比较,做相应的操作,直至整个表达式求值完毕

OperandType EvaluateExpression()
{
	InitStack(OPTR); Push(OPTR,'#');
	InitStack(OPND); c=getchar();
	while(c!='#'||GetTop(OPTR)!='#')
	{
		if(!In(c,OP)) 
		{
			Push(OPND,c);
			c=getchar();
		}else
			switch(Precede(GetTop(OPTR),c))
			{
				case '<'://栈顶元素优先权低
					Push(OPTR,c); c=getchar();
					break;
				case '=':	//脱括号,并接收下一个字符
					Pop(OPTR,x); c=getchar();
					break;
				case '>':	//退栈,并将运算结果入栈
					Pop(OPTR,theta);
					Pop(OPND,b); Pop(OPND,a);
					Push(OPND,Operate(a,theta,b));
					break;
			}//swith
	}//while
	return GetTop(OPND);
}

3.3栈的递归与实现

递归调用:一个函数(或过程)直接或间接地调用自己本身,简称递归(Recursive)。
递归是程序设计中的一个强有力的工具。因为递归函数结构清晰,程序易读,正确性很容易得到证明。
为了使递归调用不至于无终止地进行下去,实际上有效的递归调用函数(或过程)应包括两部分:递推规则(方法),终止条件。
例如:求n!
在这里插入图片描述

为保证递归调用正确执行,系统设立一个“递归工作栈”,作为整个递归调用过程期间使用的数据存储区。 每一层递归包含的信息如:参数、局部变量、上一层的返回地址构成一个“工作记录” 。每进入一层递归,就产生一个新的工作记录压入栈顶;每退出一层递归,就从栈顶弹出一个工作记录。

从被调函数返回调用函数的一般步骤:
(1) 若栈为空,则执行正常返回。
⑵ 从栈顶弹出一个工作记录。
⑶ 将“工作记录”中的参数值、局部变量值赋给相应的变量;读取返回地址。
⑷ 将函数值赋给相应的变量。
(5) 转移到返回地址。
在这里插入图片描述

3.4队列

在这里插入图片描述
在这里插入图片描述

3.3.1 队列及其基本概念

1 队列的基本概念
队列(Queue):也是运算受限的线性表。是一种先进先出(First In First Out ,简称FIFO)的线性表。只允许在表的一端进行插入,而在另一端进行删除。
队首(front) :允许进行删除的一端称为队首。
队尾(rear) :允许进行插入的一端称为队尾。
例如:排队购物。操作系统中的作业排队。先进入队列的成员总是先离开队列。

2 队列的抽象数据类型定义

ADT Queue{
数据对象:D ={ ai|ai∈ElemSet,  i=1, 2, …, n, n >= 0 }
数据关系:R = {<ai-1, ai> | ai-1, ai∈D,  i=2,3,…,n }约定a1端为队首,an端为队尾。		
基本操作:
Create():创建一个空队列;
EmptyQue():若队列为空,则返回true ,否则返回flase ;
⋯⋯
InsertQue(x) :向队尾插入元素x;
DeleteQue(x) :删除队首元素x;
} ADT Queue
3.4.2队列的顺序表示和实现
//静态顺序队列
#define  MAX_QUEUE_SIZE   100
typedef  struct  queue
{ 
	 ElemType   Queue_array[MAX_QUEUE_SIZE] ;
	int   front ;
	int  rear ;
}SqQueue;

3.4.3循环队列的基本操作

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//1 循环队列的初始化
SqQueue Init_CirQueue(void)
{  	
	SqQueue  Q ;
	Q.front=Q.rear=0;  return(Q) ;
}
//2  入队操作 /*  将数据元素e插入到循环队列Q的队尾  */
Status Insert_CirQueue(SqQueue  Q , ElemType  e)
{  
	if  ((Q.rear+1)%MAX_QUEUE_SIZE== Q.front)
		return  ERROR;      /*  队满,返回错误标志    */
	Q.Queue_array[Q.rear]=e ;   /*  元素e入队  */
	Q.rear=(Q.rear+1)% MAX_QUEUE_SIZE ; /*  队尾指针向前移动  */
	return OK;        /*  入队成功    */
}
//3  出队操作  /*  将循环队列Q的队首元素出队  */
Status Delete_CirQueue(SqQueue  Q, ElemType  *x )
  {   
  	if  (Q.front+1== Q.rear)
		return ERROR ;       /*  队空,返回错误标志    */
	*x=Q.Queue_array[Q.front] ;  /* 取队首元素 */
	Q.front=(Q.front+1)% MAX_QUEUE_SIZE ; /*  队首指针向前移动  */    
	return OK ;
}
3.4.4 队列的链式表示和实现

在这里插入图片描述
在这里插入图片描述

//1   队列的链式存储表示
typedef struct Qnode
{ 
	ElemType    data ;
	struct Qnode  *next ;
}QNode ;
//2 指针结点类型定义:
typedef struct link_queue
{  
	 QNode  *front ,  *rear ;
}Link_Queue ;

3 链队列的基本操作

//⑴ 链队列的初始化
LinkQueue *Init_LinkQueue(void)
{ 
    LinkQueue  *Q ;  QNode  *p ;
	p=(QNode *)malloc(sizeof(QNode)) ; /* 开辟头结点 */
	p->next=NULL ;
	Q=(LinkQueue  *)malloc(sizeof(LinkQueue)) ;    /*  开辟链队的指针结点  */
	Q.front=Q.rear=p ; 
	return(Q) ;
}
//⑵ 链队列的入队操作
 //在已知队列的队尾插入一个元素e ,即修改队尾指针(Q.rear)。
Status  Insert_CirQueue(LinkQueue  *Q , ElemType  e) /*  将数据元素e插入到链队列Q的队尾  */   
{ 
	p=(QNode *)malloc(sizeof(QNode)) ;
	if (!p)  return  ERROR;/*  申请新结点失败,返回错误标志 */
	p->data=e ; p->next=NULL ;       /*  形成新结点 */
	Q.rear->next=p ;  Q.rear=p ;  /*  新结点插入到队尾  */
	return OK;
}
//⑶  链队列的出队操作
Status  Delete_LinkQueue(LinkQueue  *Q, ElemType *x)
{  
    QNode *p ;
	if  (Q.front==Q.rear)  return ERROR ;    /*  队空  */
	p=Q.front->next ;   /*  取队首结点  */
	*x=p->data ; 
	Q.front->next=p->next ;      /*  修改队首指针  */
	if  (p==Q.rear)  Q.rear=Q.front ;
     /*  当队列只有一个结点时应防止丢失队尾指针  */
    free(p) ;   
	return OK ; 
//⑷ 链队列的撤消 /*  将链队列Q的队首元素出队  */
void  Destroy_LinkQueue(LinkQueue  *Q )   
{  
	while  (Q.front!=NULL)
	{  
		Q.rear=Q.front->next;   /*  令尾指针指向队列的第一个结点   */ 
		free(Q.front);      /*  每次释放一个结点  */ 
		Q.ront=Q.rear;		/*  第一次是头结点,以后是元素结点  */
	}
}

*3.5离散事件的模拟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值