数据结构之栈
栈,实际上是一种“后进先出”的思想,它本身可以是一个数组,或是一条链表!
对于学了顺序表,和链表的我们,它并不是什么新奇玩意,仅仅只是一种思想!
ok,接下来开始介绍,它的思想,首先我们用一个例子,来让大家有一个概念。
堆书例子
首先是一堆我们堆起来的书,一本一本堆起来(相当于入栈)
好接下来,我要是想拿橙色的书该怎么办呢?
它们堆起来了,我不好直接拿,我只能从上面一个一个拿出来,最后拿到橙色的书
(相当于出栈)
ok,接下来我来解释,这样的操作
入栈
首先,我们一本一本堆书
这个过程相当于就是,入栈的过程,
一个元素一个元素的入栈,
也就是一本书一本书上堆
这个就是入栈的概念
入栈一个元素,就是入栈一次
所以我们堆书的过程就是入栈5次
出栈
当我们想取橙书的时候
我们就得把压着橙书的其他书本给挪开
假设我们只能一本一本挪开
我们一本一本挪开的操作
实际上就是出栈
元素需要从栈顶一个一个往外出
出栈一个元素,就是出栈一次
所以我们取橙书的过程就是出栈3次
为什么是后进先出
我们发现取橙书的时候
先把蓝书拿出开了,但是蓝书是我们最后一个(正数第5)堆上去的
再把绿书拿出来了,但是绿树是我们倒数第二个(正数第4)堆上去的
再把橙书拿出开了,但是橙书是我们倒数第三个(正数第3)堆上去的
我们发现,后堆上去的反而先拿出来了,所以栈是一种“后进先出”的思想!
代码环节
stack.h文件
为什么是一种思想,通过最后两个打印函数就可以体会到!
(我们的思想是“后进先出”但打印明显违背了这种思想,所以属于非常规操作,只是为了方便我查看栈中的各个元素)
这里采用的是链式栈,双向链表!
#ifndef __STACK_H__
#define __STACK_H__
#include<stdio.h>
#include<stdlib.h>
typedef int Elemtype; //栈的元素类型
//构建栈的结点类型(相当于链表的结点类型)
typedef struct node
{
Elemtype data;
struct node * above;//指向上面元素的指针(相当于next)
struct node * below;//指向下面元素的指针(相当于prev)
}Node;
//(相当于链表的头结点)
typedef struct stack
{
int node_num;//栈中的结点个数
Node * top;//指向栈顶元素的指针(相当于尾指针)
Node * bottom;//指向栈底元素的指针(相当于头指针)
}Stack;
Stack* stack_init(void); //创建栈(栈初始化)(头结点初始化)
Node* node_init(Elemtype d); //创建结点(结点初始化)
int is_empty(Stack* s); //判断栈是否为空
int Push(Stack* s, Node* p); //结点入栈
int push(Stack* s, Elemtype d); //元素入栈
int pop(Stack* s); //元素出栈
void printf_Stack(Stack* s); //从栈底往栈顶打印
void printf_Stack1(Stack* s); //从栈顶往栈底打印
#endif // !__STACK_H__
stack.c文件
初始化栈(相当于双向链表头结点)
/*
* 初始化栈
*/
Stack* stack_init(void)
{
Stack* s = (Stack*)malloc(sizeof(Stack));
s->node_num = 0;
s->bottom = NULL;
s->top = NULL;
return s;
}
初始化结点
/*
* 初始化结点
*/
Node* node_init(Elemtype d)
{
Node* p = (Node*)malloc(sizeof(Node));
p->data = d;
p->above = NULL;
p->below = NULL;
return p;
}
判断栈是否为空
/*
* 判断栈是否为空
* 为空返回0,否则返回1
*/
int is_empty(Stack* s)
{
if (s == NULL || s->bottom == NULL) //栈不存在或者栈为空
return 0;
return 1;
}
元素入栈(相当于尾插法)
* 元素入栈
* 传入参数:第一个参数:Stack * 类型,传入栈
* 第二个参数:Elemtype 类型,入栈的元素
* 返回值:int 类型,入栈成功返回 1,失败返回 -1
*/
int push(Stack* s, Elemtype d)
{
if (!s) //栈不存在
{
printf("栈不存在,入栈失败\n");
return -1;
}
Node* p = node_init(d);
if (s->bottom == NULL)//从无到有
{
s->top = p;
s->bottom = p;
}
else//从少到多
{
p->below = s->top;
s->top->above = p;
s->top = p;
}
s->node_num++;
return 1;
}
结点入栈(相当于尾插法)
/*
* 结点入栈
* 传入参数:第一个参数:Stack * 类型,传入栈
* 第二个参数:Node * 类型,入栈的结点
* 返回值:int 类型,入栈成功返回 1,失败返回 0
*/
int Push(Stack* s, Node* p)
{
if (!p || !s)//结点不存在 || 栈未创建成功
return 0;
if (s->bottom == NULL)//从无到有
{
s->top = p;
s->bottom = p;
}
else//从少到多
{
p->below = s->top;
s->top->above = p;
s->top = p;
}
s->node_num++;
return 1;
}
元素出栈(相当于尾删)
/*
* 元素出栈
* 传入参数:第一个参数:Stack * 类型,传入栈
* 第二个参数:Elemtype * 类型,用于记录删除的结点的数据域
* 返回值:int 类型,出栈成功返回 1,失败返回 0
*/
Elemtype pop(Stack* s)
{
if (!is_empty(s))
return 0;
Node* p = s->top;
Elemtype d = p->data;
if (s->bottom->above == NULL)//删完该元素,栈空的情况,即栈底的上面为空
{
s->top = NULL;
s->bottom = NULL;
}
else//删其他元素的情况
{
s->top = s->top->below;
s->top->above = NULL;
}
p->above = NULL;
p->below = NULL;
free(p);
s->node_num--;
return d;
}
打印栈(栈底到栈顶!)
/*
* 打印栈
* 从栈底往栈顶打印
* 参数:第一个参数:Stack * 类型,传入栈
* 返回值:void
*/
void printf_Stack(Stack* s)
{
printf("栈中有%d个结点\n", s->node_num);
if (!s || !(s->top))
{
printf("栈空!\n");
return;
}
Node* p = s->bottom;
while (p)
{
printf("%d ", p->data);
p = p->above;
}
putchar('\n');
}
打印栈(栈顶到栈底)
/*
* 打印栈
* 从栈顶往栈底打印
* 参数:第一个参数:Stack * 类型,传入栈
* 返回值:void
*/
void printf_Stack1(Stack* s)
{
printf("栈中有%d个结点\n", s->node_num);
//if (!s || !(s->top))
//{
// printf("栈空!\n");
// return;
//}
Node* p = s->top;
while (p)
{
printf("%d ", p->data);
p = p->below;
}
putchar('\n');
}
若是对单链表熟悉,而对双向链表不太清楚,可以先看我的双向链表博客
附上链接:数据结构之双向链表
若是对单链表不熟悉可先观看我的单链表博客
附上链接:数据结构之单链表
以上就是栈的内容,希望对大家有所帮助,文章中出现问题,或者对文章有更好的介
意的宝子们记得提出来哦,感谢观看。