一、实验目的
1、掌握栈的结构特性及其入栈、出栈操作。
2、掌握队列的结构特性及其入队、出队的操作,掌握循环队列的特点及其操作。
二、实验预习
说明以下概念
1、顺序栈:
顺序结构存储的栈,使用一段地址连续的内存单元依次存储从栈底到栈顶的所有数据元素,设置指针 base 指向栈底元素,设置指针 top 指向栈顶元素。
2、链栈:
链式存储的栈,使用一段地址连续或者不连续的内存单元存放栈中所有数据,并设置栈顶指针 top 指向栈顶元素所在的结点。
3、循环队列:
顺序结构存储的栈,使用一段连续的地址单元存放队中数据元素。设置两个int型变量 front 和 rear 分别指示队头和队尾,随着入队出队操作front和rear改变。
为防止假溢出,将队列变为一个逻辑上的环形存储空间,即头尾相接的循环。当队列后面满了,就从头开始继续存储。
4、链队
链式结构存储的队列,设置一个头指针 front 和尾指针 rear 分别指向头结点和队尾元素所在结点。
三、实验内容和要求
1、阅读下面程序,将函数Push和函数Pop补充完整。要求输入元素序列1 2 3 4 5 e,运行结果如下所示。
#include<stdio.h>
#include<malloc.h>
#define ERROR 0
#define OK 1
#define STACK_INT_SIZE 10 /*存储空间初始分配量*/
#define STACKINCREMENT 5 /*存储空间分配增量*/
typedef int ElemType; /*定义元素的类型*/
typedef struct{
ElemType *base;
ElemType *top;
int stacksize; /*当前已分配的存储空间*/
}SqStack;
int InitStack(SqStack *S); /*构造空栈*/
int push(SqStack *S,ElemType e); /*入栈*/
int Pop(SqStack *S,ElemType *e); /*出栈*/
int CreateStack(SqStack *S); /*创建栈*/
void PrintStack(SqStack *S); /*出栈并输出栈中元素*/
int InitStack(SqStack *S){
S->base=(ElemType *)malloc(STACK_INT_SIZE *sizeof(ElemType));
if(!S->base) return ERROR;
S->top=S->base;
S->stacksize=STACK_INT_SIZE;
return OK;
}/*InitStack*/
int Push(SqStack *S,ElemType e){
//判断栈满
if(S->top - S->base >= S->stacksize){
S->base = (ElemType *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(ElemType));
if(NULL == S->base) //分配失败
return ERROR;
S->top = S->base + S->stacksize;
S->stacksize = S->stacksize+STACKINCREMENT;
}
*S->top = e; //栈顶元素设置为e,top+1
S->top++;
return OK;
}/*Push*/
int Pop(SqStack *S,ElemType *e){
//判断栈空
if(S->top == S->base)
return ERROR;
S->top--; //top指向栈顶元素后一个位置,应先-1指向栈顶元素
*e=*S->top;
return OK;
}/*Pop*/
int CreateStack(SqStack *S){
int e;
if(InitStack(S))
printf("Init Success!\n");
else{
printf("Init Fail!\n");
return ERROR;
}
printf("input data:(Terminated by inputing a character)\n");
while(scanf("%d",&e))
Push(S,e);
return OK;
}/*CreateStack*/
void PrintStack(SqStack *S){
ElemType e;
while(Pop(S,&e))
printf("%3d",e);
}/*Pop_and_Print*/
int main(){
SqStack ss;
printf("\n1-createStack\n");
CreateStack(&ss);
printf("\n2-Pop&Print\n");
PrintStack(&ss);
return 0;
}
算法分析:输入元素序列1 2 3 4 5,为什么输出序列为5 4 3 2 1?体现了栈的什么特性?
后进先出的特点(LIFO),因为栈只能在栈顶进行插入和删除操作。
2、在第1题的程序中,编写一个十进制转换为二进制的数制转换算法函数(利用栈实现),并验证其正确性。
提示:十进制转二进制:除2取余,逆序输出。
十进制转二进制算法代码:
//n为待转换的十进制数
int dtob(int n){ /*Decimal to Binary*/
//初始化一个栈保存余数(二进制各位)
SqStack s;
int r;
ElemType e;
//转换
if( !InitStack(&s) ) //初始化失败
return ERROR;
while(n){
r = n % 2;
Push(&s,r);
n=n/2;
}
//输出
while(Pop(&s,&e)){
printf("%d",e);
}
return OK;
}/*dtob*/
主函数补充代码:
int main(){
int n;
printf("\n1-输入十进制数:\n");
scanf("%d",&n);
if( !dtob(n) ){ //转换失败
printf("\n转换失败!\n");
}
else{
printf("\n2-转换为二进制数:\n");
dtob(n);
}
return 0;
}
运行结果:
3、阅读并运行以下程序,分析程序功能。
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#define M 20
typedef char elemtype;
typedef struct{
elemtype stack[M];
int top;
}stacknode;
void init(stacknode *st);
void push(stacknode *st,elemtype x);
void pop(stacknode *st);
void init(stacknode *st)
{
st->top=0;
}
void push(stacknode *st,elemtype x)
{
if(st->top==M)
printf("the stack is overflow!\n"); //栈满
else
{
st->top=st->top+1; //top为栈顶元素下标,从stack[1]开始存储~stack[M]
st->stack[st->top]=x;
}
}
void pop(stacknode *st)
{
if(st->top>0)
st->top--;
else
printf("Stack is Empty!\n");
}
int main()
{
char s[M];
int i;
stacknode *sp;
printf("create a empty stack!\n");
sp=(stacknode *)malloc(sizeof(stacknode));
init(sp);
printf("input a expression:\n");
gets(s);
for(i=0;i<strlen(s);i++)
{
if(s[i]=='(')
push(sp,s[i]);
if(s[i]==')')
pop(sp);
}
if(sp->top==0)
printf("'('match')'!\n"); //栈空也会打印,有缺陷
else
printf("'('not match')'!\n");
return 0;
}
输入:2+((c-d)*6-(f-7)*a)/6
运行结果:
输入:a-((c-d)*6-(s/3-x)/2
运行结果:
程序的基本功能:判断输入的表达式中的括号是否匹配正确。
4、假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾结点(不设队头指针),试编写相应的置空队列、入队列、出队列的算法。
提示:因为是循环列表,所以rear->next指向头结点
定义循环链队:
typedef int ElemType; //定义元素类型
typedef struct QNode{ //定义链表结点
ElemType data;
struct QNode *next;
}QNode,*QNodeP;
typedef struct{
QNodeP rear; //只带一个尾指针rear的循环链队
}CLQ; //circular link queue
初始化算法代码:
int InitCLQ(CLQ *q){
QNode *head;
head = (QNodeP)malloc(sizeof(QNode));
if(!head)
return ERROR;
head->next = head;
q->rear = head;
return OK;
}/*InitCLQ*/
入队算法代码:
int insertCLQ(CLQ *q,ElemType e){
//从队尾插入
QNode *s; //s为新增结点
s = (QNodeP)malloc(sizeof(QNode));
if(!s)
return ERROR; //分配失败
s->data = e;
s->next = q->rear->next; //头节点
q->rear->next = s;
q->rear = s;
return OK;
}/*insertCLQ*/
出队算法代码:
int DeleteCLQ(CLQ *q,ElemType *e){
//从队头删除
//判断队空
if(q->rear->next == q->rear)
return ERROR;
QNode *p;
p=q->rear->next->next; //p指向要删除的结点
*e = p->data;
q->rear->next->next = p->next;
//特殊情况:队中只剩一个元素(p = rear)
if(p == q->rear)
q->rear = q->rear->next;
free(p);
return OK;
}/*DeleteCLQ*/