目录
一、栈的定义和特点
1、栈的定义:栈是一个特殊的线性表,是限定仅在一端,通常为表尾进行插入和删除的操作。又称为后进先出的线性表,简称LIFO结构。
2、栈的相关概念:栈仅在表尾进行插入、删除的操作。
1)表尾an称为栈顶TOP;表头a1称为栈底Base。
2)入栈:插入元素到栈顶(即表尾)的操作,称为入栈。
3)出栈:从栈底(即表尾)删除最后一个元素的操作,称为出栈。
4)逻辑结构:与线性表相同,任为一对一关系。
5)存储结构:用顺序栈或链栈存储均可,但以顺序栈更常见。
6)栈与一般线性表的不同点:运算规则不同,一般线性表为随机存取,栈为后进先出(LIFO)。
二、队列的定义和特点
1)定义:只能在表的一段进行插入运算,在表的另一端进行删除运算的线性表(头插尾删)。
2)逻辑结构:与同线性表相同:任为一对一关系。
3)存储结构:顺序队或链队,以循环顺序队列更常见。
4)运算规则:只能在队首和队尾运算,且访问结点时依照先进先出(FIFO)的原则。
5)实现方式:关键是掌握入队和出队操作,具体实现依顺序队或链队的不同而不同。
三、栈的抽象数据类型的类型定义
1、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端为栈底。
基本操作:初始化,进栈,出栈,取栈顶元素等
}ADT Stack
2、基本操作:
InitStack(&S):初始化操作,构造一个空栈S。
DestoryStack(&):销毁栈操作。
StackEmpty(S):判断S是否为空栈。
StackLength(S):求栈的长度。
GetTop(S,&e):取栈顶元素,用e返回S的栈顶元素。
ClearStack(&S)栈置空操作,将S清为空栈。
Push(&S,e):入栈操作。
四、顺序栈的表示和实现
1、存储方式:
1)存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
2)附设top指针,指示栈顶元素在顺序栈中的位置。
3)另设base指针,指示栈底元素在顺序栈中的位置。
4)但是为了方便操作,通常top指针指示真正的栈顶元素之上的下标地址。
5)另外,用stacksize表示栈可使用的最大容量。
2、栈满时的处理方法:
1)报错,返回操作系统。
2)分配更大的空间,作为栈的存储空间,将原栈移入新栈。
3、使用数组作为顺序栈存储方式的特点
1)简单,方便,但容易溢出(数组大小固定)。
2)上溢:栈已经满,又要压入元素。
3)下溢:栈已经空,还要弹出元素。
4、顺序栈的表示:
#define MAXSIZE 100
typedef struct{
SElemType *base; //栈底指针
SElemType *top; //栈顶指针
Int stack size; //栈可用最大容量
}SqStack;
5、顺序栈的初始化:
Status InitStack(SqStack &S){
S.base = new SElemType(MAXSIZE);
//S.base=(SElemType*)malloc(MAXSIZE*sizeo(SElemType));
if(!S.base) exit (OVERFLOW);
S.top=S.base;
S.stacksize = MAXSIZE;
return OK;}
6、得到栈顶元素
void getTop(SqStack S,ElemType &e){
if(S.Top==S.base){
return;
}
e=*(S.Top-1);
}
7、判断顺序栈是否为空
Status StackEmpty(SqStack S){
if(S.Top==S.base)
return TRUE;
else
return FALSE;
}
8、求顺序栈的长度
int StackLength(Sqstack S)
{
return S.Top-S.base;
}
9、清空顺序栈
Status ClearStack(SqStack S){
if(S.base)
S.Top=S.base;
return OK;
}
10、销毁顺序栈
Status DestroyStack(SqStack &S){
if(S.base){
delete S.base;
S.stacksize=0;
S.base=S.top=NULL;
}
return OK;
}
11、顺序栈的入栈
void push(SqStack &S,ElemType e){
if(S.top-S.base=S.size){
S.base=(ElemType*)realloc(S.base,(S.size+STACK_INCERMENT)*sizeof(ElemType));
if(!S.base){
exit(-1);
}
S.top=S.base+S.size;
S.size+=STACK_INCERMENT;
}
*(S.top++)=e;
}
12、顺序栈的出栈
void pop(SqStack &S,ElemType &e){
if(S.top==S.base){
return;
}
e=*--S.top;
}
五、链栈的表示和实现
注意:1)链表的头指针就是栈顶an
2)不需要头结点
3)基本上不存在栈满的情况
4)空栈相当于头指针指向空
5)插入和删除仅在栈顶出进行
1、链栈的初始化
void InitStack(LinkStack &L){
//构造一个空栈,栈顶指针置为空
S=NULL;
return OK;
}
2、链栈的存储表示
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}*Link;
typedef struct{
Link top;
}LinkStack;
3、算法:构建一个空栈、判空、读取栈顶元素
Status InitStack(LinkStack &S){
S.top=null;
return OK;
}
Status StackEmpty(LinkStack S){
return S.top=null;
}
4、获取栈顶元素
Status GetTop(LinkStack S,SElemType &e){
if(StackEmpty(S)) return ERROR;
e=S.top->data;
return OK;
}
5、链栈的进栈
Status Push(LinkStack &S,SElemType e){
q=(Link)malloc(sizeof(StackNode));
if(!q) exit (OVERFLOW);
q->data=e;
q->next=S.top;
S.top=q;
return OK;
}
6、链栈的出栈
Status Pop(LinkStack &S,SElemType &e){
q=S.top;
if(StackEmpty(S))return ERROR;
e=q->data;
S.top=q->next;
free(q);
return OK;
}
7、总结
1)后入栈的元素总是先出栈。
2)无论顺序表还是链栈,前述算法的T(n)=O(1)。
六、队列的表示和操作的实现
1、相关术语
1)队列(queue)是仅在表尾进行插入操作,在表头进行删除操作的线性表。
2)表尾即an端,称为队尾;表头即a1端,称为队头。
3)它是一种先进先出(fifo)的线性表。
4)插入元素称为入队,删除元素称为出队。
5)队列的存储结构为链队或顺序队。(常用循环顺序队)
2、队列的相关概念
1)定义:只在表的一端进行插入运算,在表的另一端进行删除运算的线性表(头删尾插)。
2)逻辑结构:与同线性表相同,仍为一对一关系。
3)存储结构:顺序队或链队,以循环顺序队列更常见。
4)运算规则:只能在队首和队尾运算,且访问结点时依照先进先出(fifo)的原则。
5)实现方式:关键是掌握入队和出队的操作,
3、队列的抽象数据类型定义
ADT Queue{
数据对象:D={ai|ai属于ElemSet,i=1,2....,n,n>=0}
数据关系:R={<ai-1,ai>|ai-1,ai属于D,i=1,2....,n). a1端为队头,an端为队尾。
基本操作:InitQueue(&Q) 操作结果:队列Q被销毁。
DestroyQueue(&Q) 操作结果:队列Q被销毁。
ClearQueue(&Q) 操作结果:将Q清空。
QueueLength(Q) 操作结果:返回Q的元素个数。
GetHead(Q,&e) 操作结果:用e返回Q的队头元素。
EnQueue(&Q,&e) 操作结果:插入元素为e的元素。
DeQueue(&Q,&e) 操作结果:删除Q的队头元素。
}ADT Queue
七、队列的链接存储——链式队列
1、队头在链头,队尾在链尾,设置队头,队尾指针保存队头、队尾地址。
2、链队列表示
1)结点类型:
typedef struct QNode{
QElemtype data;
struct QNode *next;
}QNode,*QueuePtr;
2)链队列类型
Typedef struct{
QueuePtr front;
QueuePtr rear;
}LinkQueue;
3)定义一链队列:LinkQueue Q;
队头结点:Q.front->next;
队尾结点:Q.rear;
队尾结点数据:Q.rear->data;
3、链队列入队
QueuePtr EnQueue(LinkQueue Q,QElemtype x){
P=(QueuePtr)malloc(sizeof(QNode));
if(!p) exit(overflow);
p->data=x;
p->next=NULL;
Q.rear->next=p;
Q.rear=p;
return Q;
}
4、链队列出队
QueuePtr DeQueue(LinkQueue Q,QElemtype &e){
if(Q.front==Q.rear)
return error;
p=Q.front->next;e=p->next;
Q.front->next=P->next;
if(Q.rear==p)
Q.rear=Q.front;
free(p);
return Q;
}
八、队列的顺序存储——循环队列
1、定义
#define SIZE 100
typedef int Elemtype;
typedef struct{
Elemtype *base; //连续空间的起始地址
int front; //队头元素
int rear; //队尾下标,为队尾元素的下一位置
}SqQueue;
SqQueue Q{
Q.base[Q.front]; //队头元素
Q.base[Q.rear-1]; //队尾元素
Q.rear; //入队元素的下标
}
2、注意
1)经过多次入队,出队,可能出现rear=SIZE
2)此时,队列中可能存在未用单元
3)SIZE-1的下一位置为0
4)属于队列的元素,从front出发,沿着下标递增方向,直至达到rear
5)rear指向的不是队尾,而是队尾的下一元素(故最多只能存放SIZE-1个元素)
3、区分空和满
1)为队列附设一个专门区分空或满的标志
2)约定
·空:front=rear
·满:front在rear的下一位置
4、循环队列
1)存储队列的数组被当作首尾相接长度的表处理
2)队头、队尾指针加1时从MaxSize-1直接进到0,可用c语言的取模(余数)运算实现
3)·出队:队头指针加1
front=(front+1)%MaxSize
·入队:队尾指针加1
rear=(rear+1)%MaxSize
·队列初始化:
front=rear=0
4)·队空条件:front=rear
`队满条件:(rear+1)%MaxSize=front
5、算法实现——初始化
void Init(SqQueue &Q){
Q.base=(Elemtype*)malloc(SIZE*sizeof(Elemtype));
if(!Q.base)
exit(-1);
Q.front=Q.rear=0;
}
6、得到队列长度
void getlength(SqQueue Q,int &length){
if(Q.front<=Q.rear){
length=Q.rear-Q.front;}
else
length=SIZE-(Q.front-Q.rear);
}
7、将e入队
void enter(SqQueue &Q,Elemtype e){
int len;
getlength(Q,len);
if(len==SIZE-1)
return -1;
Q.base[Q.rear]=e; //Q.rear代表下标
if(Q.rear<=SIZE-1){
Q.rear++;
}
else
Q.rear=0;
}
8、将e出队
void remove(SqQueue &Q,Elemtype &e){
if(Q.front=Q.rear)
return;
e=Q.base[Q.front];
if(Q.front<SIZE-1){
Q.front++;
}
else
Q.front=0;
}
9、总结
1)先入队的元素总是先出队。
2)无论是循环队列还是链队列,前述各算法的T(n)=O(1)。