【数据结构与算法】受限线性表 --- 栈

【数据结构与算法】受限线性表 — 栈



前言

本篇文章就栈的基本概念,栈的顺序存储,栈的分文件编写,栈的链式存储,栈的应用案例-就近分配, 中缀表达式转后缀表达式以及基于后缀表达式运算。


一、栈的基本概念

概念:
首先它是一个线性表,也就是说,栈元素具有线性关系,即前驱后继关系。只不过它是一种特殊的线性表而已。定义中说是在线性表的表尾进行插入和删除操作,这里表尾是指栈顶,而不是栈底。
特性
它的特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行。这也就使得:栈底是固定的,最先进栈的只能在栈底。
操作:
栈的插入操作,叫做进栈,也成压栈。(如下图所示)
栈的删除操作,叫做出栈,也有的叫做弾栈,退栈。(如下图所示)
在这里插入图片描述


二、栈的顺序存储

  • 初始化栈
#define MAX 1024

struct SStack
{
	void* data[MAX];	//栈的数组

	int m_size;	//栈的大小

};

typedef void* SeqStack;

//初始化栈
SeqStack init_SeqStack() {
	struct SStack* myStack = malloc(sizeof(struct SStack));

	if (myStack == NULL)
	{
		return NULL;
	}

	//初始化数组
	memset(myStack->data, 0, sizeof(void*) * MAX);

	//初始化栈大小
	myStack->m_size = 0;

	return myStack;
}
  • 入栈
//入栈
void push_SeqStack(SeqStack stack, void* data) {
	//入栈本质  --- 数组尾插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;

	if (mystack->m_size == MAX)
	{
		return;
	}

	mystack->data[mystack->m_size] = data;

	mystack->m_size++;
}
  • 出栈
//出栈
void pop_SeqStack(SeqStack stack)
{
	//出栈本质  ---数组尾删
	if (stack == NULL)
	{
		return;
	}
	
	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return;
	}
	mystack->data[mystack->m_size - 1] = NULL;

	mystack->m_size--;
}
  • 返回栈顶
// 返回栈顶
void* top_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}
	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return NULL;
	}

	return mystack->data[mystack->m_size - 1];
}
  • 返回栈大小
// 返回栈大小
int size_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	
	struct SStack* mystack = stack;

	return mystack->m_size;
}
  • 判断是否是空栈
//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;	//返回-1 代表真  空栈
	}

	struct SStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return 1;
	}

	return 0;	//返回0 代表假 不是空栈

}
  • 销毁栈
//销毁栈
void destroy_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return;
	}

	free(stack);
	stack = NULL;
}
  • 测试代码
struct Person
{
	char name[64];
	int age;
};

void test01()
{
	//初始化栈
	SeqStack myStack = init_SeqStack();

	//创建数据
	struct Person p1 = { "aaa", 10 };
	struct Person p2 = { "bbb", 20 };
	struct Person p3 = { "ccc", 30 };
	struct Person p4 = { "ddd", 40 };
	struct Person p5 = { "eee", 50 };

	//入栈
	push_SeqStack(myStack, &p1);
	push_SeqStack(myStack, &p2);
	push_SeqStack(myStack, &p3);
	push_SeqStack(myStack, &p4);
	push_SeqStack(myStack, &p5);

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	while (isEmpty_SeqStack(myStack) == 0)
	{
		struct Person* p = top_SeqStack(myStack);
		printf("姓名:%s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_SeqStack(myStack);
	}

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	//销毁栈
	destroy_SeqStack(myStack);
}

int main() {
	test01();

	return 0;
}

三、栈的分文件编写

  • seqStack.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define  MAX 1024

//struct SStack
//{
//	void * data[MAX];  //栈的数组
//
//	int m_Size; //栈大小
//};

typedef void* SeqStack;

//初始化栈
SeqStack init_SeqStack();

//入栈
void push_SeqStack(SeqStack stack, void* data);

//出栈
void pop_SeqStack(SeqStack stack);

//返回栈顶
void* top_SeqStack(SeqStack stack);

//返回栈大小
int size_SeqStack(SeqStack stack);

//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack);

//销毁栈
void destroy_SeqStack(SeqStack stack);
  • seqStack.c
#include "seqStack.h"

struct SStack
{
	void* data[MAX];  //栈的数组

	int m_Size; //栈大小
};

//初始化栈
SeqStack init_SeqStack()
{
	struct SStack* myStack = malloc(sizeof(struct SStack));

	if (myStack == NULL)
	{
		return NULL;
	}

	//初始化数组
	memset(myStack->data, 0, sizeof(void*) * MAX);

	//初始化栈大小
	myStack->m_Size = 0;

	return myStack;
}
//入栈
void push_SeqStack(SeqStack stack, void* data)
{
	//入栈本质  --- 数组尾插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;
	if (mystack->m_Size == MAX)
	{
		return;
	}

	mystack->data[mystack->m_Size] = data;

	mystack->m_Size++;
}
//出栈
void pop_SeqStack(SeqStack stack)
{
	//出栈本质  --- 数组尾删
	if (stack == NULL)
	{
		return;
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return;
	}

	mystack->data[mystack->m_Size - 1] = NULL;

	mystack->m_Size--;

}
//返回栈顶
void* top_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return NULL;
	}
	return mystack->data[mystack->m_Size - 1];
}
//返回栈大小
int size_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}

	struct SStack* mystack = stack;

	return mystack->m_Size;

}
//判断栈是否为空
int isEmpty_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return -1;//返回-1代表真  空栈
	}

	struct SStack* mystack = stack;

	if (mystack->m_Size == 0)
	{
		return 1;
	}

	return 0; //返回0 代表 不是空栈

}
//销毁栈
void destroy_SeqStack(SeqStack stack)
{
	if (stack == NULL)
	{
		return;
	}

	free(stack);
	stack = NULL;
}
  • 栈的顺序存储.c
# include "seqStack.h"
// 测试
struct Person
{
	char name[64];
	int age;
};

void test01()
{
	//初始化栈
	SeqStack myStack = init_SeqStack();

	//创建数据
	struct Person p1 = { "aaa", 10 };
	struct Person p2 = { "bbb", 20 };
	struct Person p3 = { "ccc", 30 };
	struct Person p4 = { "ddd", 40 };
	struct Person p5 = { "eee", 50 };

	//入栈
	push_SeqStack(myStack, &p1);
	push_SeqStack(myStack, &p2);
	push_SeqStack(myStack, &p3);
	push_SeqStack(myStack, &p4);
	push_SeqStack(myStack, &p5);

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	while (isEmpty_SeqStack(myStack) == 0)
	{
		struct Person* p = top_SeqStack(myStack);
		printf("姓名:%s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_SeqStack(myStack);
	}

	printf("栈的元素个数为:%d\n", size_SeqStack(myStack));

	//销毁栈
	destroy_SeqStack(myStack);
}

int main() {
	test01();

	return 0;
}

四、栈的链式存储

  • 初始化

//节点结构体
struct stackNode {
	struct stackNode* next;

};

//栈的结构体
struct LStack
{
	struct stackNode pHeader;
	int m_size;
};

typedef void* LinkStack;

//初始化
LinkStack init_LinkStack() {
	struct LStack* myStack = malloc(sizeof(struct LStack));

	if (myStack == NULL)
	{
		return NULL;
	}
	myStack->pHeader.next = NULL;
	myStack->m_size = 0;

	return myStack;
}
  • 入栈
//入栈
void push_LinkStack(LinkStack stack, void * data)
{
	// 入栈本质 链表头插
	if (stack == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	struct LStack* mystack = stack;

	//将用户数据 取出前4字节用
	struct stackNode* New = data;

	//更新链表指向
	New->next = mystack->pHeader.next;
	mystack->pHeader.next = New;

	//更新链表长度
	mystack->m_size++;
}
  • 出栈
//出栈
void pop_LinkStack(LinkStack stack)
{
	//出栈本质链表头删
	if (stack == NULL)
	{
		return;
	}
	
	struct LStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return;
	}

	//更新指针指向  缓存第一个有数据的节点
	struct stackNode* pFirst = mystack->pHeader.next;

	mystack->pHeader.next = pFirst->next;

	//更新栈大小
	mystack->m_size--;

  • 返回栈顶元素
// 返回栈顶元素
void* top_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return NULL;
	}

	struct LStack* mystack = stack;
	if (mystack->m_size == 0)
	{
		return NULL;
	}
	
	return mystack->pHeader.next;
}
  • 返回栈的个数
// 返回栈的个数
int size_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	
	struct LStack* mystack = stack;

	return mystack->m_size;
}
  • 判断栈是否为空
//判断栈是否为空
int isEmpty_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return -1;
	}
	struct LStack* mystack = stack;

	if (mystack->m_size == 0)
	{
		return 1;
	}

	return 0;
}
  • 销毁
//销毁
void destroy_LinkStack(LinkStack stack)
{
	if (stack == NULL)
	{
		return;
	}
	free(stack);
	stack = NULL;
}
  • 测试代码:
//测试
struct Person
{
	void* node;
	char name[64];
	int age;
};


void test01()
{
	//初始化栈
	LinkStack myStack = init_LinkStack();

	//创建数据
	struct Person p1 = { NULL,"aaa", 10 };
	struct Person p2 = { NULL,"bbb", 20 };
	struct Person p3 = { NULL,"ccc", 30 };
	struct Person p4 = { NULL,"ddd", 40 };
	struct Person p5 = { NULL,"eee", 50 };

	//入栈
	push_LinkStack(myStack, &p1);
	push_LinkStack(myStack, &p2);
	push_LinkStack(myStack, &p3); 
	push_LinkStack(myStack, &p4);
	push_LinkStack(myStack, &p5);

	printf("链式存储-- 栈的元素个数为: %d\n", size_LinkStack(myStack));

	while (isEmpty_LinkStack(myStack) == 0)	//栈不为空, 查看栈顶元素,出栈
	{
		struct Person* p = top_LinkStack(myStack);
		printf("姓名: %s 年龄:%d\n", p->name, p->age);

		//出栈
		pop_LinkStack(myStack);
	}

	printf("链式存储-- 栈的元素个数为: %d\n", size_LinkStack(myStack)); 

	destroy_LinkStack(myStack);

}

在这里插入图片描述


五、栈的应用案例-就近分配

几乎所有的编译器都具有检测括号是否匹配的能力,那么如何实现编译器中的符号成对检测?
如下字符串: 5+5*(6)+9/3*1)-(1+3(

算法思路
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空

#include "seqStack.h"

/*
从第一个字符开始扫描
当遇见普通字符时忽略,
当遇见左括号时压入栈中
当遇见右括号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
*/

int isLeft(char ch)
{
	return ch == '(';
}

int isRight(char ch)
{
	return ch == ')';
}

void printError(char* str, char* errMsg, char* pos)
{
	printf("错误信息:%s\n", errMsg);
	
	printf("%s\n", str);
	//计算打印空格数量
	int num = pos - str;
	for (int i = 0; i < num; i++)
	{
		printf(" ");
	}
	printf("^\n");

}

void test01()
{
	char* str = "5+5*(6)+9/3*1-(1+3(";

	char* p = str;

	//初始化栈
	SeqStack myStack = init_SeqStack();

	while (*p != '\0')
	{
		//如果是左括号,入栈
		if (isLeft(*p))
		{
			//入栈
			push_SeqStack(myStack,p); 
		}

		//如果是又括号
		if (isRight(*p))
		{
			//栈中有元素 出栈
			if (size_SeqStack(myStack) > 0)
			{
				pop_SeqStack(myStack);
			}
			else
			{
				//右括号没用匹配到对应的左括号,立即停止,并报错
				printError(str, "右括号没有匹配到对应的左括号!", p);
				break;
			}
		}
		p++;
	}

	//遍历结束 判断是否有 左括号没有匹配到对应的右括号
	while (size_SeqStack(myStack) > 0)
	{
		printError(str, "左括号没有匹配到对应的右括号!", top_SeqStack(myStack));
		//出栈
		pop_SeqStack(myStack);
	}

	//销毁栈
	destroy_SeqStack(myStack);
	myStack = NULL;
}


int main()
{

	test01();
	return 0;
}

总结
当需要检测成对出现但又互不相邻的事物时可以使用栈“后进先出”的特性
栈非常适合于需要“就近匹配”的场合


六、 中缀表达式转后缀表达式以及基于后缀表达式运算

中缀转后缀算法:
遍历中缀表达式中的数字和符号:
对于数字:直接输出
对于符号:
左括号:进栈
运算符号:与栈顶符号进行优先级比较
若栈顶符号优先级低:此符号进栈
(默认栈顶若是左括号,左括号优先级最低)
若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
右括号:将栈顶符号弹出并输出,直到匹配左括号,将左括号和右括号同时舍弃
遍历结束:将栈中的所有符号弹出并输出
实例
5 + 4 => 5 4 +
1 + 2 * 3 => 1 2 3 * +
8 +( 3 – 1 ) * 5 => 8 3 1 – 5 * +

计算规则
遍历后缀表达式中的数字和符号
对于数字:进栈
对于符号:
从栈中弹出右操作数
从栈中弹出左操作数
根据符号进行运算
将运算结果压入栈中
遍历结束:栈中的唯一数字为计算结果
例如:8 3 1 – 5 * +


总结

到这里这篇文章的内容就结束了,谢谢大家的观看,如果有好的建议可以留言喔,谢谢大家啦!

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值