数据结构——栈

目录

一、什么是栈

二、栈的实现 

1、栈的结构

2、栈的初始化

3、栈的入栈

 4、栈的出栈

5、返回栈顶元素

6、栈是否为空

7、返回栈的元素个数

8、栈的打印


一、什么是栈

是一种特殊的线性表,只允许在固定的一端进行插入和删除操作。

进行数据插入和删除的一端叫做栈顶,另外一端称为栈底

入栈:向栈中进行插入的操作称为入栈/进栈/压栈,在栈顶入数据。

出栈:向栈中进行删除的操作称为出栈,在栈顶出数据。顶部元素不出去,下面的元素是出不去的

 可以看一下下列几道选择题,看看对栈是否理解透彻~ 

1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是(  )。

> A  12345ABCDE

> B  EDCBA54321

> C  ABCDE12345

> D  54321EDCBA

这道题答案很显然是B

 2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()

> 1,4,3,2

> B  2,3,4,1

> 3,1,4,2

> D  3,4,2,1

注意,栈是可以边入边出的,我入1之后,可以立刻把1出去。知道这个之后,对每一个选项进行模拟即可。

A选项:入1之后,立刻出1,然后入2、3、4,之后出栈,4被出出去,在出栈,3被出出去,在出栈,2被出出去,因此A是正确的

B选项:入1之后,入2,在立刻出2,然后入3出3,入4出4,最后出1,因此B也是正确的

C选项:入1、2之后,入3然后立刻出3,此时栈顶是2,要出栈只能把2出出去,不可能出到1,因此,答案选C

D选项:入1、2,之后入3出3,入4出4,在继续出栈,2、1被出出去。因此D也是正确的

二、栈的实现 

我们可以用一个顺序表,将顺序表的起始位置当做栈底,最后一个元素位置就是栈顶,入数据的时候就是尾插,出数据的时候就是尾删。

同样,我们也可以用链表,链表的第一个节点就是栈底,最后一个节点就是栈顶,入栈的时候直接尾插,出栈的时候直接尾删。但是,问题来了,链表的尾插是不是要找尾呢?是的呀,我们可以定义一个tail,记录尾部节点,这样尾插就好办了,但还有尾删的问题呀,尾删要找尾的前一个节点,怎么办呢?要用循环链表?是不是太麻烦了。但是顺序表呢?顺序表的尾插和尾删非常香。

因此,我们还是用顺序表来实现即可。

1、栈的结构

既然使用顺序表,那肯定是要有个数组的,数组肯定要有容量的,这里可以实现静态的也可以实现动态的。静态的就是数组的大小给死,但是很多情况下我们是不知道这个栈要存多少数据的,因此我们还是实现动态扩容版本的,既然是动态扩容,那我们扩容的依据是什么呢?是不是数据个数容量大小,如果数据个数 >= 容量的大小,说明数组满了,此时就要扩容,因此我们还要定义两个变量,一个是当前数据个数,一个是容量大小,既然有多个变量,我们可以用结构体存起来。

typedef int StDataType;

typedef struct stack
{
	StDataType* a;  
	int capicity;  // 当前栈的空间大小
	int top;	   //表示栈顶元素的下一个位置,同时也是当前栈的元素个数
}stack;

2、栈的初始化

定义了结构之后,首先要对它进行初始化,要做的工作就是给数组a开一点空间,给capicity和top赋予初始值。可以先用malloc给数组a开上4个空间,capicity就是4,这里top就表示栈顶元素的下一个位置,此时栈是空的,栈顶元素的下一个位置就是0,同时它还是当前栈的元素个数。空栈时,top就是0。

void StackInit(stack* st)
{
	st->a = (StDataType*)malloc(sizeof(StDataType) * 4);
	if (st->a == NULL)
	{
		perror("stack init fail");
		return;
	}
	st->capicity = 4;
	st->top = 0; 
}

3、栈的入栈

入栈就是在尾部插入数据。

函数头

     插入数据,你要告诉我,你要对哪一个栈进行插入,你要插入哪一个数 据,因此要有两个参数,要改变结构体使用结构体的指针即可,因此第一个参数是一个结构体指针,第二个参数就是要插入的数据,不需要返回值。

函数体

  1. 有没有可能我给你一个栈的指针是空,让你插入元素呢?有可能呀,栈指针都为空,我怎么插入数据呢?因此要先断言栈的指针不为空。
  2. 插入数据,元素个数是会增长的,因此空间容量就有可能不够,就需要扩容,因此要进行扩容的判断,如果当前元素个数等于容量,就用realloc进行扩容,更新capiticy
  3. 判断完前两部之后就可以进行插入,插入到哪一个位置呢?插入到尾部呀?尾部是哪里呀,是不是就是top呀?top就是栈顶元素的下一个位置。之后再让top++即可
void StPush(stack* st, StDataType x)
{
	assert(st);
	// 扩容
	if (st->top == st->capicity)
	{	
		StDataType* tmp = realloc(st->a, sizeof(StDataType) * st->capicity * 2);
		if (tmp == NULL)
		{
			perror("扩容失败");
			return;
		}
		st->a = tmp;
		st->capicity = st->capicity * 2;
	}

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

 4、栈的出栈

出栈就是在尾部删除数据。

函数头

        告诉我要出哪一个栈,因此要有栈指针,需不需要有返回值呢?可以有也可以没有,如果有,返回值是什么呢?既然是出栈,那当然就是栈顶元素咯。这里我们就不需要返回值了,因为C++的栈是没有返回值的。就跟C++一致吧,其实有返回值是挺好用的。

函数体

        函数体就在简单不过了,同样要进行断言,如果栈指针是空,或者栈里没有元素,那我出啥呢?顺序表尾删太easy了,直接将top--即可,访问不到就完事了。

5、返回栈顶元素

函数头

        同样告诉我返回哪一个栈的栈顶元素,不需要返回值。

函数体

       同样需要断言,如果栈为空,或者栈指针是空,根本返回不了栈顶元素,直接报错,top是栈顶元素的下一个位置,要返回栈顶直接返回top-1位置的元素即可。

StDataType StTop(stack* st)
{
	assert(st);
	assert(!StEmpty(st));
	return st->a[st->top - 1];
}

6、栈是否为空

如果栈的top为0,就是空

bool StEmpty(stack* st)
{
	assert(st);

	return st->top == 0;
}

7、返回栈的元素个数

top即为栈顶元素个数

int StSize(stack* st)
{
	assert(st);
	return st->top;
}

8、栈的打印

我们要如何打印一下栈里面的元素呢?由于栈的特性,因此我们只能从栈顶先获取栈顶元素,然后在把栈顶元素出栈,重复这个操作,直到栈为空。

while (!StEmpty(&st))
	{
		printf("%d\n", StTop(&st));
		StPop(&st);
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值