3.1栈
栈的定义
栈的定义
作为一种限制性线性表,是将线性表的插入和删除运算限制为仅在表的一端进行。
通常将表中允许进行插入、删除操作的一端称为栈顶(Top),表的另一端被称为栈底(Bottom)。
特点:先进后出。
抽象数据类型
ADT Stack{
数据对象:可以是任意类型的数据,但必须属于同一个数据对象
D={ai|ai∈ElemSet,i=1,2,3...n,n≥0}
数据关系:栈中数据元素之间是线性关系
R1={<ai-1,ai>|ai-1,ai∈D,i=2...n},且约定an端为栈顶,a1端为栈底。
基本操作:
InitStack(S)
ClearStack(S)
IsEmpty(S)
IsFull(S)
Push(S,X)
Pop(S,X)
GetTop(S,X)
}ADT Stack
栈的表示和实现(顺序栈、链栈)
顺序栈
定义:用顺序存储结构实现的栈,即利用一组连续的存储单元依次存放自栈底到栈顶的数据元素,同时由于栈的操作的特殊性,还必须附设一个位置指针top(栈顶指针)来动态地指示栈顶元素在顺序栈中的位置。
顺序栈的C语言描述
#define TRUE 1
#define FALSE 0
#define Stack_Size 50
typedef struct
{
StackElementType elem[Stack_Size]; /*用来存放栈顶元素下标*/
int top; /*top=-1表示空栈*/
}SeqStack;
顺序栈与顺序表
顺序栈的基本操作的实现
初始化
判栈空
判栈满
进栈
出栈
读栈顶元素
两栈共享的数据结构
链栈
定义:采用链表作为存储结构实现的栈。为便于操作,采用带头结点的单链表实现栈。
因为栈的插入和删除操作仅限制在表头位置进行,所以链表的表头指针就作为栈顶指针。
top为栈顶指针,始终指向当前栈顶元素前面的头结点
若top->next=NULL,则代表空栈。
注意:链栈在使用完毕时,应该释放其空间。
数据结构定义
typedef struct node{
StackElementType data;
struct node *next;
}LinkStackNode;
typedefLinkStactNode *LinkStact/*指向结点的指针*/
进栈(在栈顶插入元素)
出栈
总结
补充:C语言中的参数传递
函数调用时传送给形参表的实参必须与形参在类型、个数、顺序上保持一致。
参数传递有两种方式:
(1)值传递(参数为整型、实型、字符等)
把实参的值传送给函数局部工作区相应的副本中,函数使用这个副本执行必要的功能。函数修改的是副本的值,实参的值不变。
单向传递,实参的值不会被改变,浪费内存空间
(2)址传递(参数为指针变量、数组名)
参数为指针变量时,形参变化影响实参
参数为数组名时,传递的是数组的首地址,对形参数组所做的任何改变都将反映到实参数组中。
双向传递,可以改变传递过来的参数的内容,即形参的改变会影响实参,通过指针类型将结果带进带出,只是传递的地址,节省内存空间
栈的应用
括号匹配
算法的设计思想:
1)凡出现左括弧,则进栈;
2)凡出现右括弧,首先检查栈是否空若栈空,则表明该“右括弧”多余,否则和栈顶元素比较,若相匹配,则“左括弧出栈”,否则表明不匹配。
3)表达式检验结束时,若栈空,则表明表达式中匹配正确,
否则若栈中仍有等待匹配的左括号,或者读入了一个右括得,而栈中已无等待匹配的同类型左括号,均属于不合法
算法实现
void BracketMatch(char *str)
{ Stack S; int i; char ch;
lnitStack(&S);
for(i=0; str[i]!=\O"; i++){switch(str[i])
{ case '(': case '[': case '{":
Push(&S,str[i]); break;
case ')":case ']:case '}:
if(IsEmpty(S))
{printf(""n右括号多余!"); return; }
else { GetTop (&s,&ch);
if(Match(ch,str[ü]))Pop[&S,&ch);
else
{printf(""In对应的左右括号不同类!"); return; }
}
}
}
if(lsEmpty(S)) printf("In括号匹配!");
else printf(""n左括号多余!");
}
表达式求值(限于二元运算符的表达式)
表达式:操作数(operand)、运算符(operator)、界限符(delimiter)
运算符和界限符统称为运算符(操作符)
操作数:整型常数
运算符:加、减、乘、除
界限符:表达式起始符“#”、表达式结束符“#”
表达式格式:#1+2*3-4/2#
3.2递归
递归的定义
在定义自身的同时又出现了对自身的调用。
直接递归函数:如果一个函数在其定义体内直接调用自己,则称为直接递归函数。
间接递归函数:如果一个函数经过一系列的中间调用语句,通过其他函数间接调用自己,则称为间接递归函数。
栈是函数过程嵌套调用及递归调用管理的有效手段。
具有递归特性的问题
(1)定义的数学函数
(2)具有递归特性的数据结构
(3)可递归求解的问题
递归算法的两个基本特征
递归归纳:将问题转化为比原问题小的同类规模,归纳出一般递推公式,故处理的对象要有规律的递增或递减。
递归终止:当规模小到一定的程度应该结束递归调用,逐层返回;常用条件语句来控制何时结束递归。
总结
优点:递归算法是一种分而治之、把复杂问题分解为简单问题的求解方法。递归程序结构清晰、程序可读性强,且其正确性易于证明,给用户编程带来很大方便。适合解决比较精细的问题。
当问题满足如下条件时,可设计递归算法:
(1)原问题可以层层分解为类似的子问题,子问题规模比原问题小
(2)规模最小的子问题具有直接解
设计递归算法的方法:
(1)寻找分解方法,将原问题分解为类似的子问题求解
(2)设计递归出口,按最小规模子问题确定递归终止条件
缺点:每次调用都要生成工作记录,保存状态信息,入栈;返回时要出栈,恢复状态信息。时间开销大。递归算法效率低(时间和空间两方面),若处理数据规模较大时,要多加权衡。
递归的消除:递归->非递归
单向递归->循环结构
单向递归是指递归函数虽然有益处以上的递归调用语句,但各次递归调用语句的参数只和主调函数有关,相互之间参数无关,并且这些递归调用语句处于算法的最后。
尾递归->循环结构
尾递归是指递归调用语句只有一个,而且是处于算法的最后,尾递归是单向递归的特例。
3.3队列
定义:限定所有的插入操作在表的一端进行,而删除操作在表的另一端进行的线性表。
通常将表中允许进行插入操作的一端称为队尾(rear),允许进行删除操作的一端称为队头(front)。
当队列中没有元素时称为空队,队列的插入叫入队;删除操作叫出队。
特点:先进先出
抽象数据类型:
ADT Queue {
数据对象:
D-{ai| ai∈ElemSet, i=1,2,....n, n≥o }
数据关系;
R1 ={ <a-1,a >a-1,a=D, i=2,...n }
约定an端为队尾,a1端为队头。
基本操作:
1. InitQueue ( &Q)
2.ClearQueue ( &Q)
3.lsEmpty ( Q)
4.IsFull ( Q)
5.EnterQueue ( &Q,x)
6.DeleteQueue ( &Q,x)
7.GetHead ( Q,&x)
8.DestoryQueue ( &Q)
}ADT Queue
队列的表示和实现:
链队列–链式映象
循环队列–顺序映象
链队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5RKj9pf-1665755507377)(D:\桌面\CSDN\数据结构\picture\16.png)]
链队列 数据结构定义
typedef struct node/*链队列中的结点类型*/
{ QueueElementType data;
struct node *next;
}LinkQueueNode;
typedef struct/*链队列类型*/
{ LinkQueueNode *front;
LinkQueueNode *rear;
}LinkQueue;
LinkQueue *Q;
链队列 初始化
int InitQueue(LinkQueue *Q)
{
Q->front=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(Q->front!=NULL)/*申请结点成功*/
{
Q->rear=Q->front;
Q->front->next=NULL;
return(TRUE);
}else return(FALSE);
}
链队列 入队
int EnterQueue(LinkQueue *Q,QueueElementType x)
{
LinkQueueNode * N;
N=(LinkQueueNode *)malloc(sizeof(LinkQueueNode));
if(N!=NULL)
{N->data=x;
N->next=NULL;
Q->rear->next=N;
Q->rear=N;
return(TRUE);
}
else return(FALSE);
}
链队列 出队
int DeleteQueue(LinkQueue *Q,QueueElementType *x
{
LinkQueueNode * p;/*定义了一个临时指针P,指向被删结点*/
if(Q->front==Q->rear)/*空队列*/
return(FALSE);
p=Q->front->next;
Q->front->next=p->next;
if(Q->rear ==p)Q->rear=Q->front;
/*如果队中只有一个元素P则P出队后成为空队*/
*X=p->data;
fee(p);
return(TRUE);
}
循环队列–顺序存储的表示与实现
尽管链队列使用方便,但由于其指针多占存储空间,有时仍需要用顺序结构来表示队列。可以用一组地址连续的存储单元依次存放从队头到队尾的元素,如一维数组Queue[MAXSIZE]
顺序队列中也需要两个“指针”︰
头指针指示队头元素的当前位置;
尾指针指示队尾元素的后一个位置。
循环队列:“假溢出”现象描述
如何解决假溢出:
1.移动元素:麻烦,需要移动大量的元素
2.把顺序队列的数组设想为一个循环表,即规定最后一个单元的后继为第一个单元,这样就可以利用头指针前的空单元,这就构成了循环队列。
循环队列需要解决的两个问题?
1.下标计算问题
2.循环队列“空”与“满”的判定
1.下标计算问题
现在
入队操作为:
sq. element[sq.rear]=x;
sq.rear= ( sq.rear+1 ) % maxsize;
出队操作为:
x=sq. element[sq.front];
sq.front= (sq.front+1)% maxsize ;
2.循环队列“空”与“满”的判定
解决方案1:
设队“空”、队“满”标志;
解决方案2:
少用一个元素空间,当队尾指针所指向的空单元的后继单元是队头元素所在的单元时,则停止入队。
则:
sq.front==sq.rear 为空
sq.front== ( sq.rear+1 ) % maxsize 为满
循环队列 存储结构定义
#define maxsize 50
typedef struct
{ QueueElemghtType element [maxsize];
int front;/*头指针指示器,指示队头元素的当前位置,并不是真正的指针类型,相当于数组元素的下标;*/
int rear; /*尾指针指示器指示队尾元素的后一个位置.*/
}seqQueue;
循环队列 初始化
void lnitQueue(SeqQueue *Q)
{
Q->front=Q->rear=0;
/*0表示位置,对应数组元素的下标*/
}
循环队列 入队
int EnterQueue(SeqQueue *Q,QueueElementType x)
{
if((Q->rear+1)% maxsize ==Q->front)
/*尾指针加1追上头指针,标志队列已经满了*/
return(FALSE);
Q->element[Q->rear]=x;
Q->rear=(Q->rear+1)% maxsize;/*重新设置队尾指针*/
return(TRUE);
循环队列 出队
int DeleteQueue(SeqQueue *Q,QueueElementType *x)
{
if(Q->front== Q->rear)/*队列为空*/
return(FALSE);
*x=Q->element[Q->front];
Q->front=(Q->front+1)% maxsize;/*重新设置头指针*/
returh(TRUE);
}
补充:双端队列
双端队列是指允许两端都可以进行入队和出队的操作的队列。
输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列。
输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列。
总结
栈、队列与一般线性表的区别
nt DeleteQueue(SeqQueue *Q,QueueElementType *x)
{
if(Q->front== Q->rear)/队列为空/
return(FALSE);
*x=Q->element[Q->front];
Q->front=(Q->front+1)% maxsize;/重新设置头指针/
returh(TRUE);
}
#### 补充:双端队列
双端队列是指允许两端都可以进行入队和出队的操作的队列。
[外链图片转存中...(img-272Fw3MF-1665755507380)]
输出受限的双端队列:允许在一端进行插入和删除,但在另一端只允许插入的双端队列。
[外链图片转存中...(img-U1xjC7Jx-1665755507381)]
输入受限的双端队列:允许在一端进行插入和删除,但在另一端只允许删除的双端队列。
[外链图片转存中...(img-se7q1SXN-1665755507381)]
#### 总结
栈、队列与一般线性表的区别
![在这里插入图片描述](https://img-blog.csdnimg.cn/0071621a6a7449089f786859ef3fc179.png#pic_center)