栈的链式存储结构及实现
栈的链式存储结构,简称链栈。
链栈的结构代码:
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStackPtr;
typedef struct LinkStack{
LinkStackPtr top;
int count;
}LinkStack;
栈的链式存储结构–进栈(头插法)
Status Push(LinkStack *S,SElemType e){
LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode));
s->data=e;
s->next=S->top;
S->top=s;
S->cont++;
return ok;
}
栈的链式存储结构–出栈
Status Pop(LinkStack *S,SElemType e){
LinkStackPtr p;
if(StackEmpty(*S))
return ERROR;
*e=S->top->data;
p=S->top;
S->top=S->top->next;
free(p);
S->count--;
return OK;
}
栈的作用
佩波纳契数列:如果兔子在出生两个月后,就有繁殖能力,一队兔子每个月能出生一对小兔子,假设所有兔子都不死,那么一年以后可以繁殖对少对兔子。
如图:
可以发现一队兔子经过6个月就变成了8队兔子,用数学函数来定义就是
代码实现如下:
int Fbi(int i){
if(i<2)
return i=0? 0:1;
return Fbi(i-1)+Fbi(i-2);
}
递归的定义:把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称作递归函数。每个递归定义必须至少有一个条件,满足时不再进行,即不再引用自身而是返回值退出
迭代和递归的区别:
1、迭代使用的是循环结构
2、递归使用选择结构
栈的应用–四则表达式
后缀(逆波兰)表达式:一种不需要括号的后缀表达法,称为逆波兰(Reverse Polish Notation,RPN)表达式
如:9 3 1 - 3 * + 10 2 / + 这样的表达式为后缀表达式(所有符号都是在要运算数字的后面),它是由 9 + ( 3 - 1)* 3 +10 / 2 的中缀表达式变换而来。
中缀表达式转后缀表达式
中缀表达式 9 + ( 3 - 1)* 3 +10 / 2 转化为后缀表达式 9 3 1 - 3 * + 10 2 / + 的规则:
从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级不高于栈顶符号(乘除优先加减)则栈顶元素一次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
队列的定义
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
队列是一种先进先出(First In First Out)的线性表,简称FIFO。允许插入的一端称为队尾,允许删除的一端称为队头。
如图:
队列的抽象数据类型
ADT 队列(Queue)
Data
同线性表,元素具有相同的类型,相邻元素具有前驱和后继关系
Operation
InilQueue(*Q):初始化操作,建立一个空队列Q
DestroyQueue(*Q):若队列Q存在,销毁它
ClearQueue(*Q):清空队列Q
QueueEmpty(*Q):若队列Q为空,返回true,否则返回false
GetHead(Q,*e):若队列QQ存在且非空,用e返回队列Q的头元素
EnQueue(*Q,e):若队列Q存在,插入新元素e到队列Q中并成为队尾
DeQueue(*Q,e):删除队列Q中头元素,并用e返回其值
QueueLength(Q):返回队列Q的元素个数
循环队列
定义:头尾相接的顺序存储结构的队列称为循环队列
我们引入两个指针rear、front. rear指针指向队尾元素的下一个位置、front指针指向 队头元素。这样当front=rear时,队列为空。
如图:
循环队列满的条件是:(rear+1) % QueueSize = front 、计算队列长度的公式为:(rear -front + QueueSize) % QueueSize
所以循环队列的实现代码如下:
typedef int QElemType; //QElemType类型根据实际情况而定,这里假设为int
typedef struct {
QElemType data[MAXSIZE];
int front; //头指针
int rear; //尾指针
}SqQueue;
循环结构初始代码如下:
Status InitQueue(SqQueue *Q){
Q->front = 0;
Q->rear = 0 ;
return OK;
}
循环队列的求长度代码
int QueueLength(SqQueue Q){
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
循环队列入队操作
Status EnQueue(SqQueue *Q,QElemType *e){
if((Q -> reat+1) % MAXSIZE == Q->front )
return ERROR;
Q->data[Q->rear]=e;
Q->rear = (Q->rear + 1) % MAXSIZE;
return OK;
}
循环队列出队操作
Status DeQueue(SqQueue *Q,QElemType *e){
if(Q->front == Q -> rear)
return ERROR;
*e=Q->data[Q->front];
Q->front = (Q->front +1 ) % MAXSIZE;
return OK;
}
队列的链式存储结构实现
队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。
如图:
链队列结构:
typedef int QElemType;
typedef struct QNode{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct{
QueuePtr front,rear;
}LinkQueue;
队列的链式存储结构–入队
如图:
Status EnQueue(LinkQueue *Q,QElemType e){
QueuePtr s= (QueuePtr) malloc(sizeof(QNode));
if(!s)
exit(OVERFLOW);
s->data=e;
s->next=null;
Q->rear->next=s; //把拥有元素e新节点s赋值给原队尾结点的后继
Q->rear=s; //把当前的s设置为队尾结点,rear指向s.
return OK;
}
队列的链式存储结构–出队操作
如图:
代码如下:
Status DeQueue(LinkQueue *Q,QElemType *e){
QueuePtr p;
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;
}
总结
1、栈(stack)是限定仅在表尾进行插入和删除操作的线性表:顺序栈、链栈
2、队列(queue)是只允许在一端进行插入操作,一端进行删除操作的线性表:顺序队列、链队列