【数据结构-C语言】顺序栈,链栈

我们之前已经学习过顺序表,单向链表,单向循环链表,双向循环链表,下一个要学习的就是栈了。

1、基本概念

栈式一种逻辑结构,是特殊的线性表,特殊在:只能在固定的一段操作

只要满足上述条件,那么这种特殊的线性表就会呈现一种“先进后出”的逻辑,这种逻辑就是被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们等。

 由于约定了只能在线性表固定的一段进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,起了下面这些特定的名称:

  • 栈顶:可以进行插入删除的一端
  • 栈底:栈顶的对端
  • 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
  • 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
  • 取栈顶:取得栈顶元素,但不出栈,函数名通常为top()

基于这种固定一端的操作的简单约定,栈获得了”后进先出“的基本特性,最后一个放入的元素,最先被拿出来:

2、顺序栈存储形式

栈只是一种数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。

顺序栈:

顺序栈存储意味着开辟一块连续的内存来存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要配互斥锁和信号量等信息,为了便于管理,通常将这些信息统一在一个管理结构体之中,其示意图为:

//管理结构体的定义
typedef struct seqStack
{
    datatype* data;  //顺序栈入口,通用数据类型
    int size;   //顺序栈总容量
    int top;    //顺序栈栈顶元素下标
}seqStack;

3、 顺序栈的代码实现

先需要初始化一个顺序栈,在执行入(压)栈动作或出(弹)栈动作之前,需要对顺序栈进行栈的空后者满进行一次判断,栈空则无法进行出栈,栈满则无法进行入栈。

//初始化空栈
seqStack* init(int size)        //size为顺序栈的总容量大小
{
    seqStack* s = malloc(sizeof(seqStack));
    if(s != NULL)
    {
        s->data = malloc(sizeof(datatype) * size);
        if(s->data == NULL)
        {
            free(s);
            return NULL;
        }

        s->size = size;
        s->top = -1;        //栈顶元素的下标应该为0
    }
    
    return s;
}

//判断栈是否为空
bool isEmpty(seqStack* s)
{
    return s->top == -1;       //栈顶元素的下标为-1时则证明栈空    
}


//判断栈是否为满
bool isFull(seqStack* s)
{
    return s->top == s->size - 1;     //如果栈顶元素的下标等于栈的总容量-1则证明栈满(栈的首个元素的下标为0)      
}

//入栈(压栈)
bool push(seqStack* s,datatype data)
{
    //判断栈是否为满
    if(isFull(s))
    {
        return false;
    }

    //++top保证出栈的数据放在栈顶
    s->data[++s->top] = data;
   
    return true;
}

//出栈(弹栈)
bool pop(seqStack* s,datatype data)
{
    //判断栈是否为空
    if(isEmpty(s))
    {
        return false;
    }

    //栈顶元素下标-1,最后一个元素出来的时候栈顶元素下标为-1
    s->top--;

    return true;
}

但是,在入栈和出栈动作执行完之后需要检验自己的动作是否执行成功,所以就很有必须必要写一个取栈顶元素的功能函数

//取栈顶元素
bool top(seqStack* s,datatype* pm)
{
    //判断栈是否为空
    if(isEmpty(s))
    {
        return false;
    }
    
    //取栈顶元素下标所在的数据
    *pm = s->data[s->top];

    return true;
}

4、顺序栈的主函数测试代码

这里的主函数只是给予一种测试的思路,具体测试需要自己调试

int main()
{
	//初始化一个空的顺序栈
	seqStack* s = initStack(10);
	if (s == NULL)
	{
		printf("初始化顺序栈失败!\n");
	}
	else
		printf("初始化顺序栈成功!\n");

	push(s, 3);
	push(s, 4);
	int m;
	top(s, &m);
	printf("当前的栈顶元素是:%d\n",m);
	push(s, 5);

	while (!isEmpty(s))
	{
		int m;
		pop(s, &m);
		printf("%d\n", m);
	}

	return 0;
}

5、链栈存储形式

链式栈:

链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一段。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息

//链式栈节点
typedef struct node
{
    
    //数据域
    datatype data;
        
    //指针域
    struct node* next;
  
}node;

//链式栈管理结构体
typedef struct linkStack
{
    node* top;    //链式栈栈顶指针
    int size;    //链式栈当前元素个数
}linkStack;

6、链栈代码实现

需要的基本函数接口和顺序栈差不多

//初始化空栈
linkStack* initStack()
{
    linkStack* s = malloc(sizeof(linkStack));
    if(s != NULL)
    {
        s->size = 0;
        s->top = NULL;
    }

    return ture;
}

//判断栈是否为空
bool isEmpty(linkStack* s)
{
    return s->size == 0;
}

//创建栈的新节点
node* newNode(datatype data)
{
    node* new = calloc(1,sizeof(node));
    if(new != NULL)
    {
        new->data = data;
        new->next = NULL;
    }
    
    return new;
}

//入栈
bool push(linkStack* s,node* new)
{
    new->next = s->top;    //新节点成为栈顶元素,原来的栈顶元素作为新节点的下一个节点
    s->top = new;          //新节点成为栈顶元素
    s->size++;             //栈的元素个数+1
}

//出栈
node* pop(linkStack* s)
{
    node* p = top(s);    //获取栈顶元素

    if(isEmpty(s))
    {
        return false;
    }
        
    //更新栈
	s->top = s->top->next;
	s->size--;
	p->next = NULL;
	return p;
}

//取栈顶元素
bool top(linkStack* s,datatype* pm)
{
    //判断栈是否为空
    if(isEmpty(s))
    {
        return false;
    }

    //pm保存栈顶元素的数据域里面的数据
    *pm = s->top->data; 
   
    return true;
}

7、链栈主函数测试代码

这里的主函数只是给予一种测试的思路,具体测试需要自己调试

int main()
{
	//初始化一个空的链式栈
	linkstack* s = initStack();

	if (s)
	{
		printf("初始化空链栈成功!\n");
	}

	push(s, newNode(3));
	push(s, newNode(4));
	push(s, newNode(5));

	node* p;
	if(p = top(s))
		printf("%d\n",p->data);

	p = pop(s);
	printf("%d\n", p->data);
	free(p);

	printf("%d\n",top(s)->data);
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值