栈和队列的定义和特点
栈和队列是一种特殊的线性表,只能在表的端点进行操作
栈的定义和特点
这就是栈的结构,是一个特殊的线性表,只能在栈顶(或者说是表尾)进行操作。其中top为栈顶,base为栈底
栈s的存储结构示意图就这样,插入元素到栈中只能在top进行操作,称为入栈(push)
从栈顶tpo删除一个元素的操作pop称为出栈。
入栈示意图
出栈示意图
栈的操作特性:后进先出。
思考
假设有元素a,b,c入栈,那么有多少种出栈的顺序可能呢?
第一种a,b,c全入栈,然后出栈cba
第二种边进边出,a进a出,b进b出,c进c出。abc
第三种a进a出,b进c进,c出b出,acb
第四种a进b进,b出a出,c进c出,bac
第五种a进,b进b出,c进c出,a出,bca
思考小结
abc入栈共有5种出栈的方式,分别是cba,abc,acb,bac,bca那么cba为什么不能出现呢。这里考虑到栈的操作特性是先进的后出,abc入栈而cba违背了这个特性。由此想到我们生活中栈的例子,
栈又称为后进先出的线性表,简称LIFO
队列的定义和特点
队列示意图
有一个队列 Q=(a1,a2,a3,a4,.......,an)
a1为队头,an为队尾。通常在队尾进行插入,队头进行删除
队列的操作特性:先进先出。
栈的表示和操作实现
栈本身也是一种线性表,因此也可以分为顺序存储跟链式存储,分别是顺序栈和链栈
顺序栈的表示和实现
顺序栈在内存中的存储方式与线性表一样,因此我们利用一组地址连续的存储单元来依次存放栈底到栈顶的元素,栈底一般在低地址端。另外设置top指针指向栈顶,base指针指向栈底。(但是方便操作top一般指向真正的栈顶元素之上的下标地址)
这是一个长度为4的栈存储结构,我们使用top指向栈顶base指向栈底,此时栈顶与栈底指向的地址相同,表示栈中无元素即为空栈。此时要让a元素入栈,进行的操作如下,a元素进入栈然后top指针上移1个位置,进行++top。使得ABCD元素依次入栈如下图
观察到栈已满,此时top-base==max(这是栈满的标志)
对于栈满系统的处理方法一般是报错,不过还可以申请一个更大的空间把数据拷贝过去,从而形成一个新的栈。
我们上边使用数组来作为顺序栈的存储方式,简单方便但是容易产生溢出。因为我们的数组大小是固定的。栈满还要入栈此时空间不足产生上溢,栈空但是还要出栈元素此时下溢。在实际应用中我们一般把上溢作为一种错误,下溢作为结束条件。
因此我们定义顺序栈的时候需要定义三个元素的结构体就可以,包含一个指向栈顶的top指针和指向栈底的base指针还有栈的最大可用容量stacksize
typedef int elemtype;
typedef struct{elemtype *top;elemtype *base;int stacksize;
}sqstack;
有如下这样一个栈,将AB入栈
此时top指向下标为2的地址,base指向下标为0的地址。显然的top-base=2,这个2就是栈中元素的个数。但是再次将C D入栈,那么top会上移指向下标为4的地址,那么top-base=4就是栈中的元素个数5
顺序栈的初始化
typedef int elemtype;
typedef void status;
#define ERROR 0
#define OK 1
#define MAXSIZE 100
#include <stdio.h>
#include <stdlib.h>
typedef struct{elemtype *top;elemtype *base;int stacksize;
}sqstack;status lnitstack(sqstack *s){s->base=(elemtype*)malloc(sizeof(elemtype));//给栈底指针申请空间s->top=s->base;//令栈底跟栈顶指向的地址相同s->stacksize=MAXSIZE; return OK;
}
这里的结构体定义后,在后面函数定义传参的时候我们传入进去的是栈s的地址,因此形参列表是sqstack *s类型,在函数中要时引用成员top和base使用->的指向运算符,否则将编译失败。
判断栈是否为空
当栈底指针的指向与栈顶指针的指向相同时,栈为空;
int stackelemtype (sqstack s){if(s.top==s.base)return OK;elsereturn ERROR;
}
求一个顺序栈的长度
建立一个栈,依次将ABC入栈,此时栈的长度为3.由此栈的长度计算为 top-base
int stacklength(sqstack s){return s.top-s.base;
}
清空顺序栈
不管里面存了什么,只要将top指针指向栈底就是空,如果这个栈存在,S.base,有这样一个地址,那我们就 将top指针移下来,移动到和base指针的值相等
int clearstack(sqstack s){if(s.base)s.top=s.base;return OK;
} //清空顺序栈
销毁栈
销毁栈不同于清空栈,销毁是在内存中不存在这样的栈了,栈的结构体定义里有栈顶指针top栈底指针base,还有栈的最大长度,销毁操作要使得最大长度置为0,top和base置为NULL
int destroystack(sqstack*s){if(s->base){free(s->base);s->stacksize=0;s->top=s->base=NULL;}return OK;
}
入栈
栈在进行入栈操作的时候首先我们要先判断栈是否满,若满则不能入栈。栈顶指针指向的是栈顶元素的下一个地方,我们先把要入栈的元素放到栈顶指针指向的地方,然后栈顶指针++
int push(sqstack *s,int e){if (s->top==s->base)return ERROR;*(s->top)=e;s->top++;return OK;
}