1. 程式人生 > >順序棧的表示和實現

順序棧的表示和實現

參考書目:《資料結構(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檔案):