1. 程式人生 > >C語言資料結構----棧的定義及實現

C語言資料結構----棧的定義及實現

本節主要說的是資料結構中的棧的基本定義和實現的方式,其中實現的方式採用的是複用順序表和單向連結串列的方式。

一、棧的基本定義

1.棧是一種特殊的線性表,只能從固定的方向進出,而且棧進出的基本原則是:先進棧的元素後出棧。

2.老唐對棧頂棧底的定義:

棧頂:允許操作的一端。

棧底:不允許操作的一端。

二、棧的基本實現方式

1.使用順序儲存的方式實現棧

 

在這種方式下,我們採用順序表的複用方法來實現棧的資料儲存。

2.使用鏈式儲存來實現棧

在這種方式下,我們採用單向連結串列的複用技術來實現鏈棧。

三、普通的順序棧

1.首先定義的順序棧中的資料結點的結構,主要包括兩個部分,一部分是資料元素,另一部分是順序棧的長度。

具體程式碼如下:

typedef struct _tag_stack_
{
	int a[20];
	int top;
}Sqstack;

2.使用順序棧之前要先初始化順序棧。

主要是為順序棧結點分配一個空間,然後將順序棧的長度初始化為0.

Sqstack* InitStack ()
{
	Sqstack *ret = NULL;
	ret = (Sqstack*)malloc (sizeof(Sqstack));
	if (ret)
	{
		/*將棧的長度初始化為0*/ 
		ret -> top = 0;
	}
	return ret;
}

3.將元素壓入棧,這裡採用複用方式。

int Push(Sqstack *stack, int data)
{
	/*這裡有一個複用方式,也就是順序棧的長度和陣列的下標進行復用s*/
	stack -> a[stack -> top] = data;
	stack -> top++;
	return 1;
}

4.將已經在棧中的元素進行列印,因為棧不是隻是一種儲存資料的結構,所以我們不經過彈出棧中的元素也是可以訪問到棧中的元素的。

void Play (Sqstack *stack)
{
	int i = 0;
	if (stack -> top == 0)
	{
		printf ("It is empty\n");
	}
	/*stack -> top,棧的長度*/
	else
	{
		for (i = 0; i < stack -> top; i++)
		{
			printf ("棧中的資料為:%d\n", stack -> a[i]);
		}
	}
}

5.資料結點出棧

int Pop (Sqstack *stack, int *data)
{
	if (stack -> top == 0)
	{
		printf ("the stack is empty\n");
		printf ("彈出已經被改變了的u的值");
	}
	else
	{	
		stack -> top--;
		*data = stack -> a[stack -> top];
	}
	return 1; 	
}

6.測試部分程式碼如下:

int main()
{	
	int h = 4;
	int p = 0;
	int i = 0;
	int u = 3;
	Sqstack* qq;
	
	qq = InitStack();
			
	for (i = 0; i < 5; i++)
	{
		Push (qq, i);
	}
	Play (qq);
	
	/*彈出操作*/
	Pop (qq, &u);
	printf ("彈出的元素是:%d\n",u);
	Pop (qq, &u);
	printf ("彈出的元素是:%d\n",u);
	Pop (qq, &u);
	printf ("彈出的元素是:%d\n",u);
	Pop (qq, &u);
	printf ("彈出的元素是:%d\n",u);
	Pop (qq, &u);
	printf ("彈出的元素是:%d\n",u);
	Pop (qq, &u);
	printf ("%d\n",u);

	return 1;
}


7.雖然順序棧實現了棧的基本功能,畢竟是順序儲存結構,而且佔用的記憶體空間也必須是連續的,所以還是有一定的侷限性的。

四、棧的具體實現程式碼解析

1.順序棧的實現

主要運用的是順序表的複用方法,棧的建立和進棧出棧的過程都是採用的順序表的複用技術。

具體的程式碼如下:

#include <stdio.h>
#include "1.h"
#include "SeqList.h" 

/*******************************************************************************
*函式名: SeqStack_Create
*引數:capacity 棧中元素的個數 
*返回值:SeqStack*型別,是一個void*型別,然後再由接收函式進行強制型別轉換 
*功能:建立順序棧,呼叫順序表建立函式 
*******************************************************************************/ 
SeqStack* SeqStack_Create(int capacity)
{
	return SeqList_Create(capacity);
}

/*******************************************************************************
*函式名: SeqStack_Destroy
*引數:SeqStack* stack 棧指標 
*返回值:void 
*功能:銷燬順序棧,呼叫順序表銷燬函式 
*******************************************************************************/ 
void SeqStack_Destroy(SeqStack* stack)
{
	SeqList_Destroy (stack);
}

/*******************************************************************************
*函式名: SeqStack_Clear
*引數:SeqStack* stack 棧指標 
*返回值:void 
*功能:清空順序棧,呼叫順序表清空函式 
*******************************************************************************/ 
void SeqStack_Clear(SeqStack* stack)
{
	SeqList_Clear (stack); 
}

/*******************************************************************************
*函式名: SeqStack_Push
*引數:SeqStack* stack 棧指標  void* item要進棧的元素 
*返回值:void 
*功能:將一個item元素壓入棧 
*******************************************************************************/ 
int SeqStack_Push(SeqStack* stack, void* item)
{
	return SeqList_Insert(stack, item, SeqList_Length(stack));
}

/*******************************************************************************
*函式名: SeqStack_Pop
*引數:SeqStack* stack 棧指標 
*返回值:void 
*功能:將元素彈出棧 
*******************************************************************************/
void* SeqStack_Pop(SeqStack* stack)
{
	return SeqList_Delete(stack, SeqList_Length(stack) - 1); 
}

/*******************************************************************************
*函式名: SeqStack_Top
*引數:SeqStack* stack 棧指標 
*返回值:void 
*功能:獲取棧頂元素 
*******************************************************************************/
void* SeqStack_Top(SeqStack* stack)
{
	return SeqList_Get(stack, SeqList_Length(stack) - 1);
}

/*******************************************************************************
*函式名: SeqStack_Size
*引數:SeqStack* stack 棧指標 
*返回值:int 返回棧的長度 
*功能:獲取棧的長度 
*******************************************************************************/
int SeqStack_Size(SeqStack* stack)
{
	return SeqList_Length (stack);
}

/*******************************************************************************
*函式名: SeqStack_Capacity
*引數:SeqStack* stack 棧指標 
*返回值:void 
*功能:獲取棧的容量 
*******************************************************************************/
int SeqStack_Capacity(SeqStack* stack)
{
	return SeqList_Capacity(stack);
}

測試部分程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include "1.h"

/*int main(int argc, char *argv[])
{
	int i;
	int a[10];
	int q = 20;
	int temp;
	
	SeqStack* stack = SeqStack_Create (20);
	for (i = 1; i < 10; i++)
	{
		a[i] = i;
		SeqStack_Push (stack, a + i);
	} 
	
	printf ("棧的長度是: %d\n", SeqStack_Size (stack));
	
	/*這裡必須加上強制型別轉換,因為呼叫函式結束以後返回的也是void *型別,所以要轉換*/
	/*printf ("棧頂元素是: %d\n", *(int*)SeqStack_Top (stack));
	
	for (i = 1; i < 10; i++)
	{
		printf ("棧中的元素分別是:%d\n", *(int *)SeqStack_Pop (stack));
	} 
	
	
	temp =  (int) SeqStack_Capacity(stack);
	printf ("棧的容量為:%d\n", temp);
	
	temp = SeqStack_Size(stack); 
	printf ("棧的元素個數為:%d\n", temp);
	
	return 0;
}*/


int main()
{
	int i = 0;
	char a[10];
	char temp;
	
	SeqStack* stack = SeqStack_Create (20);
	for (i = 0; i < 9; i++)
	{
		a[i] = 'a';
		SeqStack_Push (stack, a + i);
	}
	a[9] = 'b';
	SeqStack_Push (stack, a + 9);	
	
	for (i = 0; i < 10; i++)
	{
		temp = *(char*)SeqStack_Pop (stack);
		printf ("%c\n", temp);
	}
	return 0;
}

2.鏈式棧的實現

(1)定義資料結點

資料結點用結構體來封裝,這個結構體中包含了每一個next元素的資訊和進棧元素的地址,雖然我們在建立連結串列的時候已經進行了一個結構體的定義,但是我們的棧成員並不適用於那套連結串列,所以這裡進行重新定義。

結構體定義如下:

typedef struct _tag_LinkStack_
{
	LinkListNode header;
	void *item;
}TLinkStackNode;

(2)銷燬棧的函式

void LinkStack_Destroy(LinkStack* stack)
{
	/*呼叫棧清空函式*/
	LinkStack_Clear (stack);
	/*呼叫連結串列銷燬函式*/
	LinkList_Destroy(stack); 
}

銷燬棧的函式中主要呼叫了棧的清空函式和連結串列的銷燬函式,銷燬棧的的前提首先要銷清空棧中的每一個成員,然後在銷燬棧的頭。

(3)棧的清空函式

void LinkStack_Clear(LinkStack* stack)
{
	while (LinkStack_Size (stack) > 0)
	{
		LinkStack_Pop (stack);
	} 
}

棧的清空函式中和連結串列的清空函式是由區別的,在棧的清空函式中我們主要是對棧中是否還存在元素進行了判定,如果還有元素就對將棧中的元素彈出,而連結串列的清空只是將連結串列頭指向NULL,將連結串列長度置為0.
(4)將元素壓入棧的操作

int LinkStack_Push(LinkStack* stack, void* item)
{
	TLinkStackNode* node = (TLinkStackNode*)malloc (sizeof (TLinkStackNode));

	int ret = (node != NULL) && (item != NULL); 
	
	if (ret)
	{
		node->item = item;
		
		ret = LinkList_Insert (stack, (LinkListNode*)node, 0);
	}

	if (!ret)
	{
		free(node);
	}
	
	return ret; 
}

在將元素壓入棧的過程中,我們首先要為我們即將壓入棧的元素開闢一塊空間,因為是鏈式棧,所以我們的空間不一定非要是連續的,這是採用malloc的方式。

然後進行安全性的檢測,我們要判斷開闢的空間是否成功,然後還要判斷我們要插入的元素的地址是不是空。如果條件都成立那我們進行元素的進棧操作。

元素的進棧操作,我們將要插入的資料結點的地址賦給棧結構體的item,然後呼叫連結串列的插入函式操作,將這個棧的資料結點插入棧中,而且由於我們是把連結串列的頭作為棧頂,所以我們插入棧元素的位置為0.

如果我們的安全性檢測沒有通過,那麼我們就釋放為了插入一個棧元素而釋放的空間。

(5)元素的出棧操作

void* LinkStack_Pop(LinkStack* stack)
{
	TLinkStackNode* node = (TLinkStackNode*)LinkList_Delete(stack, 0);
	void * ret = NULL;
	if (node != NULL)
	{
		ret = node->item;
		free(node);
	} 
	return ret;
}

元素的出棧操作中,首先通過呼叫連結串列的元素刪除函式來刪除我們要彈出棧的元素,因為棧永遠都是從棧頂彈出元素,而我們進棧的方向也是從連結串列的0位置方向進棧的,所以我們只要刪除連結串列中的第0個元素即可(所謂的連結串列第0個元素哈)。

然後我們判定我們要刪除的元素是否為空,如果不為空,那麼我們將返回我們要彈出棧的元素。

在將元素彈出棧以後我們就要釋放為這個連結串列中的元素開闢的空間。而我們的連結串列的操作的空間是在操作具體資料元素的時候才開闢空間,我們不使用連結串列的時候它是不佔用空間的。

(6)獲取棧頂元素操作

void* LinkStack_Top(LinkStack* stack)
{
	TLinkStackNode* node = (TLinkStackNode*)LinkList_Get(stack, 0);
	void *ret = NULL;
	if (node != NULL)
	{
		ret = node->item;
	} 
	return ret;
}

通過LinkList_Get函式可以獲得固定位置的元素,由於我們進棧的時候就是從0位置 所以在我們獲取棧頂元素的時候也是從0位置開始獲取。


五、鏈式棧的實現程式碼

主要附上鍊式表的實現部分程式碼,複用單向連結串列的程式碼這裡就不再附了。

1.鏈式棧的程式碼實現部分

#include <stdio.h>
#include <malloc.h>
#include "1.h"
#include "LinkList.h"

typedef struct _tag_LinkStack_
{
	LinkListNode header;
	void *item;
}TLinkStackNode;

/*******************************************************************************
*函式名: LinkStack_Create
*引數:void
*返回值:LinkStack* 棧的指標 
*功能:建立一個鏈棧,並返回建立成功後的指標 
*******************************************************************************/
LinkStack* LinkStack_Create()
{
	return LinkList_Create(); 
}

/*******************************************************************************
*函式名:LinkStack_Destroy
*引數:void
*返回值:LinkStack* 棧的指標 
*功能:銷燬棧 呼叫棧清除函式和連結串列銷燬函式 
*******************************************************************************/
void LinkStack_Destroy(LinkStack* stack)
{
	/*呼叫棧清空函式*/
	LinkStack_Clear (stack);
	/*呼叫連結串列銷燬函式*/
	LinkList_Destroy(stack); 
}

/*******************************************************************************
*函式名:LinkStack_Clear
*引數:void
*返回值:LinkStack* 棧的指標 
*功能:棧清空函式  
*******************************************************************************/
void LinkStack_Clear(LinkStack* stack)
{
	while (LinkStack_Size (stack) > 0)
	{
		LinkStack_Pop (stack);
	} 
}

/*******************************************************************************
*函式名:LinkStack_Push
*引數:LinkStack* stack  棧指標  void* item  要壓入棧的元素 
*返回值:int 判斷壓棧操作是否成功 
*功能:將資料元素壓入棧 
*******************************************************************************/
int LinkStack_Push(LinkStack* stack, void* item)
{
	TLinkStackNode* node = (TLinkStackNode*)malloc (sizeof (TLinkStackNode));

	int ret = (node != NULL) && (item != NULL); 
	
	if (ret)
	{
		node->item = item;
		
		ret = LinkList_Insert (stack, (LinkListNode*)node, 0);
	}

	if (!ret)
	{
		free(node);
	}
	
	return ret; 
}

/*******************************************************************************
*函式名:LinkStack_Pop
*引數:LinkStack* stack  棧指標 
*返回值:void* 返回的是出棧的元素 
*功能:將資料元素彈出棧 
*******************************************************************************/
void* LinkStack_Pop(LinkStack* stack)
{
	TLinkStackNode* node = (TLinkStackNode*)LinkList_Delete(stack, 0);
	void * ret = NULL;
	if (node != NULL)
	{
		ret = node->item;
		free(node);
	} 
	return ret;
}

/*******************************************************************************
*函式名:LinkStack_Top
*引數:LinkStack* stack  棧指標 
*返回值:void* 返回的是棧頂的元素 
*功能:返回棧頂元素 
*******************************************************************************/
void* LinkStack_Top(LinkStack* stack)
{
	TLinkStackNode* node = (TLinkStackNode*)LinkList_Get(stack, 0);
	void *ret = NULL;
	if (node != NULL)
	{
		ret = node->item;
	} 
	return ret;
}

/*******************************************************************************
*函式名:LinkStack_Size
*引數:LinkStack* stack  棧指標 
*返回值:int 失敗返回-1,成功返回棧的大小 
*功能:返回鏈棧的大小 
*******************************************************************************/
int LinkStack_Size(LinkStack* stack)
{
	int ret = -1;
	ret = LinkList_Length(stack); 
	return ret;
}

2.標頭檔案部分

#ifndef _LINKSTACK_H_
#define _LINKSTACK_H_

typedef void LinkStack;

LinkStack* LinkStack_Create();

void LinkStack_Destroy(LinkStack* stack);

void LinkStack_Clear(LinkStack* stack);

int LinkStack_Push(LinkStack* stack, void* item);

void* LinkStack_Pop(LinkStack* stack);

void* LinkStack_Top(LinkStack* stack);

int LinkStack_Size(LinkStack* stack);

#endif

3.測試程式碼部分

#include <stdio.h>
#include <stdlib.h>
#include "1.h"
 
int main(int argc, char *argv[])
{
	int i = 0;
	int a[10];
	int temp;
	
	LinkStack * stack = LinkStack_Create();
	for (i = 0; i < 10; i++)
	{
		a[i] = i;
		LinkStack_Push(stack, a + i);
	}
	
	temp = LinkStack_Size(stack);
	printf ("棧的大小為:%d\n", temp);
	for (i = 0; i < 10; i++)
	{
		printf ("出棧元素為:%d\n", *(int*)LinkStack_Pop(stack));
	}
	
	return 0;
}