顺序栈与链式栈

目录

[1. 栈](#1. 栈)

[1.1 栈的概念](#1.1 栈的概念)

[2. 栈的实现](#2. 栈的实现)

[3. 顺序栈的实现](#3. 顺序栈的实现)

[3.1 顺序栈的声明](#3.1 顺序栈的声明)

[3.2 顺序栈的初始化](#3.2 顺序栈的初始化)

[3.3 顺序栈的入栈](#3.3 顺序栈的入栈)

[3.4 顺序栈的出栈](#3.4 顺序栈的出栈)

[3.5 顺序栈获取栈顶元素](#3.5 顺序栈获取栈顶元素)

[3.6 顺序栈获取栈内有效数据个数](#3.6 顺序栈获取栈内有效数据个数)

[3.7 顺序栈判断栈是否为空](#3.7 顺序栈判断栈是否为空)

[3.8 顺序栈打印栈内元素](#3.8 顺序栈打印栈内元素)

[3.9 顺序栈销毁栈](#3.9 顺序栈销毁栈)

[3.10 完整代码](#3.10 完整代码)

[4. 链式栈的实现](#4. 链式栈的实现)

[4.1 链式栈的声明](#4.1 链式栈的声明)

[4.2 链式栈的初始化](#4.2 链式栈的初始化)

[4.3 链式栈的入栈](#4.3 链式栈的入栈)

[4.4 链式栈的出栈](#4.4 链式栈的出栈)

[4.5 链式栈获取栈顶元素](#4.5 链式栈获取栈顶元素)

[4.6 链式栈获取栈内有效数据个数](#4.6 链式栈获取栈内有效数据个数)

[4.7 链式栈判断栈是否为空](#4.7 链式栈判断栈是否为空)

[4.8 链式栈打印栈内元素](#4.8 链式栈打印栈内元素)

[4.9 链式栈销毁栈](#4.9 链式栈销毁栈)

[5. 疑问](#5. 疑问)


1. 栈

1.1 栈的概念

栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素的操作。**进行数据插入和删除操作的一段称为栈顶,另一端称为栈底。**栈中的元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2. 栈的实现

我们了解了栈的概念,我们就可以发现,栈其实可以用顺序表和链表来实现,我们根据它的实现方式,可以把它分为顺序栈与链式栈。

3. 顺序栈的实现

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* arr;//指向动态开辟的一块空间
	int top;//指向栈顶元素的下一个位置
	int capacity;//栈的容量
}st;

//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst,STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

3.1 顺序栈的声明

Stack.h

typedef int STDataType;

typedef struct Stack
{
	STDataType* arr;//指向动态开辟的一块空间
	int top;//指向栈顶元素的下一个位置
	int capacity;//栈的容量
}st;

这里我们在声明顺序栈的结构的时候,这个top可以是指向栈顶元素的,那我们在进行栈的初始化就需要将他初始化为-1,如果top是指向栈顶元素的下一个,那我们就需要将他的初始化设置为0。两种方法均可行,这里可以根据自己的习惯进行设置。

3.2 顺序栈的初始化

Stack.c

void STInit(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	pst->arr = NULL;
	pst->capacity = pst->top = 0;
}

3.3 顺序栈的入栈

Stack.c

void STPush(st* pst, STDataType x)
{
	//判断pst是否为空指针
	assert(pst);
	//检查空间大小,是否需要增容
	if (pst->capacity == pst->top)
	{
		int newcapacity = pst->capacity == 0 ? 4 :2 * pst->capacity;
		STDataType* tmp = (STDataType*)realloc(pst->arr,newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		pst->arr = tmp;
		pst->capacity = newcapacity;
	}
	//入数据
	pst->arr[pst->top++] = x;
}

3.4 顺序栈的出栈

Stack.c

void STPop(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	//判断栈是否为空
	assert(pst->top > 0);

	pst->top--;
}

3.5 顺序栈获取栈顶元素

Stack.c

STDataType STTop(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	//判断栈是否为空
	assert(pst->top > 0);
	//这里top指向栈顶元素的下一个位置,-1刚好指向栈顶元素
	return pst->arr[pst->top - 1];
}

3.6 顺序栈获取栈内有效数据个数

Stack.c

int STSize(st* pst)
{
	assert(pst);

	return pst->top;
}

3.7 顺序栈判断栈是否为空

Stack.c

bool STEmpty(st* pst)
{
	assert(pst);

	return pst->top == 0;
}

3.8 顺序栈打印栈内元素

Stack.c

void STPrint(st* pst)
{
	assert(pst);
	while(!STEmpty(pst))
	{
		printf("%d\n", STTop(pst));
		STPop(pst);
	}
}

3.9 顺序栈销毁栈

Stack.c

void STDestroy(st* pst)
{
	assert(pst);
	free(pst->arr);
	pst->arr = NULL;
	pst->top = pst->capacity = 0;
}

3.10 完整代码

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* arr;//指向动态开辟的一块空间
	int top;//指向栈顶元素的下一个位置
	int capacity;//栈的容量
}st;

//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst,STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

Stack.c

#include"Stack.h"

void STInit(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	pst->arr = NULL;
	pst->capacity = pst->top = 0;
}

void STPush(st* pst, STDataType x)
{
	//判断pst是否为空指针
	assert(pst);
	//检查空间大小,是否需要增容
	if (pst->capacity == pst->top)
	{
		int newcapacity = pst->capacity == 0 ? 4 :2 * pst->capacity;
		STDataType* tmp = (STDataType*)realloc(pst->arr,newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc");
			exit(1);
		}
		pst->arr = tmp;
		pst->capacity = newcapacity;
	}
	//入数据
	pst->arr[pst->top++] = x;
}

void STPop(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	//判断栈是否为空
	assert(pst->top > 0);

	pst->top--;
}

STDataType STTop(st* pst)
{
	//判断pst是否为空指针
	assert(pst);
	//判断栈是否为空
	assert(pst->top > 0);
	//这里top指向栈顶元素的下一个位置,-1刚好指向栈顶元素
	return pst->arr[pst->top - 1];
}

int STSize(st* pst)
{
	assert(pst);

	return pst->top;
}

bool STEmpty(st* pst)
{
	assert(pst);

	return pst->top == 0;
}

void STPrint(st* pst)
{
	assert(pst);
	while(!STEmpty(pst))
	{
		printf("%d\n", STTop(pst));
		STPop(pst);
	}
}

void STDestroy(st* pst)
{
	assert(pst);
	free(pst->arr);
	pst->arr = NULL;
	pst->top = pst->capacity = 0;
}

4. 链式栈的实现

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;

typedef struct StackNode //定义节点结构
{
	STDataType data;
	struct StackNode* next;
}stnode;

typedef struct Stack //定义栈结构
{
	//记录栈顶
	stnode* top;
	//记录栈内有效数据个数
	int size;
}st;

//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst, STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

4.1 链式栈的声明

Stack.h

typedef int STDataType;

typedef struct StackNode //定义节点结构
{
	STDataType data;
	struct StackNode* next;
}stnode;

typedef struct Stack //定义栈结构
{
	//记录栈顶
	stnode* top;
	//记录栈内有效数据个数
	int size;
}st;

这里如果我们用单链表的形式实现栈的话,单链表头插的时间复杂度为O(1),尾插的时间复杂度为O(N),所以这里我们需要把单链表的第一个有效节点设置为栈顶,如果想把链表的尾设置尾栈顶的话,那我们这里可能就需要使用双向链表来实现了,本章主要使用单链表来实现栈。

我们在声明链式栈的时候,我们这里使用了两个结构体,其中一个结构体声明链式栈里的数据节点,另一个结构体声明栈的结构,其中里面有个指向栈顶指针,还有一个记录栈内有效数据个数的size,这样我们在获取栈内有效数据个数的时候不需要遍历链表,时间复杂度从O(N)变成O(1)

4.2 链式栈的初始化

Stack.c

void STInit(st* pst)
{
	assert(pst);
	pst->top = NULL;
	pst->size = 0;
}

4.3 链式栈的入栈

Stack.c

void STPush(st* pst, STDataType x)
{
	assert(pst);
	stnode* newnode = (stnode*)malloc(sizeof(stnode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pst->top== NULL)
	{
		pst->top = newnode;
	}
	else
	{
		newnode->next = pst->top;
		pst->top = newnode;
	}
	pst->size++;
}

4.4 链式栈的出栈

Stack.c

void STPop(st* pst)
{
	assert(pst);
	assert(pst->size > 0);
	stnode* next = pst->top->next;
	free(pst->top);
	pst->top = next;
	pst->size--;
}

4.5 链式栈获取栈顶元素

Stack.c

STDataType STTop(st* pst)
{
	assert(pst);
	assert(pst->size > 0);

	return pst->top->data;
}

4.6 链式栈获取栈内有效数据个数

Stack.c

int STSize(st* pst)
{
	assert(pst);
	
	return pst->size;
}

4.7 链式栈判断栈是否为空

Stack.c

bool STEmpty(st* pst)
{
	assert(pst);

	return pst->size == 0;
}

4.8 链式栈打印栈内元素

Stack.c

void STPrint(st* pst)
{
	assert(pst);

	while (!STEmpty(pst))
	{
		printf("%d\n",STTop(pst));
		STPop(pst);
	}
}

4.9 链式栈销毁栈

Stack.c

void STDestroy(st* pst)
{
	assert(pst);
	stnode* pcur = pst->top;
	while (pcur)
	{
		stnode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pst->top = NULL;
	pst->size = 0;
}

4.10 完整代码

Stack.h

//需要用到的库函数的头文件
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;

typedef struct StackNode //定义节点结构
{
	STDataType data;
	struct StackNode* next;
}stnode;

typedef struct Stack //定义栈结构
{
	//记录栈顶
	stnode* top;
	//记录栈内有效数据个数
	int size;
}st;

//初始化栈
void STInit(st* pst);
//入栈
void STPush(st* pst, STDataType x);
//出栈
void STPop(st* pst);
//获取栈顶元素
STDataType STTop(st* pst);
//获取栈内有效数据个数
int STSize(st* pst);
//判断栈是否为空
bool STEmpty(st* pst);
//打印栈内元素
void STPrint(st* pst);
//销毁栈
void STDestroy(st* pst);

Stack.c

void STInit(st* pst)
{
	assert(pst);
	pst->top = NULL;
	pst->size = 0;
}

void STPush(st* pst, STDataType x)
{
	assert(pst);
	stnode* newnode = (stnode*)malloc(sizeof(stnode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pst->top== NULL)
	{
		pst->top = newnode;
	}
	else
	{
		newnode->next = pst->top;
		pst->top = newnode;
	}
	pst->size++;
}

void STPop(st* pst)
{
	assert(pst);
	assert(pst->size > 0);
	stnode* next = pst->top->next;
	free(pst->top);
	pst->top = next;
	pst->size--;
}

STDataType STTop(st* pst)
{
	assert(pst);
	assert(pst->size > 0);

	return pst->top->data;
}

int STSize(st* pst)
{
	assert(pst);
	
	return pst->size;
}

bool STEmpty(st* pst)
{
	assert(pst);

	return pst->size == 0;
}

void STPrint(st* pst)
{
	assert(pst);

	while (!STEmpty(pst))
	{
		printf("%d\n",STTop(pst));
		STPop(pst);
	}
}

void STDestroy(st* pst)
{
	assert(pst);
	stnode* pcur = pst->top;
	while (pcur)
	{
		stnode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pst->top = NULL;
	pst->size = 0;
}

5. 疑问

我们发现,我们很多的接口函数只有几行代码,有必要单独写成一个函数吗?比如说出栈,我们直接通过访问结构体进行删除。

其实有必要的,为了规范一定的写法,保持一致性,我们可以通过接口来调用它的函数,不建议自行访问,因为我们不知道设计者在设计top的时候,top是指向栈顶元素还是栈顶元素的下一个位置。

所以,不管再简单,数据结构里面不要直接访问数据,建议调用它的接口方法。

相关推荐
mirevass1 分钟前
【嵌入式软件-数据结构与算法】01-数据结构
c语言·数据结构·链表··队列
只是有点小怂37 分钟前
数据结构const char *INSTNAME[]
数据结构
问道飞鱼1 小时前
每日学习一个数据结构-堆
数据结构·学习·算法
潘潘潘潘潘潘潘潘潘潘潘潘1 小时前
【数据结构】栈和队列
开发语言·数据结构·学习方法
c#上位机3 小时前
C#从入门到精通(30)—C#Marshal类用法总结
开发语言·数据结构·c#
程序和我有一个能跑就行。3 小时前
【Python】递归
数据结构·python·算法·递归
Grayson_Zheng3 小时前
【数据结构】环形队列(循环队列)学习笔记总结
c语言·数据结构·算法
代码雕刻家5 小时前
数据结构-3.5.队列的顺序实现
c语言·数据结构·算法
奶香滴小馒头5 小时前
Day101 代码随想录打卡|动态规划篇--- 分割等和子集
数据结构·算法·leetcode·游戏·动态规划
fhvyxyci9 小时前
【数据结构初阶】排序算法(上)插入排序与选择排序
数据结构·算法·排序算法