这个简单程序的编写过程可谓不简单,一点小bug卡了将近10多天,今天灵感一来,解决了。
书上只有完整函数声明,但没有完整函数代码,于是我为了练习并且加深印象,就打算将所有函数实现一遍,于是就有了这个程序。在完成过程中有借鉴,但是有些借鉴并不正确,编译出来还是有错,我就自己再看书学习研究,根据自己的理解修改,最后费尽心思还是完成了这个练习程序代码,在Code::Blocks中顺利编译运行,真的是长舒了一口气,有些时候,需要缓缓,不要死磕,不然会很痛苦,放放再换个思路,可能就正确了。
在写代码过程中,收获最大的就是,对顺序栈的数据结构和基本操作函数理解更深了,也更清楚了,但还是差火候,需要经常巩固训练,不然会忘记,还没能达到刻在脑子里的熟练度。
要深深记住和理解&取地址符用的时机,啥时候该用啥时候不该用不需要用。
/**
此函数用来练习栈的顺序存储数据结构和基本操作函数实现
栈有栈底和栈顶base和top
Author:Cyan
Date:2018/10/10
**/
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 100//存储空间初始分配量
#define STACKINCREMENT 10//存储空间分配增量
typedef int sElemType;
typedef int Status;
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -1
//栈的ADT类型说明定义表示
typedef struct {
sElemType *base;
sElemType *top;
sElemType stacksize;
}sqStack ;
/*以下为栈的基本操作函数原型声明*/
//构造一个空栈
Status initStack(sqStack *);
//销毁栈S,S不再存在
Status destoryStack(sqStack *);
//把S置为空栈
Status clearStack(sqStack *);
//若栈为空栈,则返回TRUE,否则返回FALSE
Status emptyStack(sqStack );
//返回S的长度
sElemType stackLength(sqStack );
//若栈不空,则用e返回S栈顶的元素,并返回OK;否则返回ERROR
Status getTop(sqStack *, sElemType *);
//插入元素e为新的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Push(sqStack *, sElemType );
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
Status Pop(sqStack *, sElemType *);
//从栈底到栈顶依次对栈中每个元素调用函数visit()。一旦visit()失败,则操作失败
Status stackTraverse(sqStack ,Status(*visit)());
//VISIT函数
Status visit(sElemType);
int main(void){
sElemType i, j, k;
Status a, b, c;
sqStack S;
printf("下面我们来初始化一个空栈吧!嘿嘿嘿\n");
a = initStack(&S);
if(a)
printf("我们成功啦\n");
b = emptyStack(S);
if(!b)
printf("是空栈没错\n\n");
printf("下面准备输入\n");
printf("打算输几个?\n");
scanf("%d", &i);
printf("好的,请输入%d个数\n",i);
for(j = 0; j < i; j++){
sElemType e;
scanf("%d", &e);
printf("你输入的第%d个数是%d\n", j + 1, e);
c = Push(&S,e);
if(c)
printf("压栈成功\n");
}
j = stackLength(S);
printf("此栈的长度为%d\n", j);
printf("下面来测试stackTraverse()函数\n");
stackTraverse(S,visit);
sElemType e;
printf("下面来测试getTop()函数\n");
getTop(&S, &e);
printf("栈顶的值为%d\n", e);
printf("下面来测试Pop()函数\n");
Pop(&S, &e);
printf("删除的栈顶的值为%d\n", e);
printf("下面我们来清空栈\n");
Status s = clearStack(&S);
if(s)
printf("栈已成功清空\n");
printf("下面我们来销毁栈\n");
s = destoryStack(&S);
if(s)
printf("销毁成功\n");
return 0;
}
/*以下是基本操作的算法描述*/
//构造一个空栈
Status initStack(sqStack *S){
S->base = (sElemType*)malloc(STACK_INIT_SIZE * sizeof(sElemType));
if(!S->base)
exit (ERROR);
S->top = S->base;
S->stacksize = STACK_INIT_SIZE;
return OK;
}
//销毁栈
Status destoryStack(sqStack *S){
if(S->top == S->base)
printf("是空栈,接下来继续销毁\n");
S->top = NULL;
S->stacksize = 0;
free(S->base);
return OK;
}
/*1->顺序栈的清空和销毁区别在哪?具体操作过程是怎样的?
2->清空栈只需修改栈顶指针吗?还需不需要将栈的大小置为0?
5->销毁栈是通过移动top指针依次释放每个节点,还是直接将栈顶指针和栈底
*/
//把S置为空栈
Status clearStack(sqStack *S){
S->top = S->base;
return OK;
}
//若栈为空栈,则返回TRUE,否则返回FALSE
Status emptyStack(sqStack S){
if(S.base = S.top)
return ERROR;
else return TRUE;
}
//返回S的长度
sElemType stackLength(sqStack S){
if((emptyStack(S)))
exit(ERROR);
return S.top - S.base;
}
//若栈不空,则用e返回S栈顶的元素,并返回OK;否则返回ERROR
Status getTop(sqStack *S, sElemType *e){
if(S->top - S->base == 0)
return ERROR;
*e = *(S->top - 1);
return OK ;
}
//插入元素e为新的栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Push(sqStack *S, sElemType e){
if(S->top - S->base >= STACK_INIT_SIZE){
S->base = (sElemType *)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(sElemType));//realloc函数的使用
if(!S->base)
return OVERFLOW;
S->top = S->base + STACK_INIT_SIZE;
S->stacksize = STACK_INIT_SIZE + STACKINCREMENT;
}
*S->top++ = e;
printf("%d", e);
printf("这数为%d\n", *(S->top - 1));
return OK;
}
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
Status Pop(sqStack *S, sElemType *e){
if(S->top-S->base==0)
return ERROR;
*e = *(S->top - 1);//疑惑:弹栈时,是指针先减一还是先赋值给e
S->top --;
return OK;
}
//从栈底到栈顶依次对栈中每个元素调用函数visit()。一旦visit()失败,则操作失败
Status stackTraverse(sqStack S,Status(* visit)(sElemType)){// 函数指针?
int *p, *n;
int i;
n = S.top;
for( i = 0,p = S.base ; p < n ; p++, i++){
Status s = visit(S.base[i]);
if(!s)
return ERROR;
}
printf("\n");
return OK;
}
Status visit(sElemType e){
printf("%d ", e);
return OK;
}