棧是一種特殊的線性表,它特殊在對線性表的操作進行了限制,只能在它的一端進行插入和刪除,如下圖所示:
遵循的是“先進後出”(first in last out)的規則,簡稱爲”FILO”。
既然棧也是線性表,那麼線性表的存儲結構同樣也適用於棧,通常有順序棧和鏈式棧兩種。
1.順序棧的實現
頭文件聲明:
seqstack.h
#pragma once
#include<stddef.h>
typedef char SeqStackType;
typedef struct SeqStack
{
SeqStackType* data;
size_t size;
size_t capacity;//data這段內存中能容納的元素個數
}SeqStack;
void SeqStackInit(SeqStack* stack);//初始化
void SeqStackDestroy(SeqStack* stack);//銷燬
void SeqStackReSize(SeqStack* stack);//重新設置大小
void SeqStackPush(SeqStack* stack,SeqStackType value);//壓棧
void SeqStackPop(SeqStack* stack);//出棧
//返回兩個信息:執行成功失敗,棧頂元素是多少
int SeqStackTop(SeqStack* stack,SeqStackType* value);//取棧頂元素,value輸出型參數
具體實現及測試代碼:
seqstack.c
#include"seqstack.h"
#include<stdio.h>
#include<stdlib.h>
void SeqStackInit(SeqStack* stack)
{
stack->size = 0;
stack->capacity = 1000;
stack->data = (SeqStackType*)malloc(stack->capacity*sizeof(SeqStackType));//開闢空間
}
void SeqStackDestroy(SeqStack* stack)
{
free(stack->data);//釋放數據
stack->size = 0;//大小設爲空
stack->capacity = 0;
}
void SeqStackReSize(SeqStack* stack)
{
if(stack->size < stack->capacity)//棧大小小於最大容量,不需要擴容,直接返回
{
return;
}
stack->capacity = stack->capacity*2+1;//擴容規則根據需求選擇
SeqStackType* new_ptr = (SeqStackType*)malloc(stack->capacity * sizeof(SeqStackType));//重新開闢空間
int i = 0;
for( ;i < stack->size;i++)
{
new_ptr[i] = stack->data[i];//將以前棧的數據賦給新開闢的空間
}
free(stack->data);//釋放以前的棧數據
stack->data = new_ptr;//更新指針
return;
}
void SeqStackPush(SeqStack* stack,SeqStackType value)
{
if(stack == NULL)
{
return; //非法輸入
}
if(stack->size >= stack->capacity)//當棧大小大於棧的最大容量時,擴容
{
//TODO 擴容
SeqStackReSize(stack);
}
stack->data[stack->size++] = value;
}
void SeqStackPop(SeqStack* stack)
{
if(stack == NULL)
{
return; //非法輸入
}
if(stack->size == 0)
{
return; //空棧
}
--stack->size;
return;
}
int SeqStackTop(SeqStack* stack,SeqStackType* value)
{
if(stack == NULL)
{
return 0; //非法輸入
}
if(stack->size == 0)
{
return 0; //空棧
}
*value = stack->data[stack->size-1];
return 1;
}
//////////////////////////////////////////////
///////////////以下爲測試代碼////////////////
////////////////////////////////////////////
#include<stdio.h>
#define TEST_HEADER printf("\n======================%s========================\n",__FUNCTION__)
void SeqStackPrintChar(SeqStack*stack,char* msg)
{
if(stack == NULL)
{
return; //非法輸入
}
printf("[%s]\n",msg);
int i = 0;
for( ;i <stack->size;i++)
{
printf("[%c] ",stack->data[i]);
}
return;
}
void TestInit()
{
TEST_HEADER;
SeqStack stack;
SeqStackInit(&stack);
printf("size expected 0,actual %lu\n",stack.size);
printf("capacity expected 1000;actual %lu\n",stack.capacity);
}
void TestDestroy()
{
TEST_HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackDestroy(&stack);
}
void TestPush()
{
TEST_HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
SeqStackPush(&stack,'d');
SeqStackPrintChar(&stack,"入棧四個元素");
}
void TestPop()
{
TEST_HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
SeqStackPush(&stack,'d');
SeqStackPop(&stack);
SeqStackPrintChar(&stack,"出棧棧頂元素");
}
void TestTop()
{
TEST_HEADER;
SeqStack stack;
SeqStackInit(&stack);
SeqStackPush(&stack,'a');
SeqStackPush(&stack,'b');
SeqStackPush(&stack,'c');
SeqStackPush(&stack,'d');
SeqStackType value;
int ret = SeqStackTop(&stack,&value);
printf("ret expected 1,actual %d\n",ret);
printf("value expected d,actual %c\n",value);
SeqStackPop(&stack);
SeqStackPop(&stack);
ret = SeqStackTop(&stack,&value);
printf("ret expected 1,actual %d\n",ret);
printf("value expected b,actual %c\n",value);
SeqStackPop(&stack);
SeqStackPop(&stack);
ret = SeqStackTop(&stack,&value);
printf("ret expected 0,actual %d\n",ret);
}
int main()
{
TestInit();
TestDestroy();
TestPush();
TestPop();
TestTop();
printf("\n");
return 0;
}
上面這種棧的實現採用了擴容的方式,避免了棧溢出問題。
順序棧是用數組的形式存儲數據,數組中的元素在內存中是連續存儲的,這樣在訪問數據時,時間複雜度較小,效率高,插入和刪除時不需要移動元素。但是系統將內存分配給數組後,這些內存其他任務不可用,內存利用率低。
2.鏈式棧的實現
鏈式棧是用指針來指示數據的存儲位置的,元素在內存中可以隨意存儲,不需要連續的存儲位置,內存利用率高,一般適用於棧元素數目變化較大或不清楚棧元素的數目的情況,鏈棧存儲如下圖所示:
實現:
頭文件聲明:
linkstack.h
#include<stddef.h>
typedef char LinkStackType;
typedef struct LinkStackNode
{
LinkStackType data;
struct LinkStackNode* next;
}LinkStackNode;
LinkStackNode* phead;
void LinkStackInit(LinkStackNode** phead);//鏈式棧初始化
void LinkStackPush(LinkStackNode** phead,LinkStackType value);//入棧
void LinkStackPop(LinkStackNode** phead);//出棧
int LinkStackTop(LinkStackNode* phead,LinkStackType* value);//取棧頂元素
具體實現及測試代碼:
linkstack.c
#include"linkstack.h"
#include<stdio.h>
#include<stdlib.h>
LinkStackNode* CreateLinkStackNode(LinkStackType value)//創建結點
{
LinkStackNode* new_node = (LinkStackNode*)malloc(sizeof(LinkStackNode));//開闢空間
new_node->data = value;
new_node->next = NULL;
return new_node;
}
void LinkStackInit(LinkStackNode** phead)
{
if(phead == NULL)
{
return;
}
*phead = NULL;
return;
}
void LinkStackDestroy(LinkStackNode** phead)
{
if(phead == NULL)
{
return;//非法輸入
}
if(*phead == NULL)
{
return;//空棧
}
LinkStackNode* cur = *phead;
while(cur != NULL)
{
LinkStackNode* to_delete = cur;
free(to_delete);
cur = cur->next;
}
return;
}
void LinkStackPush(LinkStackNode** phead,LinkStackType value)
{
if(phead == NULL)
{
return;//非法輸入
}
LinkStackNode* cur = *phead;
(*phead) = CreateLinkStackNode(value);//頭結點直接指向新結點,使新結點爲新的頭結點
(*phead)->next = cur;
return;
}
void LinkStackPop(LinkStackNode** phead)
{
if(phead == NULL)
{
return; //非法輸入
}
if(*phead == NULL)
{
return;//空棧
}
LinkStackNode* to_delete = *phead;
(*phead) = to_delete->next;//更新頭結點
free(to_delete);//釋放舊的頭結點
return;
}
int LinkStackTop(LinkStackNode* phead,LinkStackType* value)
{
if(phead == NULL)
{
return 0;//非法操作
}
*value = (phead)->data;
return 1;
}
////////////////////////////////////////////////
/////////////以下是測試代碼////////////////////
///////////////////////////////////////////////
#include<stdio.h>
#if 1
#define TEST_HEADER printf("\n===============================%s===================================\n",__FUNCTION__)
void LinkStackPrintChar(LinkStackNode* phead,const char* msg)
{
printf("[%s]\n",msg);
if(phead == NULL)
{
return;
}
LinkStackNode* cur = phead;
for( ;cur != NULL;cur = cur->next)
{
printf("[%c] ",cur->data);
}
printf("\n");
}
void TestInit()
{
TEST_HEADER;
LinkStackNode* head;
LinkStackInit(&head);
}
void TestPush()
{
TEST_HEADER;
LinkStackNode* head;
LinkStackInit(&head);
LinkStackPush(&head,'a');
LinkStackPush(&head,'b');
LinkStackPush(&head,'c');
LinkStackPush(&head,'d');
LinkStackPrintChar(head,"入棧四個元素");
}
void TestPop()
{
TEST_HEADER;
LinkStackNode* head;
LinkStackInit(&head);
LinkStackPop(&head);
LinkStackPrintChar(head,"嘗試對空棧操作");
LinkStackPush(&head,'a');
LinkStackPush(&head,'b');
LinkStackPush(&head,'c');
LinkStackPush(&head,'d');
LinkStackPop(&head);
LinkStackPop(&head);
LinkStackPrintChar(head,"出棧兩個元素");
LinkStackPop(&head);
LinkStackPop(&head);
LinkStackPrintChar(head,"出棧兩個元素");
}
void TestTop()
{
TEST_HEADER;
LinkStackNode* head;
LinkStackInit(&head);
LinkStackPush(&head,'a');
LinkStackPush(&head,'b');
LinkStackPush(&head,'c');
LinkStackPush(&head,'d');
LinkStackType value;
int ret = LinkStackTop(head,&value);
printf("ret expected 1.actual %d\n",ret);
printf("value expected d,actual %c\n",value);
LinkStackPop(&head);
LinkStackPop(&head);
ret = LinkStackTop(head,&value);
printf("ret expected 1.actual %d\n",ret);
printf("value expected b,actual %c\n",value);
LinkStackPop(&head);
LinkStackPop(&head);
ret = LinkStackTop(head,&value);
printf("ret expected 0,actual %d\n",ret);
}
int main()
{
TestInit();
TestPush();
TestPop();
TestTop();
printf("\n");
}
#endif