限定性线性表——栈

  • 语言:C语言
  • 软件:Visual Studio 2022
  • 笔记书籍:数据结构——用C语言描述
  • 如有错误,感谢指正。若有侵权请联系博主

 一、栈的定义

栈作为一种限定性线性表,是将线性表的插入和删除操作限制为仅在表的一端进行,通常将表种允许进行插入、删除操作的一端称为栈顶(top),栈顶的当前位置是动态变化的,他是一个称为栈顶指针的位置指示器来指示;将表的另一端称为栈底。当栈中没有元素时称为空栈。栈的插入操作称为进栈或入栈,删除操作称为出栈或退栈。栈又称为后进先出的线性表。

栈的示意图如下所示:

ec9809e04fe4432e82470186f58e9db6.png

栈示意图

栈的基本操作除了进栈(栈顶插入)、出栈(删除栈顶)外,还有建立栈(栈的初始化)、判空、判满及取栈顶元素。

ADT Stack{
    数据对象:可以是任何类型的数据,但必须性质相同。
    结构关系:栈中数据元素之间是线性关系。
    基本操作:
    1.InitStack(S)
      操作前提:S为未初始化的栈。
      操作结果:将S初始化为空栈。
    2.ClearStack(S)
      操作前提:栈S已经存在。
      操作结果:将栈S置成空栈。
    3.IsEmpty(S)
      操作前提:栈S已经存在。
      操作结果:判栈S是否为空栈。若S为空栈,则返回TRUE,否则返回FALSE。
    4.IsFull(S)
      操作前提:栈S已经存在。
      操作结果:判栈S是否为满栈。若S栈已满,则返回TRUE,否则返回FALSE。
    5.Push(S,x)
      操作前提:栈S已经存在。
      操作结果:在S的顶部插入(亦称入栈)元素x。若S栈未满,将x插入栈顶位置,并返回TRUE,若栈已满,则返回FALSE,表示操作失败。
    6.Pop(S,x)
      操作前提:栈S已经存在。
      操作结果:删除(亦称出栈)栈S的顶部元素,并用x带回该值,返回TRUE;若栈为空,返回值为FALSE,表示操作失败。
    7.GetTop(S,x)
      操作前提:栈S已经存在。
      操作结果:取栈S的顶部元素赋给x所指向的单元,也称读栈。该操作与Pop(S,x)的不同之处在于,GetTop(S,x)不改变栈顶的位置。若栈为空,返回值为FALSE,表示操作失败。
}

栈作为一种特殊的线性表,在计算机中主要有两种基本存储结构:顺序存储结构和链式存储结构。采用顺序存储的栈简称为顺序栈,采用链式存储结构的栈简称为链栈。 

二、顺序栈的表示和实现

1、栈的存储表示

#define Stack_Size 50
typedef struct {
	StackElementType elem[Stack_Size];      //StackElementType为自己想使用的数据类型
	int top;
}SeqStack;

说明:顺序栈是用顺序存储结构实现的栈,即利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时由于栈的操作的特殊性,还必须附设一个位置指针top(栈顶指针)来动态地指示栈顶元素在顺序栈中的位置。通常以top=-1表示空栈。 

2、初始化栈

void InitStack(SeqStack* s) {
	s->top = -1;
}

 说明:构造一个空栈,给栈顶指针赋值-1。

3、将栈置为空栈

void ClearStack(SeqStack* s) {
	memset(s->elem, NULL, sizeof(s->elem));  //将数组中每一位置NULL
	s->top = -1;
	printf("栈已置空");
}

说明:memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值memset(void *s,int c,size_t n);

  • s指向要填充的内存块。
  • c是要被设置的值。
  • n是要被设置该值的字符数。
  • 返回类型是一个指向存储区s的指针。

4、判断是否为空栈

int IsEmpty(SeqStack s) {
	if (s.top == -1) return true;
	return false;
}

说明:通过判断栈顶指针是否为-1,从而判断是否是空栈。若空返回true,非空返回false。

5、 判断是否为满栈

int IsFull(SeqStack s) {
	if (s.top == (Stack_Size - 1)) return true;
	return false;
}

 说明:看栈顶指针是否到达数组开辟的大小,满足即满了,返回true,否则返回false。

6、入栈操作

int Push(SeqStack* s, StackElementType x) {
	if (s->top == (Stack_Size - 1)) return false;
	s->top++;
	s->elem[s->top] = x;
	return true;
}

 说明:入栈前,先判断栈有没有满,满了返回false。未满,将栈顶指针往上加一,将数据存储数组中加一的位置。

7、出栈操作

int Pop(SeqStack* s, StackElementType* x) {
	if (s->top == -1) return false;
	*x = s->elem[s->top];
	s->elem[s->top] = NULL;
	s->top--;
	return true;
}

说明:先判断是否为空栈,若是空栈返回false,否则,先将栈顶元素取出赋值给x,在将栈顶置空,栈顶指针往下移一位。

8、读栈顶操作

int GetTop(SeqStack* s, StackElementType* x) {
	if (s->top == -1) return false;
	*x = s->elem[s->top];
	return true;
}

 说明:只读栈顶,不改变栈顶,所以先判断是否为空栈,在将栈顶元素读出赋值给x。

9、顺序栈的全套代码

#include <iostream>
#include <stdio.h>
#define Stack_Size 50
#define StackElementType int

typedef struct {
	StackElementType elem[Stack_Size];
	int top;
}SeqStack;

/*初始化栈*/
void InitStack(SeqStack* s) {
	s->top = -1;
}
/*将栈置为空栈*/
void ClearStack(SeqStack* s) {
	memset(s->elem, NULL, sizeof(s->elem));  //将数组中每一位置NULL
	s->top = -1;
	printf("栈已置空");
}
/*判断栈是否为空栈*/
int IsEmpty(SeqStack s) {
	if (s.top == -1) return true;
	return false;
}
/*判断栈是否为满栈*/
int IsFull(SeqStack s) {
	if (s.top == (Stack_Size - 1)) return true;
	return false;
}
/*入栈操作*/
int Push(SeqStack* s, StackElementType x) {
	if (s->top == (Stack_Size - 1)) return false;
	s->top++;
	s->elem[s->top] = x;
	return true;
}
/*出栈操作*/
int Pop(SeqStack* s, StackElementType* x) {
	if (s->top == -1) return false;
	*x = s->elem[s->top];
	s->elem[s->top] = NULL;
	s->top--;
	return true;
}
/*读栈顶操作*/
int GetTop(SeqStack* s, StackElementType* x) {
	if (s->top == -1) return false;
	*x = s->elem[s->top];
	return true;
}

void SeqStack_text() {
	SeqStack S, * Seq;
	Seq = &S;
	int* s, x;
	s = &x;
	InitStack(Seq);
	printf("空栈返回1,非空返回0:%d\n", IsEmpty(S));
	for (int i = 0; i < 55; i++) {
		printf("第%d次操作结果,1为成功,0为失败:%d\n", i, Push(Seq, (i * 10)));
	}
	printf("满栈返回1,非满返回0:%d\n", IsFull(S));
	for (int i = 0; i < 30; i++) {
		Pop(Seq, s);
		printf("第%d次取出栈顶的值为:%d\n", i, *s);
	}
	printf("空栈返回1,非空返回0:%d\n", IsEmpty(S));
	for (int i = 0; i < 5; i++) {
		GetTop(Seq, s);
		printf("第%d次取出栈顶的值为:%d\n", i, *s);
	}
	printf("满栈返回1,非满返回0:%d\n", IsFull(S));
	printf("清空操作\n");
	ClearStack(Seq);
	printf("空栈返回1,非空返回0:%d", IsEmpty(S));
}

 10、测试结果

b84ee69149fe4a3795e53a1e47986bda.png

85324b01dff14ec7bb68af05fef5bda5.png

三、链栈的表示和实现

1、链栈的存储表示

#define StackElementType int

typedef struct NodeStack {
	struct NodeStack* next;
	StackElementType data;
}NodeStack,*NodeS;

说明:StackElementType用来替换int,若想换成其他类型,将int换了,printf里输出占位符也换即可。struct后边是结构体名称,typedef是代表这个结构体可以用大括号后边的也就是NodeStack来代替。NodeS表示定义一个结构体指针,用于指向单链表的头指针变量,便于为头指针变量开辟空间。

2、初始化链栈

void InitStack(NodeS* S) {
	*S = (NodeStack*)malloc(sizeof(NodeStack));
	(*S)->next = NULL;
	printf("初始化完成");
}

说明:通过结构体指针将单链表的头指针变量传入函数中,malloc为头指针变量开辟空间,它的用法和顺序栈开辟空间是一样的。第三行是将开辟空间的头结点的指针域赋值为NULL。printf函数可以不要,仅仅是为了看代码运行情况。

3、将栈置为空栈

int ClearStack(NodeS S) {
	if (S->next == NULL) return 0;
	NodeStack* r,*p;
	r = S->next;
	while (r != NULL) {
		p = r;
		r = r->next;
		free(p);
	}
	S->next = NULL;
}

说明:传入的是一个头指针变量,相当于NodeStack* S。先判断栈是否为空,即判断头结点的指针域是否为空。在定义两个结构体指针,一个用于指向结点,一个用于存储要释放的空间。while遍历循环释放空间,用free释放空间(有malloc出现的地方就要记得去释放空间)。最后将头结点置空。

4、判断是否为空栈

int IsEmpty(NodeS S) {
	if (S->next == NULL) return true;
	return false;
}

说明:即判断头节点的指针域是否为空即可。

5、判断是否为满栈

链表只要电脑内存足够就可以一直存数据。

6、入栈操作

int Push(NodeS S, StackElementType e) {
	NodeStack* r = (NodeStack*)malloc(sizeof(NodeStack));
	if (r == NULL) return 0;
	r->data = e;
	r->next = S->next;
	S->next = r;
	return 1;
}

说明:开辟一个新的空间作为新的一个结点。判断开辟的空间是否为空,若为空证明计算机空间全用完了,没法开辟新的空间,返回入栈0。否则,将函数传入的数据存入新开辟的结点中的数据域中,再将头结点指针域的数据传给新开辟的结点的指针域。最后将新开辟的结点存入头结点的指针域。

7、出栈操作

int Pop(NodeS S, StackElementType* e) {
	if (S->next == NULL) return false;
	NodeStack* r;
	r = S->next;
	*e = r->data;
	S->next = S->next->next;
	free(r);
	return true;
}

说明:先判断栈是否为空栈,定义一个结构体指针,用于存放栈顶结点(即首元结点)。将首元结点赋值给定义的r,将首元结点数据赋值给参数e,将首元结点的指针域传给头结点。至此,栈顶结点(首元结点)的数据全拿出来了,最后将栈顶结点空间释放(防止内存泄漏)。

8、读栈顶操作

int GetTop(NodeS S, StackElementType* e) {
	if (S->next == NULL) return false;
	*e = S->next->data;
	return true;
}

说明:读栈顶,只读不取出,故只要访问栈顶(首元结点的数据域)即可。先判断栈是否为空,再去取数据。

9、链栈的全套代码

#include <iostream>
#include <stdio.h>
#define StackElementType int

typedef struct NodeStack {
	struct NodeStack* next;
	StackElementType data;
}NodeStack,*NodeS;

/*初始化栈*/
void InitStack(NodeS* S) {
	*S = (NodeStack*)malloc(sizeof(NodeStack));
	(*S)->next = NULL;
	printf("初始化完成");
}

/*将栈置为空栈*/
int ClearStack(NodeS S) {
	if (S->next == NULL) return 0;
	NodeStack* r,*p;
	r = S->next;
	while (r != NULL) {
		p = r;
		r = r->next;
		free(p);
	}
	S->next = NULL;
}

/*判断栈是否为空栈*/
int IsEmpty(NodeS S) {
	if (S->next == NULL) return true;
	return false;
}

/*入栈操作*/
int Push(NodeS S, StackElementType e) {
	NodeStack* r = (NodeStack*)malloc(sizeof(NodeStack));
	if (r == NULL) return 0;
	r->data = e;
	r->next = S->next;
	S->next = r;
	return 1;
}

/*出栈操作*/
int Pop(NodeS S, StackElementType* e) {
	if (S->next == NULL) return false;
	NodeStack* r;
	r = S->next;
	*e = r->data;
	S->next = S->next->next;
	free(r);
	return true;
}

/*读栈顶操作*/
int GetTop(NodeS S, StackElementType* e) {
	if (S->next == NULL) return false;
	*e = S->next->data;
	return true;
}

/*打印栈表*/
void PrintStack(NodeS S) {
	NodeStack* r;
	r = S->next;
	while (r != NULL) {
		printf("%d  ", r->data);
		r = r->next;
	}
}

void IntStack(NodeS S) {
	int i,e;
	printf("入栈前栈中的数据:");
	PrintStack(S);
	printf("\n你要入多少个数据:");
	scanf_s("%d",&i);
	while (i--) {
		scanf_s("%d",&e);
		Push(S,e);
	}
	printf("入栈后数据:");
	PrintStack(S);
	printf("\n");
}

void StackText() {
	NodeS stack, * S;
	S = &stack;
	printf("-----------------初始化栈--------------\n");
	InitStack(S);
	printf("\n-----------------判断是否为空栈--------------\n");
	printf("空栈1,非空0:%d", IsEmpty(stack));
	printf("\n-----------------入栈操作--------------\n");
	IntStack(stack);
	printf("\n-----------------出栈操作--------------\n");
	StackElementType* e,en;
	e = &en;
	for (int i = 0; i < 3; i++) {
		Pop(stack, e);
		printf("第%d次出栈数据为:%d\n", i,*e);
	}
	printf("出栈后,栈中数据为:");
	PrintStack(stack);
	printf("\n-----------------读栈顶操作--------------\n");
	for (int i = 0; i < 3; i++) {
		GetTop(stack, e);
		printf("第%d次读栈顶数据为:%d\n", i,*e);
	}
	printf("\n-----------------将栈表置空--------------\n");
	printf("置空前,空栈1,非空0:%d\n", IsEmpty(stack));
	ClearStack(stack);
	printf("置空后,空栈1,非空0:%d\n", IsEmpty(stack));
}

int main() {
	StackText();
}

10、测试结果

dede1d8ec7414febbcbfccb6c33cc7f7.png

栈的具体使用

  • 33
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值