本文详细介绍了栈的顺序存储结构,包括初始化时栈顶指针指向-1和0的两种情况,以及如何实现进栈、出栈、读取栈顶元素等操作。同时,展示了共享栈的概念,以及栈在十进制转二进制和判断回文数问题中的应用。此外,还探讨了链栈的存储结构,包括带头结点和不带头结点的实现。最后,总结了链栈和顺序栈在时间和空间复杂度上的优缺点,并指出选择栈类型的原则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

定义

栈是一种受限的线性表,栈的逻辑结构任然是一对一的关系,但只能在栈顶进行插入和删除
特点:是先进去的最后出来

在这里插入图片描述

栈的顺序存储结构

初始化的时候栈顶指针指向-1

在这里插入图片描述

//栈
//顺序栈:此时top 指向 -1 
#include <stdio.h>
#include <stdlib.h> 
#define MaxSize 20
typedef struct{
	int data[MaxSize];//存储栈元素 
	int top;//栈顶指针:指向栈顶的 
}SqStack;
//初始化
bool InitStack(SqStack &s){
	s.top = -1;
	return true;
} 
//判断栈是否为空
bool StackEmpty(SqStack s){
	if(s.top == -1){
		return true;
	}else{
		return false;
	}
} 
//进栈
bool Push(SqStack &s,int e){
	if(s.top == MaxSize - 1){
		return false;
	}
	s.top += 1;
	s.data[s.top] = e;
	return true;
} 
//读取栈顶元素
bool GetTop(SqStack s,int &e){
	if(s.top == -1){
		return false; 
	}
	e = s.data[s.top];
	return true;
} 
//出栈
bool Pop(SqStack &s,int &e){
	if(s.top == -1){
		return false;
	}
	e = s.data[s.top];
	s.top -= 1;
	return true; 
} 
int main(){
	SqStack s;
	int e;
	int e1;
	int e2;
	if(InitStack(s)){
		printf("初始化成功\n");
		printf("请输入,入栈元素:");
		scanf("%d",&e);
		if(Push(s,e)){
			printf("添加成功\n");
		}else{
			printf("添加失败\n");
		}
		if(GetTop(s,e2)){
			printf("删除成功,此时的删除的元素为:%d\n",e2);
		}
		if(Pop(s,e2))
		if(StackEmpty(s)){
			printf("此时栈为空");
		}else{
			printf("此时栈不为空");
		}
	} 
	return 0;
}

初始化的时候栈顶指针指向0

在这里插入图片描述

//栈
//顺序栈:此时top 指向 0
#include <stdio.h>
#include <stdlib.h> 
#define MaxSize 20
typedef struct{
	int data[MaxSize];//存储栈元素 
	int top;//栈顶指针:指向栈顶的 
}SqStack;
//初始化
bool InitStack(SqStack &s){
	s.top = 0;
	return true;
} 
//判断栈是否为空
bool StackEmpty(SqStack s){
	if(s.top == 0){
		return true;
	}else{
		return false;
	}
} 
//进栈
bool Push(SqStack &s,int e){
	if(s.top == MaxSize){
		return false;
	}
	
	s.data[s.top] = e;
	s.top += 1;
	return true;
} 
//读取栈顶元素
bool GetTop(SqStack s,int &e){
	if(s.top == 0){
		return false; 
	}
	e = s.data[s.top - 1];
	return true;
} 
//出栈
bool Pop(SqStack &s,int &e){
	if(s.top == 0){
		return false;
	}
	s.top -= 1;
	e = s.data[s.top];
	
	return true; 
} 
int main(){
	SqStack s;
	int e;
	int e1;
	int e2;
	if(InitStack(s)){
		printf("初始化成功\n");
		printf("请输入,入栈元素:");
		scanf("%d",&e);
		if(Push(s,e)){
			printf("添加成功\n");
		}else{
			printf("添加失败\n");
		}
		if(GetTop(s,e2)){
			
		}
		if(Pop(s,e2)){
			printf("删除成功,此时的删除的元素为:%d\n",e2);
			
		}
		if(StackEmpty(s)){
			printf("此时栈为空");
		}else{
			printf("此时栈不为空");
		}
	} 
	return 0;
}

共享栈

出现的背景:一个栈我们必须提前声明她的空间大小,当对于两个相同类型的栈,我们却可以做到最大限度地利用空间,思路:是在数组的两端,向中间靠拢

//共享栈
#include <stdio.h>
#include <stdlib.h>
#define MaxSize 20
typedef struct{
	int data[MaxSize];
	int top1;//栈1的栈顶指针
	int top2;//栈2的栈顶指针
}SqStack; 
//初始化
bool InitSqStack(SqStack &s){
	s.top1 = -1;
	s.top2 = MaxSize;
} 

int main(){
	SqStack s;
	return 0;
}
//栈满的:s.top2 - s.top1 = 1 

栈的顺序存储的应用

十进制转二进制

#include <stdio.h>
#define MaxSize 20
typedef struct{
	int data[MaxSize];
	int top;
}Stack; 
//初始化
bool InitStack(Stack &s){
	s.top = -1;
	return true;
	
} 
//入栈
bool push(Stack &s,int e){
	if(s.top == MaxSize - 1){
		return false;
	}
	s.top += 1;
	s.data[s.top] = e;
	return true;
} 
//出栈
bool pop(Stack &s,int &e){
	if(s.top == -1){
		return false;
	}
	e = s.data[s.top];
	s.top -= 1;
	return true;
} 
//显示栈中的元素
int StackPrint(Stack s){
	if(s.top == -1){
		return 0;
	}
	while(s.top != -1){
		printf("%d",s.data[s.top]);
	    s.top -= 1;
	}
	
} 
int main(){
	Stack s;
	int sum;
	int e,i;
	if(InitStack(s)){
		printf("初始化成功\n");
		printf("请输入一个十进制的数:");
		scanf("%d",&sum);
		while(sum){
			e = sum % 2;//辗转相除法 
			push(s,e);//余数进栈 
			sum = sum / 2;
		}
		printf("对应的二进制:");
		StackPrint(s);
		
	}
	return 0;
}

回文数

//顺序栈的实际应用 
//判断是否为回文数:1221,12321
#include <stdio.h>
#define MaxSize 20
typedef struct{
	int data[MaxSize];
	int top;
}Stack;
//初始化
bool InitStack(Stack &s){
	s.top = -1;
	return true;
} 
//入栈
bool push(Stack &s,int e){
	if(s.top == MaxSize - 1){
		return false;
	}
	s.top += 1;//因为数组下标从0开始,而开始指针指向-1所以要+1 
	s.data[s.top] = e;
	return true; 
}
bool pop(Stack &s,int &e){
	if(s.top == -1){
		return false;
	}
	e = s.data[s.top];
	s.top -= 1;
	return true;
} 
int main(){
	Stack s;
	int sum;//回文数的个数
	int team[MaxSize];
	int i,e; 
	int temp = 0;
	if(InitStack(s)){
		printf("初始化成功\n");
		printf("请输入回文数的个数:");
		scanf("%d",&sum);
		printf("请输入:");
		for(i = 0;i < sum;i ++){
			scanf("%d",&team[i]);
		}
		
				for(i = 0;i < sum / 2;i ++){//一半的元素进栈 
					push(s,team[i]);//入栈 
				
				}
		if(sum % 2 == 0){
			i = 0;
			while(i < sum / 2){
				pop(s,e);
				if(e != team[sum / 2 + i]){
					temp = 1;
				}
				i ++;
			}
		}
		else{
			i = 0;
			
		
			while(i < sum / 2){
				pop(s,e);
				if(e != team[sum / 2 + i + 1]){
					temp = 1;
				}
				i ++;
			}
			
		}
	  if(temp == 1){
	  	printf("不是回文数");
	  }else{
	  	printf("是");
	  }
	
		
		
		
	}
	return 0;
}

栈的链式存储结构

对于链栈来说,不存在栈满的情况,除非内存已经没有可以使用的内存

带头结点

L1结点入栈
在这里插入图片描述
L2入栈
在这里插入图片描述
我们发现这个S结点并没有存放数据那就是头结点

#include <stdio.h>
#include <stdlib.h>
//带有头结点的链栈 
typedef struct StackNode{
	int data;
	struct StackNode *next;//我们会发现这个定义和单链表的定义是一样的 
}StackNode,*LinkStackPtr;
//初始化
bool InitStack(LinkStackPtr &s){
	
	s = (StackNode *)malloc(sizeof(StackNode));
	if(!s){
		return false;
	}
	s->next = NULL;//带有头结点的链栈 
	return true;
} 
//进栈
bool push(LinkStackPtr &s,int e){
	StackNode *L = NULL;
	L = (StackNode *)malloc(sizeof(StackNode));
	if(L == NULL){
		return false;
	}
	L->next=NULL;//将新入栈的结点的指针域设为空
	L->data = e;//将元素添加到申请的结点的数据域中
	L->next = s->next;//将新添加的栈元素放到栈顶那他的指针域就存放下一个结点 
	s->next =L; //将栈顶指针指向栈顶元素 
	return true;
	 
} 
//出栈
bool pop(LinkStackPtr &s,int &e){
	StackNode *p = NULL;
	if(s->next == NULL){
		return false;
	}
	p=s->next;//将s所指向的结点让p也指向他 
	e=p->data;
	s->next = p->next;
	free(p);
	return true;
} 

//打印栈元素
int StackPrint(LinkStackPtr s){
	StackNode *p = NULL;
	if(s->next == NULL){
		return 0;
	}

	p = s->next;
	while(p != NULL){
		printf("元素:%d\t",p->data);
		p=p->next;
	}
} 
int main(){
	LinkStackPtr s;
	int e;
	int i;
	if(InitStack(s)){
		printf("初始化成功\n");
		for(i = 0;i < 4;i ++){
				printf("请输入入栈元素:");
				scanf("%d",&e);
				if(push(s,e)){
			        printf("入栈成功\n");
		        }
		}
		for(i = 0;i < 4;i ++){
			pop(s,e);
			printf("出栈:%d\t",e);
			
		}	
		if(StackPrint(s) == 0){
			printf("链栈为空\n");
		}else{
			StackPrint(s);
		}
	
		
	}
	return 0;
}

不带头结点

#include <stdio.h>
#include <stdlib.h>
//不带头结点的链栈 
typedef struct StackNode{
	int data;
	struct StackNode *next;
}StackNode,*LinkStack;
//初始化
bool InitStack(LinkStack &s){
	s = NULL;
	return true;
} 
//入栈
bool push(LinkStack &s,int e){
	StackNode *L = NULL;
	L = (StackNode *)malloc(sizeof(StackNode));
	if(L == NULL){
		return false;
	}
	L->data = e;
	L->next = s;
	s = L;//栈顶指针指向栈顶元素
	return true;
	 
} 
//出栈
bool pop(LinkStack &s,int &e){
	if(s == NULL){
		return false;
	}
	StackNode *p = NULL;
	p = s;
	e = p->data;
	s = p->next;
	free(p);
	return true;
}
//打印栈中所有元素
int StackPrint(LinkStack s){
	if(s == NULL){
		return 0;
	}
	StackNode *p = NULL;
	p = s;
	while(p != NULL){
		printf("%d\t",p->data);
		p = p->next;
	}
} 
int main(){
	LinkStack s;
	int e;
	if(InitStack(s)){
		printf("初始化成功\n");
		printf("请输入要入栈的元素:\n");
		scanf("%d",&e);
		if(push(s,e)){
			printf("入栈成功\n");
		}
		if(pop(s,e)){
			printf("出栈元素:%d\t",e);
		}
		if(StackPrint(s)){
			StackPrint(s);
		}
	}
	return 0;
} 

总结

链栈和顺序栈:
时间复杂度:都为O(1)
空间复杂度:顺序栈需要提前分配空间,而链栈就比较灵活了通过指针可以有效利用内存中的空间
使用:当栈长度不固定使用链栈,当栈长度固定使用顺序栈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值