順序棧的表示和實現
參考書目:《資料結構(C語言版)》,嚴蔚敏
/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
思考:對於棧和棧的Pop()函式的理解。
我原本以為Pop()函式就是一個刪除棧頂元素的函式,但如果這樣的話,那麼他和線性表中的delete函式又有啥區別呢?
思考之後我覺得應這樣理解:棧可以看成是一個後進先出的buffer。資料進棧後,待棧中元素滿足程式要求時,便要進行出棧操作,這時就要呼叫Pop()函式,當棧中所有的元素都出棧後,此時棧就空了,等待下次資料的到來,這有點類似微控制器中具有LIFO(last in first out)結構的buffer。
(完全是自己悟的,也不知道理解的對不對,還請各位指正)
當然棧也可以當做線性表來用,畢竟棧就是由線性表而來的,但我感覺實際應用沒有必要這樣。因為既然重新定義棧這樣的資料結構,在實際應用中棧的這種操作必然有其用武之地。
注意:鑑於以上分析,程式中呼叫的StackDisp()函式並未按照後進先出這一規定顯示stack的,當然完全可以將其改為按後進先出的規定來顯示stack的,不過我估計在實際應用中StackDisp()函式未必排得上用場。
/~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
common.h
#ifndef _COMMON_H_
#define _COMMON_H_
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
//Status Equal(int a, int b);
#endif
stack.h
#ifndef _STACK_H_ #define _STACK_H_ #define STACK_INIT_SIZE 10 #define STACKINCREMENT 10 typedef int SElemType; typedef struct { SElemType *base; //在棧構造之前和銷燬之後,base的值為NULL SElemType *top; //棧頂指標 int stacksize; //當前已分配的儲存空間,以元素為單位 }SqStack; Status InitStack(SqStack *S); //構造一個空棧S Status StackScanf(SqStack *S); //向指定的stack輸入資料 Status StackDisp(SqStack *S); //顯示指定的stack int StackLength(SqStack *S); //返回指定stack的元素個數,即棧的長度 Status GetTop(SqStack *S,SElemType *e); //若棧不空,則將S的棧頂元素傳遞給*e,並返回OK;否則返回ERROR Status Push(SqStack *S,SElemType e); //插入資料e為新的棧頂元素 Status Pop(SqStack *S,SElemType *e); //若棧不空,則刪除指定棧的棧頂元素,並用e返回其值,並返回OK;否則返回ERROR。 Status DestroyStack(SqStack *S); //銷燬指定棧 Status ClearStack(SqStack *S); //清空指定棧 Status StackEmpty(SqStack *S); //判斷指定棧是否為空,若為空棧,則返回TRUE,否則返回FALSE. #endif
stack.c
#include "stdio.h"
#include "stdlib.h"
#include "common.h"
#include "stack.h"
/*
輸入引數:*S 待處理的棧地址
返回引數:OVERFLOW 儲存分配失敗
OK 成功
函式功能:初始化棧,構造一個空棧。
*/
Status InitStack(SqStack *S)
{
S->base = (SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!S->base)
exit(OVERFLOW); //儲存分配失敗
S->top = S->base; //使棧頂指標等於棧底指標
S->stacksize = STACK_INIT_SIZE; //初始化棧當前可用的最大容量
return OK;
}
/*
輸入引數:*S 待處理的棧地址
返回引數:OK 操作成功
OVERFLOW stack滿
還可以根據實際情況定義相應的返回值
函式功能:向指定的stack輸入資料,約定若輸入0則結束,並將0儲存stack。
注意:在stack的儲存空間夠用的情況下,呼叫該函式得到的棧頂元素都為0,若想棧頂元素不為0可以呼叫Push()函式。
*/
Status StackScanf(SqStack *S)
{
SElemType *cap_max;
cap_max = S->base + S->stacksize - 1; //減1的原因是:留一個儲存單元給棧頂指標,即當stack的儲存單元用完時,使棧頂指標指向儲存空間的最後一個儲存單元。
scanf("%d",S->top);
S->top++;
while(*(S->top-1))
{
scanf("%d",S->top);
S->top++;
if(S->top >= cap_max)
{
printf("The stack is full."); //實際應用時當然還可以在這裡繼續為原stack擴充容量。
return OVERFLOW;
}
}
return OK;
}
/*
輸入引數:*S 待顯示的棧地址
返回引數:OK 操作成功
還可以根據實際情況定義相應的返回值
函式功能:顯示指定的stack
注意:程式中呼叫的StackDisp()函式並未按照後進先出這一規定顯示stack的,當然完全可以將其改為按後進先出的規定來顯示stack的,不過我估計在實際應用中StackDisp()函式未必排得上用場。
*/
Status StackDisp(SqStack *S)
{
SElemType *bottom = S->base;
printf("\n*************** output start ***************\n");
while(bottom<S->top)
{
printf("%d ",*bottom);
bottom++;
}
printf("\n*************** output end ***************\n");
return OK;
}
/*
輸入引數:*S 待處理的棧地址
返回引數:*S所指向的stack的長度,即該stack中的元素個數
函式功能:返回指定stack的元素個數,即棧的長度
*/
int StackLength(SqStack *S)
{
//可根據需要在這裡新增判斷S是否滿足一些條件,如判斷*S指向的stack是否存在等,因為棧是否存在和棧是否空有時還是有區別的。
//if(!(S->top||S->top)) //判斷棧是否存在,這個當然要在銷燬棧時將棧頂指標和棧底指標都清為0才能在此有所判斷。
// return -1;
return(S->top - S->base);
}
/*
輸入引數:*S 待處理的棧地址
*e 用於儲存棧頂元素
返回引數:ERROR 該棧為空
OK 操作成功
函式功能:若棧不空,則將S的棧頂元素傳遞給*e,並返回OK;否則返回ERROR.
*/
Status GetTop(SqStack *S,SElemType *e)
{
if(!StackLength(S))
return ERROR;
else
{
*e = *(S->top - 1);
}
return OK;
}
/*
輸入引數:*S 待處理的棧地址
e 待插入的資料
返回引數:OVERFLOW stack滿
OK 操作成功
函式功能:插入資料e為新的棧頂元素
*/
Status Push(SqStack *S,SElemType e)
{
if(StackLength(S) >= S->stacksize)
{
S->base = (SElemType *)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(SElemType));
if(!S->base)
exit(OVERFLOW);
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
return OK;
}
/*
輸入引數:*S 待處理的棧地址
*e 待返回的棧頂元素
返回引數:ERROR 操作失敗
OK 操作成功
函式功能:若棧不空,則刪除指定棧的棧頂元素,並用e返回其值,並返回OK;否則返回ERROR。
*/
Status Pop(SqStack *S,SElemType *e)
{
if(StackLength(S) <= 0)
return ERROR;
*e = * --S->top;
return OK;
}
/*
輸入引數:*S 待銷燬的棧地址
返回引數:OK 操作成功
還可根據需要加入別的返回值
函式功能:銷燬指定棧
*/
Status DestroyStack(SqStack *S)
{
free(S->base);
S->base = NULL;
S->top = NULL;
S->stacksize = 0;
return OK;
}
/*
輸入引數:待清空的棧地址
返回引數:OK 操作成功
函式功能:清空指定棧
*/
Status ClearStack(SqStack *S)
{
S->top = S->base;
return OK;
}
/*
輸入引數:*S 待處理的棧地址
返回引數:FALSE 指定的棧非空
OK 指定的棧為空
函式功能:判斷指定棧是否為空,若為空棧,則返回TRUE,否則返回FALSE.
*/
Status StackEmpty(SqStack *S)
{
//if(!(S->top||S->top)) //判斷棧是否存在,這個當然要在銷燬棧時將棧頂指標和棧底指標都清為0才能在此有所判斷。
// return -1;
if(S->top - S->base)
{return FALSE;}
return TRUE;
}
main.c
//>>>第3章/3.1節
#include "stdio.h"
#include "stdlib.h"
#include "common.h" //這個標頭檔案一定要包含在stack.h前,因為在標頭檔案stack.h中會用到common.h標頭檔案中的定義。
#include "stack.h"
int main(void)
{
int top_elem=0;
SqStack stack;
InitStack(&stack);
StackScanf(&stack);
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
Push(&stack,1010);
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
Pop(&stack,&top_elem);
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
printf("~~~~~~~~~~~~~~~~\n");
printf("%d\n",*stack.top); //之前的棧頂元素還存在,只不過是棧頂指標後退了一位而已。
printf("~~~~~~~~~~~~~~~~\n");
Push(&stack,2020);
Push(&stack,3030);
Push(&stack,4040);
Push(&stack,5050);
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
top_elem = 0;
ClearStack(&stack);
printf("\nAfter clear stack:\n");
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
top_elem = 0;
DestroyStack(&stack);
printf("\nAfter destroy stack:\n");
StackDisp(&stack);
printf("The current stack length is %d.\n",StackLength(&stack));
GetTop(&stack,&top_elem);
printf("The top element is %d.\n",top_elem);
return 0;
}
下面這個是後來除錯所加的,當然可以將兩個main檔案合併。
main.c
//第3章/3.2節
#include "stdio.h"
#include "stdlib.h"
#include "common.h" //這個標頭檔案一定要包含在stack.h前,因為在標頭檔案stack.h中會用到common.h標頭檔案中的定義。
#include "stack.h"
#include "example.h"
int main(void)
{
Conversion(1348,8);
return 0;
}
運算結果如下(第一個main檔案):