学习笔记4:栈的实现和代码

前言

自己学习数据结构的笔记,看的是王道的视频,总结了栈的基本概念,代码实现,实际应用。写了能跑的源代码,有详细的注释。希望对大家学习数据结构有所帮助。

一、栈的基本概念

1.栈是只允许在一端进行插入和删除操作的线性表

2.栈顶:线性表允许进行插入和删除操作的一端

3.栈尾:不允许进行插入和删除的另一端

4.空栈:不含任何元素的空表

5.栈的特点:后进先出

二、顺序栈

1.顺序栈的定义和初始化

//顺序栈的定义 
#define MaxSize 50
typedef struct {
	int data[MaxSize];	//用静态数组的方式存储栈 
	int top;			//用top表示数组下标的位置,可看做一个指针(并不是真的指针) 
}SqStack;


//初始化 
void InitStack(SqStack& S) { 
	S.top = -1;			//栈顶指针指向-1,表示栈空 
}


//判空 
bool StackEmpty(SqStack S) {
	if (S.top == -1) {	//判满操作为:S.top == MaxSize-1 
		return true;
	}
	return false;
}

2.顺序栈的进栈和出栈

//进栈 
bool Push(SqStack& S, int x) {
	if (S.top == MaxSize-1)		
		return false;
		
	S.top = S.top + 1;		//栈顶指针加1 
	S.data[S.top] = x;		//在数组下标为top的位置存放元素x 
	//上面两条语句可以简写为:S.data[++S.top] = x;
	return true;
}
 
 
//出栈 
bool Pop(SqStack& S, int& x) {
	if (S.top == -1) 
		return false;
	
	x = S.data[S.top];		//将数组下标为top位置的元素赋值给x 
	S.top = S.top - 1;		//栈顶指针减1 
	//上面两条语句可以简写为:x = S.data[S.top--];
	return true;
}

3.获取栈顶元素

//获取栈顶元素 
bool GetTop(SqStack S, int& x) {
	if (S.top == -1) 
		return false;
		
	x = S.data[S.top];
	return true;
}

4.共享栈

利用栈底位置相对不变的特性,可让两个顺序栈共享一个一位数组空间,将两个栈的栈顶分贝设置在共享空间的两端,两个栈顶向共享空间的中间延伸。

//共享栈的定义 
#define MaxSize 50
typedef struct {
	int data[MaxSize];	//用静态数组的方式存储栈 
	int top0;			//0号栈
    int top1;			//1号栈
}ShStack;

//初始化 
void InitStack(ShStack& S) { 
	S.top0 = -1;			//0号栈栈空
    S.top1 = MaxSize;		//1号栈栈空
}

三、链栈

链栈一般不带头结点 因为栈只对第一个节点进行操作,如果加上了头结点,在对头结点后面的结点也要进行操作 反而使算法复杂 所以设置头指针即可。因此这部分的代码实现都是不带头结点的。

1.链栈的定义和初始化

typedef struct LinkNode{
	int data;
	struct LinkNode *next;
}*LiStack,LinkNode;


//初始化
bool InitStack(LiStack &S){
	S == NULL;
	return true; 
}


//判空
bool StackEmpty(LiStack S) {
	if (S == NULL) {
		return true;
	}
	return false;
} 

2.进栈和出栈

//进栈
bool Push(LiStack &S,int x){ 
    
	//1.分配内存空间 
	LinkNode *p = (LinkNode)malloc(sizeof(LinkNode));
	if(p == NULL)
		return false;
	
	//2.进行进栈操作  
	p->data = x; 		//将x的值赋给p的数据域 
	p->next = S;	 	//让p的指针指向第一个元素 
	S = p;				//让头指针指向p 
	return true;
} 


//出栈操作
bool Pop(LiStack &S,int &x){
	if(StackEmpty(S))
		return false;
	
	//进行出栈操作	
	LinkNode *p = S;		//创建指针p指向要删除的节点 
	S = S->next;			//让头指针指向要删除节点的下一个节点 
	x = p->data;			//将要删除节点的数据通过x返回 
	free(p);				//将要删除的节点从链表中断开 
	return true;
}

3.获取栈顶元素

//获取栈顶元素
bool GetTop(LiStack S,int &x){
	if(StackEmpty(S))
		return false;
		
	x = S->data;
	return true;
}

四、栈的应用

1.括号匹配

用栈实现括号匹配:依次扫描所有字符,遇到左括号入栈,遇到右括号则弹出栈顶元素检查是否匹配

bool bracketCheck(char str[],int length){
	SqStack S;
	InitStack(S);		//初始化栈
	for(int i=0;i<length;i++){
		if(str[i]=='(' || str[i]== '[' || str[i]== '{') {
		Push(S,str[i]);	//扫描到左括号,入栈
		}else{
			if(StackEmpty(S))	//扫描到右括号且栈空
				return false;	//匹配失败
				
			char topElem;
			Pop(S,topElem);		//栈顶元素出栈
			if(str[i]== ')' && topElem != '(')
				return false 
			if(str[i]== ']' && topElem != '[')
				return false
			if(str[i]== '}' && topElem != '{')
				return false
		}
	}
	return (StackEmpty(S);		//检查完全部括号后,栈空说明匹配成功
}

2.表达式求值

2.1 中缀表达式转后缀表达式(手算)

① 确定中缀表达式中各个运算符的运算顺序

② 根据运算顺序,按照[左操作数 右操作数 运算符]的方式组合成一个新的操作数

③ 如果还有运算符未处理,重复②

左优先原则:优先使用左边的运算符

2.2 后缀表达式的计算(手算/机算)

手算方法:从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算,合并为一个操作数

机算方法:

① 从左往右扫描下一个元素,直到处理完所有元素

② 若扫描到操作数则压入栈,并回到①,否则执行③

③ 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①

注意事项: 先出栈的元素是右操作数

2.3 中缀表达式转前缀表达式

① 确定中缀表达式中各个运算符的运算顺序

② 根据运算顺序,按照[运算符 左操作数 右操作数]的方式组合成一个新的操作数

③ 如果还有运算符未处理,重复②

右优先原则:优先使用右边的运算符

2.4 前缀表达式的计算(手算/机算)

手算方法:从右往左扫描,每遇到一个运算符,就让运算符后最近的两个操作数执行对应运算,合并为一个操作数

机算方法:

① 从右往左扫描下一个元素,直到处理完所有元素

② 若扫描到操作数则压入栈,并回到①,否则执行③

③ 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①

注意事项: 先出栈的元素是左操作数

2.5 中缀表达式转后缀表达式(机算)

初始化一个栈,用于保存暂时还不能确定运算顺序的运算符

从左到右处理各个元素,直到末尾,可能遇到三种情况:

① 遇到操作数。直接加入后缀表达式

② 遇到界限符。遇到左括号时直接入栈;遇到右括号则依次弹出栈内运算符并加入后缀表达式,直到弹出左括号为止

注意事项:左括号不加入后缀表达式

③ 遇到运算符。依次弹出栈中和该运算符优先级相同或更高的所有运算符,并加入后缀表达式,若遇到左括号或栈空则停止。并将该运算符入栈。

按上述步骤处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

2.6 中缀表达式的计算(机算)

实现:中缀表达式转后缀表达式和后缀表达式求值两个算法相结合

初始化两个栈,分别存储操作数和运算符

若扫描到操作数,压入操作数栈

若扫描到运算符或界限符,按照2.5节②③操作压入运算符栈

每弹出一个运算符,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈

3.函数调用/递归调用

函数调用的特点:最后被调用的函数最先执行结束

函数调用时,需要用一个”函数调用栈“结构存储:调用返回地址,实参,局部变量等信息

递归调用时,”函数调用栈“可称为“递归工作栈”

每进入一层递归,就将递归调用所需的信息压入栈顶

每退出一层递归,就从栈顶弹出相应信息

五、源代码

顺序栈

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>


//顺序栈的定义 
#define MaxSize 50
 
typedef struct {
	int data[MaxSize];	//用静态数组的方式存储栈 
	int top;			//用top表示数组下标的位置,可以看做一个指针(并不是真的指针) 
}SqStack;


//函数声明 
void InitStack(SqStack &S);			//初始化栈 
bool StackEmpty(SqStack S);			//判空 
bool Push(SqStack& S, int x);		//进栈 
bool Pop(SqStack& S, int &x);		//出栈 
bool GetTop(SqStack S, int& x);		//获取栈顶元素 
void SqStackPrint(SqStack S);		//遍历顺序栈 


//主函数
int main(void) {
	SqStack S;
	InitStack(S);
	StackEmpty(S);
	Push(S,1); 
	Push(S,2);
	Push(S,3);
	Push(S,4);
	Push(S,5);
	SqStackPrint(S);
	int a=0,b=0; 
	GetTop(S,a); 
	int x=0,y=0,z=0; 
	Pop(S,x);
	Pop(S,y);
	Pop(S,z);
	SqStackPrint(S);
	GetTop(S,b);
    return 0; 
}


//初始化 
void InitStack(SqStack& S) { 
	S.top = -1;		//栈顶指针指向-1,表示栈空
	printf("顺序栈初始化成功!\n"); 
}
 

//判空 
bool StackEmpty(SqStack S) {
	if (S.top == -1) {		//判满操作为:S.top == MaxSize-1 
		return true;
		printf("顺序栈目前为空\n"); 
	}
	return false;
}
 
 
//进栈 
bool Push(SqStack& S, int x) {
	if (S.top == MaxSize-1)		
		return false;
		
	S.top = S.top + 1;		//栈顶指针加1 
	S.data[S.top] = x;		//在数组下标为top的位置存放元素x 
	//上面两条语句可以简写为:S.data[++S.top] = x;
	
	printf("元素%d进栈成功\n",x); 
	return true;
}
 
 
//出栈 
bool Pop(SqStack& S, int& x) {
	if (S.top == -1) 
		return false;
	
	x = S.data[S.top];		//将数组下标为top位置的元素赋值给x 
	S.top = S.top - 1;		//栈顶指针减1 
	//上面两条语句可以简写为:x = S.data[S.top--];
	
		printf("元素%d出栈成功\n",x); 
	return true;
}


//获取栈顶元素 
bool GetTop(SqStack S, int& x) {
	if (S.top == -1) 
		return false;
		
	x = S.data[S.top];
	
	printf("栈顶元素的值为:%d\n",x); 
	return true;
}


//遍历顺序栈
void SqStackPrint(SqStack S){
	printf("================\n"); 
	printf("顺序栈的遍历结果为:\n");
	while(S.top > -1){
		printf("%d ",S.data[S.top]);
		S.top--;
	} 
	printf("\n"); 
	printf("================\n"); 
}

链栈

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

//链栈的定义
typedef struct LinkNode{
	int data;
	struct LinkNode *next;
}*LiStack,LinkNode;


bool InitStack(LiStack &S);			//初始化栈 
bool StackEmpty(LiStack S);			//判空 
bool Push(LiStack& S, int x);		//进栈 
bool Pop(LiStack& S, int &x);		//出栈 
bool GetTop(LiStack S, int& x);		//获取栈顶元素 
void LiStackPrint(LiStack S);		//遍历链栈 


int main(void){
	LiStack S;
	InitStack(S);
	StackEmpty(S);
	Push(S,1); 
	Push(S,2);
	Push(S,3);
	Push(S,4);
	Push(S,5);
	LiStackPrint(S);
	int a=0,b=0; 
	GetTop(S,a); 
	int x=0,y=0,z=0; 
	Pop(S,x);
	Pop(S,y);
	Pop(S,z);
	LiStackPrint(S);
	GetTop(S,b);
	return 0; 
} 


//初始化
bool InitStack(LiStack &S) {
	S = NULL;
	printf("链栈初始化成功!\n"); 
	return true; 
}


//判空
bool StackEmpty(LiStack S) {
	if (S == NULL) {
		printf("顺序栈目前为空!\n"); 
		return true;
	}
	return false;
} 


//进栈
bool Push(LiStack &S,int x){ 

	//分配内存空间 
	LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
	if(p == NULL)
		return false;
	
	//进行进栈操作  
	p->data = x; 		//将x的值赋给p的数据域 
	p->next = S;	 	//让p的指针指向第一个元素 
	S = p;				//让头指针指向p 
	
	printf("元素%d进栈成功\n",x); 
	return true;
} 


//出栈操作
bool Pop(LiStack &S,int &x){
	if(StackEmpty(S))
		return false;
	
	//进行出栈操作	
	LinkNode *p = S;		//创建指针p指向要删除的节点 
	S = S->next;			//让头指针指向要删除节点的下一个节点 
	x = p->data;			//将要删除节点的数据通过x返回 
	free(p);				//将要删除的节点从链表中断开
	
	printf("元素%d出栈成功\n",x);  
	return true;
}


//获取栈顶元素
bool GetTop(LiStack S,int &x){
	if(StackEmpty(S))
		return false;
		
	x = S->data;
	
	printf("栈顶元素的值为:%d\n",x);
	return true;
	
}


//链栈表的遍历
void LiStackPrint(LiStack S){
	LinkNode *s = S;
	printf("==================\n");
	printf("链栈的遍历结果为:");  
 	if(s == NULL){
 		printf("栈空!\n");
	}

	 while(s != NULL){
	 	printf("%d ",s->data);
	 	s = s->next;
	}
	printf("\n");
	printf("==================\n");  
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值