栈
特点:仅在表的一端进行插入和删除的线性表。允许插入、删除的一端是栈顶,另一端是栈底。后进先出。
顺序栈
定义
#define MAXSSIZE 100
typedef struct{
datatype data[MAXSSIZE];
int top; /*栈顶指针*/
}SeqStack;
基本操作
1. 初始化
SeqStack *Init_SStack(){
SeqStack *s; /*定义一个指向顺序栈的指针*/
s = malloc(sizeof(SeqStack));
if(s) s->top = -1; /*空栈标志*/
return s;
}
2. 判空栈
int Empty_SStack(SeqStack *s){ /*判断top值是否与空栈标志相等*/
if(s->top == -1) return 1;
else return 0;
}
3. 入栈
int Push_SStack(SeqStack *s, datatype e){
if(s->top == MAXSSIZE-1) return 0;
else{
s->top++;
s->data[s->top]=e;
return 1;
}
}
4. 出栈
int Pop_SStack(SeqStack *s, datatype *e){
if(Empty_SStack(s)) return 0;
else{
*e = s->data[s->top];
s->top--;
return 1;
}
}
两栈共享空间
使用一个数组来存储这两个栈,其中栈1的栈底设在该数组的始端,栈2的栈底设在该数组的尾端,两个栈都从各自的端点向数组中部延伸,只有在两个栈的栈顶在数组空间的某一位置相遇时才会产生“上溢”。
栈1在入栈操作时栈顶指针top1++,出栈操作时top1–;栈2在入栈操作时栈顶指针top2–,出栈操作时top2++。
链栈
定义
typedef struct node{
datatype data;
struct node *next;
}StackNode, *LinkStack;
LinkStack top ; /*top为栈顶指针*/
链栈指针指向栈底方向
基本操作
1. 初始化
LinkStack Init_LinkStack(){
return NULL;
}
2. 判空栈
int Empty_LinkStack(LinkStack top){
if(top === NULL) return 1;
else return 0;
}
3. 入栈
LinkStack Push_LinkStack(LinkStack top, datatype e){
StackNode *s;
s = malloc(sizeof(StackNode));
s->data = e;
s->next = top;
top = s;
return top;
}
4. 出栈
LinkStack Pop_LinkStack(LinkStack top, datatype *e){
StackNode *p;
if(top == NULL) return NULL;
else{
*e = top->data;
p = top;
top = top->next;
free (p);
return top;
}
}
栈的应用
1. 递归
当某个问题面对不同的数据规模,其解决方案类同,可考虑采用递归定义。
递归函数执行时,都遵循“后调用先返回”的原则。为了保证递归函数能正确执行,系统会设立一个“递归工作栈”,将这些活动记录保存在系统的“递归工作栈”中,每递归调用一次,就要在栈顶为其建立一个新的活动记录,一旦本次调用结束,则将当前栈顶活动记录出栈,根据获得的返回地址信息返回到本次的调用处(断点)继续执行后续程序。
/*求n的阶乘n!*/
int fac(int n){
if(n == 0) return 1;
else return(n * fac(n-1));
}
2. 数制转换
所转换的8进制数是按低位到高位的顺序来产生,而数据输出时都是从高位到低位,正好与计算过程相反,具有“后进先出”的特性,所以可以将转换过程中得到的每一位8进制数都按序进栈保存,转换完毕后再依次出栈就能得到所要的转换结果。
3. 括号匹配
队列
特点:插入操作在表的一端进行,删除操作在表的另一端进行。队尾插入 ,队头删除。先进先出
顺序队
定义
define MAXQSIZE 100
typedef struct{
datatype data[MAXQSIZE];
int front , rear;
}SeqQue;
SeqQue *sq; /*定义指向队列的指针变量*/
- 队列的数据区为:sq->data[0]……sq->data[MAXQSIZE -1]
- 队头指针:sq->front
- 队尾指针:sq->rear
- 置空队:sq->front=sq->rear=-1;
- 入队:sq->rear++; sq->data[sq->rear]=a;
- 出队指令:sq->front++; a=sq->data[sq->front];
- 队中元素个数:n=(sq->rear)-(sq->front);
- 队列的移动有什么特点:整个队列向数组下标较大的方向移动→单向移动性→假溢出→循环队列(头尾相接的循环结构): 用"%"(取余)操作来实现。front=rear表示队满或队空情况。
sq->rear = (sq->rear+1) % MAXQSIZE; /*入队*/
sq->front = (sq->front+1) % MAXQSIZE; /*出队*/
链队
特点:一般用单链表结构来实现链队。 为单链表分别设置一个头指针和尾指针。
定义
typedef struct node{
datatype data;
struct node *next; /*指针指向队尾*/
} QNode;
typedef struct{
QNode *front,*rear;
}LQueue;
基本操作
1. 创建
LQueue *Init_LQue(){
LQueue *q; /*定义一个指向链队的指针*/
QNode *p; /*定义一个头结点*/
q = malloc(sizeof(LQueue));
p = malloc(sizeof(QNode));
p->next = NULL; /*这是一个带头结点的空队列*/
q->front = q->rear=p; /*指针和队列的两种存在,p及队列是蛋糕,q是上面的奶油*/
return q; /*得到创建的队列的指针*/
}
2. 入队
void In_LQue(LQueue *q , datatype e)
{ QNode *p;
p=malloc(sizeof(QNode));
p->data=e;
p->next=NULL;
q->rear->next=p;
q->rear=p;
}
3. 出队
int Out_LQue(LQueue*q, datatype *e){
QNode *p;
if(Empty_LQue(q)){
printf(“队空”);
return 0; /*队空,出队失败*/
}
else{
p = q->front->next; /*指针指向队尾*/
q->front->next = p->next;
*e = p->data; /*队头元素放e中*/
free(p);
if (q->front->next == NULL) /*只剩头结点*/
q->rear = q->front;
return 1;
}
}
队列的应用
1. 键盘输入缓冲区的问题
在多任务系统中,当程序正在执行其它任务时,用户还可以从键盘上不断键入所要输入的内容。用户键入的内容不会在屏幕上立刻显示出来,直到当前正在工作的那个进程结束为止。
队列的特性可以保证输入字符先键入、先保存、先处理的要求,而循环队列结构又能有效地限制缓冲区的大小,并可避免假溢出问题。所以键盘输入缓冲区一般都采用循环队列这种数据结构。
2. 杨辉三角问题
因为上一行用来计算过的数据以后就不需要了,为了节省存储空间,可以利用循环顺序队列打印杨辉三角。