一篇教你搞懂——数据结构之栈和队列


由于篇幅问题,笔者将代码放在了文章的最后,读者可自行摘取。

①栈

在这里插入图片描述

栈(stack)是限定 仅在表尾进行插人或删除操作的线性表。因此,对栈来说,表尾
端有其特殊含义,称为栈顶(top),相应地,表头端称为栈底(bottom)。不含元素的空表称"
为空栈。

假设栈S=(a1,a2...,an,),则称as为栈底元素,a。为栈顶元素。栈中元素按a1,
a2,..,an的次序进栈,退栈的第-一个元素应为栈顶元素。换句话说,栈的修改是按后进
先出的原则进行的(如图3.1(a)所示)。因此,栈又称为后进先出(lastinfirstout)的线
性表(简称LIFO结构),它的这个特点可用图3.1(b)所示的铁路调度站形象地表示。

抽象数据类型栈的定义

栈的基本操作除了在栈顶进行插人或删除外,还有栈的初始化、判空及取栈顶元素
等,下面给出栈的抽象数据类型的定义:
在这里插入图片描述

栈的表示和实现

栈的顺序存储大概就和数组类似,读者只需要再其上记录一下栈顶元素的位置即可(可以使用结构体数组)。下面我们重点讲解链式存储的实现。
下面是链式存储中的结构体的定义:
*struct stack{
int value; // 栈内的数值
struct stack next;
};
int num

InitList(); // 创造一个空栈
对于链式存储来说,就是建立一个value值为特殊值(比如0,代表无元素),和next为null的单一结构体,并且返回头指针。这里定义head为stack的头指针,全局变量num用来记录stack的长度。
DestroyStack(); // 摧毁栈
使用malloc函数包里的free函数即可——free(head)
stack *ClearStack(); // 将head置为空栈
把head的next的值赋为null,即删除了之后的所以栈元素。
IfEmpty(); // 判断是否为空栈
即只需要判断num的值即可,若为0则为空栈,反之亦然。
StackLength(); // 返回栈的长度
返回num的长度即可
GetTop(); // 返回栈顶元素,如果栈为空则返回0
返回栈顶元素,即返回p->next = null 时的value值
Push(); // 进栈element
增加一个结构体数据,并将它连接在stack的最后
Pop(); // 若栈不空,删除栈顶元素
返回栈顶元素,即返回p->next = null 时的value值,然后把这个末尾的指针删去
下面是代码运行的结果截图
在这里插入图片描述

②栈的应用举例

表达式求值

一、表达式求值的规则
表达式求值是程序设计语言编译中的一个基本问题。它的实现就是对“栈”的典型应用。

  首先了解算术四则运算的运算规则:

 (1)先乘除,后加减。

 (2)从左到右计算

 (3)先算括号内,再算括号外

  因此,下面这个算数表达式的计算顺序应为:

  4 + 2 * 3 - 10 / 5

  = 4 + 6 - 10 / 5

  = 10 - 10 / 5

  = 10 - 2

  = 8

任何一个表达式都由操作数(operand)、运算符(operator)和界定符组成:

①操作数即可以是常量,也可以是被说明为变量或常量的标识符。

②运算符可以分为算术运算,关系运算和逻辑运算符。

③界定符有左右括号和结束符等。

  为了叙述的简洁,我们仅讨论简单算数表达式的求值问题,这种表达式只包含加、减、乘、除等四种算术运算。需要时
  ,不难把它推广到更一般的表达式上。

二、运算符优先级
对于两个相继出现的操作符θ1和θ2 有三种关系:

θ1 <θ2    θ1的优先级低于θ2

θ1 =θ2    θ1的优先级等于θ2

θ1 >θ2    θ1的优先级高于θ2

由此可以列出“+-*/”之间的优先级。如下图

在这里插入图片描述
通过图可以看出:加减乘除优先性都低于“(”但是高于“)”,由运算从左到右可知,当θ1=θ2 ,令θ1>θ2;

为了算法简洁,在表达式的左边和右边虚设一个“#”,这一对“#”表示一个表达式求值完成。

“(”=“)”当一对括号相遇时表示括号内已运算完成。

 “)”和“(”、“#”和“(”、“(”和“#”无法相继出现,如果出现则表达式出现语法错误。(实现时,可以用0存储)

这个表如何理解呢?例如:a+b+c,这里有两个运算符,运算符θ1 为+,运算符θ2 也为+ ,查上表,得到“>”的关系,那么意味着先计算前面的+号,也就是先算a+b,得到结果后,再考虑算后面的表达式。

三、算法思路
为实现优先算法,可以使用两个工作栈,一个是OPTR,用于寄存运算符,一个是OPND,用于寄存操作数和运算结果。算法的基本思想是:

(1) 首先置操作数栈为空栈,表达式起始符'#'为栈底元素。

(2)依次读入表达式中的每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先级作相应操作,直至整个表达式求值完毕(OPTR栈顶元素和当前读入的字符均为'#')

算数表达式求值的运算符优先算法。假定输入的表达式语法正确,以'#'做结束符。使用OPTR和OPND分别作为运算符栈和操作数栈。

expression函数的算法逻辑如下。以下是伪代码,需要用真实的代码代替:

setNull(OPTR);

push(OPTR, ‘#’); //将’#'压入操作符OPTR栈

setNull(OPND);

读入字符ch;

do{

  if (ch IN op) //op为运算符的集合

        swith( precede(top(OPTR),ch ) { //比较栈顶元素和ch的优先关系

        case '<':

              push(OPTR,ch);  //栈顶元素优先级低,则压入操作符栈

              读入字符ch;

              break;

        case '=':

              if (ch==')') 

              x = pop(OPTR);   //自己思考什么情况需要这个判断

              读入字符ch;

              break;

        case '>'://栈顶元素优先级高,取出一个运算符,两个操作数,并计算

              theta = pop(OPTR);

              b = pop(0PND);

              a = pop(OPND);

              push(OPND,operate(a, theta, b));//将计算结果压入操作数栈

  }

  else{   //op为操作数的集合

        push(OPND,ch);

        读入字符ch;

  }

}while (( ch != ‘#’) OR ( top(OPTR) != ‘#’))

return top(OPND);

算法中调用了两个函数,precede是判断运算符栈的栈顶运算符与读入的运算符之间的优先关系;

operate作一元运算:a θ b,算出运算结果,例如调用operate('1', '+', '5');算出结果6。

这个问题b站上笔者也为大家找到很好的讲解视频,亲测有用。这里附上连接表达式求值

③队列

抽象数据类型队列的定义

在这里插入图片描述

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

链队列

和线性表类似,队列也可以有两种存储表示。
用链表表示的队列简称为链队列,如图3.10所示。一个链队列显然需要两个分别指
示队头和队尾的指针(分别称为头指针和尾指针)才能惟一确定。这里,和线性表的单链
表一样,为了操作方便起见,我们也给链队列添加一个头结点,并令头指针指向头结点。
由此,空的链队列的判决条件为头指针和尾指针均指向头结点,如图3.11(a)所示。
链队列的操作即为单链表的插人和删除操作的特殊情况,只是尚需修改尾指针或头
指针,图3.11(b)~(d)展示了这两种操作进行时指针变化的情况。下面给出链队列类型
的模块说明。
在这里插入图片描述
链队列的基本操作的定义,大家可以参看抽象数据类型栈的定义,大同小异。鉴于篇幅和内容的难度,笔者帮助大家讲解循环队列的实现,至于链队列笔者对照着课本看看,相信循环队列搞懂之后,链队列的问题也就迎刃而解了。

循环队列

这里给出课本上循环队列的经典示意图,具体的文字笔者细细品味书籍即可。这里重点讲解关于对应不同操作的函数的实现中,需要注意的地方。循环队列即是在顺序栈的基础上改一些地方,相信笔者在了解后面附上的第一个程序之后,也可以自己完成这部分的代码。下面给大家提供队列基本操作的定义和说明。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

终于到代码啦

栈的顺序实现

#include<stdio.h>
#include<malloc.h>
#define LEN sizeof(struct stack)
struct stack{
    
    
	int value;	// 栈内的数值 
	struct stack *next;
};
int num;	// 全局变量,记录栈顶元素的序号
int main()
{
    
    
	struct stack *InitList();	// 创造一个空栈
	void DestroyStack(struct stack *head);	// 摧毁栈
	struct stack *ClearStack(struct stack *head);	// 将head置为空栈
	bool IfEmpty();	// 判断是否为空栈
	int StackLength();	// 返回栈的长度
	int GetTop(struct stack *head);		// 返回栈顶元素,如果栈为空则返回0
	void Push(struct stack *head,int element);	// 进栈element
	int Pop(struct stack *head);	// 若栈不空,删除栈顶元素
	
	// 测试
	struct stack *head;		// 创造栈指针
	int element,length;
	head = InitList();
	Push(head,1);
	Push(head,2);
	element = Pop(head);
	length = StackLength();
	head = ClearStack(head);
	DestroyStack(head);
}

struct stack *InitList()	// 创造一个栈
{
    
    
	// 创造一个栈
	struct stack *head;
	head = (struct stack*)malloc(LEN);
	head->value = 0;
	head->next = NULL;
	num = 0;	// 栈元素的位置为0,即空栈
	return head;
}

void DestroyStack(struct stack *head)
{
    
    
	// 摧毁栈
	free(head);
	printf("delete stack successfully!\n");
}

struct stack *ClearStack(struct stack *head)
{
    
    
	// 将head置为空栈
	head->value = 0;
	head->next = NULL;
	printf("reset successfully!\n");
	return head;
}

bool IfEmpty()
{
    
    
	// 判断是否为空栈	
	bool flag;
	if(num == 0){
    
    
		flag = true;
		printf("stack is empty!\n");
	}else{
    
    
		printf("stack is not empty!\n");
		flag = false;
	}
	return flag;
}

int StackLength()
{
    
    
	// 返回栈的长度
	int length;
	length = num;
	return length;
}

int GetTop(struct stack *head)
{
    
    
	// 返回栈顶元素,如果栈为空则返回0
	struct stack *p;
	int element;
	p = head;
	while(head->next != NULL){
    
    
		p = p->next;
	}
	element = p->value;
	return element;
}

void Push(struct stack *head,int element)
{
    
    
	// 进栈element
	struct stack *p,*add;
	add = (struct stack*)malloc(LEN);
	add->value = element;
	add->next = NULL;
	p = head;
	while(p->next != NULL){
    
    
		p = p->next;
	}
	p->next = add;
	num = num + 1;
}	

int Pop(struct stack *head)
{
    
    
	// 若栈不空,删除栈顶元素
	struct stack *p;
	bool flag;
	int element;
	p = head;
	flag = IfEmpty();
	if(flag == false){
    
    
		element = 0;
		printf("stack is empty! error!\n");
	}else{
    
    
		while(p->next->next != NULL){
    
    
			p = p->next;
		}
		element = p->value;
		p->next = NULL;
		num = num - 1;
	}
	return 0;
}	

猜你喜欢

转载自blog.csdn.net/wlfyok/article/details/113967064
今日推荐