数据结构:顺序栈和链式栈的原理,实现和实际应用

✨✨小新课堂开课了,欢迎欢迎~✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法

小新的主页:编程版小新-CSDN博客

 1.栈的定义

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作

进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2.栈的分类

这里对栈的分类,其实就是用不同的方式来实现栈。可以用顺序表链表来实现。这里的链表指的是单链表,当然双向链表也能实现栈,但是选双向链表的话不如选单链表,少维护一个指针,而且单链表的实现也十分的简单。

ps:这里在实现链式栈的时候采用的头插法,因为栈的特点是后进先出,采用头插法入栈和出栈都很方便,比尾插法高效。 

3.栈的声明

1.顺序栈

a指向一块空间,top指的是栈顶元素,capacity指的是栈内数据的个数。

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;//指栈顶
	int capacity;//容量大小
}ST;

2.链式栈

链式栈里只需要一个结构体指针top和记录栈的大小的size即可。

typedef int STDataType;
typedef struct SListNode
{
	STDataType data;
	struct SListNode* next;
}STNode;


typedef struct Stack
{
	STNode* top;
	int size;
}Stack;

 4.栈的功能

1.栈的初始化

2.检测栈是否为空

3.入栈

4.出栈

5.返回栈顶元素

6.返回栈的大小

7.打印栈

8.销毁栈

5.实现栈的功能 

5.1初始化栈

1.顺序栈

//初始化
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶数据的下一个位置
	pst->top =0;

	//top指向栈顶数据
	//pst->top = -1;

	pst->capacity = 0;
}

在初始化top时分两中情况。如果将top初始化为0,那么top就必须指的是栈顶数据的下一位置,要想top指向栈顶元素,就要将top初始化为-1。(想让top指向栈顶元素,还把top初始化为0的话,当top==0时栈内有数据还是无数据是区分不开的,因此有了这两种初始化的方式)

2.链式栈

只需将维护栈的Stack指针初始化即可。

//初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->top = NULL;
	pst->size = 0;
}

5.2检测栈是否为空

1.顺序栈

当top==0时就像初始化时的那样,栈内没有数据,栈为空。

//判空
bool STempty(ST* pst)
{
	assert(pst);

	return pst->top == 0;

}

2.链式栈 

像初始化时给定的那样,当pst->top==NULL时,栈内没有数据,栈为空。

//判空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	return (pst->top == NULL);
}

5.3入栈

1.顺序栈

入栈前要检查空间是否充足,空间不够的话要扩容。

//入栈
void STPush(ST* pst,STDataType x)
{
	assert(pst);
	
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
        pst->a = tmp;
        pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}

2.链式栈

每添加一个数据(压栈),就要创建一个节点。并且这里采用头插法的方式插入数据。(f方便出数据即出栈)

STNode* CreatNode(STDataType x)
{
	STNode* newnode = (STNode*)malloc(sizeof(struct SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}
//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	STNode* newnode = CreatNode(x);
	if (pst->top == NULL)//if(Stackempty(pst))
	{
		pst->top = newnode;
	}
	else
	{
		newnode->next = pst->top;
		pst->top = newnode;
	}
	pst->size++;
}

5.4出栈

1.顺序栈

出栈的时候不需要删除数据,只需让top往前走即可。

//出栈
void STPop(ST* pst)
{
	assert(pst);

	assert(pst->top > 0);
    //assert(!STempty(pst));
	pst->top--;
}

2.链式栈 

pst->top指向的就是要删除的数据,只需释放头节点即可(不要忘记记住下一个节点的位置哦)

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	STNode* next = pst->top->next;
	free(pst->top);

	pst->top = next;

	pst->size--;
}

5.5返回栈顶元素

1.顺序栈

top初始化的时候指向栈顶元素的下一个位置,要返回栈顶元素的时候,就要让top-1,当然这是在栈内有数据的前提下。

//取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
    //assert(!STempty(pst));

	return pst->a[pst->top - 1];

}

2.链式栈 

//取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	return pst->top->data;
}

5.6返回栈的大小

1.顺序栈

这里就把顺序表想象成数组,当然顺序表的底层就是数组,这里top是采用下标访问的。top所代表的下标就是栈的大小。


//获取数据个数
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

2.链式栈 

//返回栈的大小
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->size;
}

5.7打印栈

1.顺序栈

//打印栈
void STPrint(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	//assert(!STempty(pst));
	for (int i = pst->top - 1; i >= 0; i--)
	{
		printf("%d\n", pst->a[i]);
	}

}

2.链式栈 

//打印栈
void StackPrint(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	while (pst->top)
	{
		printf("%d\n", pst->top->data);
		pst->top = pst->top->next;
	}
}

5.8销毁栈

1.顺序栈

//销毁
void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

2.链式栈

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);
	while (pst->top)
	{
		STNode* next = pst->top->next;
		free(pst->top);
		
		pst->top = next;
	}
}

 6.顺序栈和链式栈的区别

在看顺序栈和链式栈的区别之前,我们先来回顾一下顺序表和单链表的区别。

6.1顺序表和链表的区别

不同点顺序表双链表
储存空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(N)
任意位置插入或删除可能需要搬运元素,效率低下只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念(按需申请释放)
应用场景元素高效储存+频繁访问在任意位置插入和删除频繁
缓存利用率

6.2顺序栈和链式的区别

 7.栈的实际应用

栈在现实中有很多应用,以下是一些常见的例子:

1. 网页浏览器的历史记录:每次访问一个新页面时,将当前页面压入栈中,当用户点击后退按钮时,从栈中弹出上一个页面。

2. 文本编辑器的撤销操作:每次执行一个操作(如插入、删除等)时,将操作前的状态压入栈中,当用户点击撤销按钮时,从栈中弹出上一个状态并恢复。

8.完整代码

1.顺序栈

stack.h:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;

//初始化
void STInit(ST* pst);

//入栈
void STPush(ST* pst,STDataType X);

//出栈
void STPop(ST* pst);

//取栈顶数据
STDataType STTop(ST* pst);

//判空
bool STempty(ST* pst);

//获取数据个数
int STSize(ST* pst);

//打印栈
void STPrint(ST* pst);

//销毁
void STDestroy(ST* pst);

stack.c:

#include"stack.h"

//初始化
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶数据的下一个位置
	pst->top =0;

	//top指向栈顶数据
	//pst->top = -1;

	pst->capacity = 0;
}

//入栈
void STPush(ST* pst,STDataType x)
{
	assert(pst);
	
	//扩容
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}

	pst->a[pst->top] = x;
	pst->top++;
}

//出栈
void STPop(ST* pst)
{
	assert(pst);

	assert(pst->top > 0);
	//assert(!STempty(pst));
	pst->top--;
}

//取栈顶数据
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	//assert(!STempty(pst));

	return pst->a[pst->top - 1];

}

//判空
bool STempty(ST* pst)
{
	assert(pst);

	return pst->top == 0;

}

//获取数据个数
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}

//打印栈
void STPrint(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	//assert(!STempty(pst));
	for (int i = pst->top - 1; i >= 0; i--)
	{
		printf("%d\n", pst->a[i]);
	}

}

//销毁
void STDestroy(ST* pst)
{
	assert(pst);

	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

2.链式栈

Stack.h:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;
typedef struct SListNode
{
	STDataType data;
	struct SListNode* next;
}STNode;


typedef struct Stack
{
	STNode* top;
	int size;
}Stack;

//初始化
void StackInit(Stack* pst);

//判空
bool StackEmpty(Stack* pst);

//入栈
void StackPush(Stack* pst, STDataType x);

//出栈
void StackPop(Stack* pst);

//取栈顶元素
STDataType StackTop(Stack* pst);

Stack.c:

#include"stack.h"

//初始化
void StackInit(Stack* pst)
{
	assert(pst);
	pst->top = NULL;
	pst->size = 0;
}

//判空
bool StackEmpty(Stack* pst)
{
	assert(pst);
	return (pst->top == NULL);
}

STNode* CreatNode(STDataType x)
{
	STNode* newnode = (STNode*)malloc(sizeof(struct SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}
//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);
	STNode* newnode = CreatNode(x);
	if (pst->top == NULL)//if(StackEmpty(pst))
	{
		pst->top = newnode;
	}
	else
	{
		newnode->next = pst->top;
		pst->top = newnode;
	}
	pst->size++;
}

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	STNode* next = pst->top->next;
	free(pst->top);
	pst->top = NULL;
	pst->top = next;

	pst->size--;
}

//取栈顶元素
STDataType StackTop(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));

	return pst->top->data;
}

//返回栈的大小
int StackSize(Stack* pst)
{
	assert(pst);
	return pst->size;
}

//打印栈
void StackPrint(Stack* pst)
{
	assert(pst);
	assert(!StackEmpty(pst));
	while (pst->top)
	{
		printf("%d\n", pst->top->data);
		pst->top = pst->top->next;
	}
}

//销毁栈
void StackDestroy(Stack* pst)
{
	assert(pst);
	while (pst->top)
	{
		STNode* next = pst->top->next;
		free(pst->top);
		
		pst->top = next;
	}
}

结束了~

下次还记得来哦

  • 31
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值