c数据结构--栈


概念:

栈(Stack)是一种先进后出(FILO,First in Last Out)的线性表,只允许在线性表的一端进行操作。

典型应用:

函数调用时,函数的上下文环境(返回地址,全局变量等),形参,是以栈这样的数据结构存储的。

基本操作:

1. 压栈

2. 出栈

3. 栈判空

4. 元素个数

5. 栈清空

6. 栈销毁

操作应用:

1. 进制转换

2. 括号匹配

3. 后缀表达式求值

4. 中缀转后缀

5. 迷宫问题求解


栈的实现方法:

方法1:静态顺序表

方法2:动态顺序表(高效)

方法3:只能头插头删的单链表


栈的定义(动态顺序表):

typedef struct Stack {
	//以数组地址为栈底指针
	DataType* _array;
	//栈顶指针
	int _top;
	//栈的大小
	unsigned int _capacity;
}*PStack;

初始化:

//初始化栈
void StackInit(PStack stack) {
	stack->_array = (DataType*)malloc(sizeof(DataType)*Init_StackSize);
	for (int i = 0; i < sizeof(stack->_array) / sizeof(stack->_array[0]); i++) {
		stack->_array[i] = 0;
	}
	if (!stack->_array) {
		exit(0);
	}
	stack->_top = 0;
	stack->_capacity = Init_StackSize;
}

基本操作:

1. 压栈


//压栈
void StackPush(PStack stack, DataType data) {
	assert(stack);
	//判断栈是否已满
	if (stack->_top >= stack->_capacity) {
		//申请10个空间
		stack->_array = (DataType*)realloc(stack->_array, 
			sizeof(DataType)*(stack->_capacity + 10));
		//判断是否申请成功
		if (!stack->_array) {
			exit(0);
		}
		stack->_top = stack->_capacity;
		stack->_capacity = stack->_capacity + 10;
	}

	*(stack->_array + stack->_top) = data;
	stack->_top++;
}




2. 出栈(pop的数据放在data里面)

//出栈
int StackPop(PStack stack, DataType* data) {
	assert(stack);
	//判断栈是否为空
	if (stack->_top == 0) {
		return 0;
	}
	//先减一再取值
	--stack->_top;
	*data =  *(stack->_array + stack->_top);
	
	return 1;
}


3. 栈判空

//判断栈是否为空
int StackEmpty(PStack stack) {
	assert(stack);
	//为空返回1
	return stack->_top == 0 ? 1 : 0;
}


4. 有效元素个数

//有效元素个数
int StackSize(PStack stack) {
	assert(stack);
	return stack->_top;
}

5. 栈清空

//清空栈,不进行物理删除
void ClearStack(PStack stack) {
	assert(stack);
	stack->_top = 0;
}


6. 栈销毁

//销毁栈
void dropStack(PStack stack) {
	assert(stack);
	int len = stack->_capacity;
	for (int i = 0; i < len; i++) {
		free(stack->_array);
		stack->_array++;
	}
	stack->_array = NULL;
	stack->_top = 0;
	stack->_capacity = 0;
}


操作应用:

1. 进制转换


//二进制转10进制
int BinaryToTen(PStack stack, DataType* str) {
	assert(stack);
	unsigned int len = stack->_capacity;
	char* p_str = str;
	int sum = 0;
	for (int i = 0; i < len; i++) {
		if (*p_str == '\0')
			break;
		StackPush(stack, *p_str);
		p_str++;
	}
	char binary = ' ';
	for (int i = 0; i < len; i++) {
		if (0 == StackPop(stack, &binary))
			break;
			sum += (binary - 48)*pow(2, i);
	}
	return sum;
}



2. 括号匹配


//括号匹配
void MatchBranckets(char* str) {
	if (str == NULL)
		return;
	
	//初始化栈
	PStack stack = (PStack)malloc(sizeof(struct Stack));
	StackInit(stack);

	char* p_str = str;

	//用来保存弹出的字符
	char c;

	while (*p_str) {

		if (*p_str == '{' || *p_str == '(' || *p_str == '[') {
			StackPush(stack, *p_str);
		}

		if (*p_str == '}' || *p_str == ')' || *p_str == ']') {

			StackPop(stack, &c);

			if (c == '{' && *p_str == '}');
			else if (c == '(' && *p_str == ')');
			else if (c == '[' && *p_str == ']');
			else {
				printf("匹配失败\n");
				return;
			}

		}

		p_str++;
	}

	//遍历完之后,栈中还有括号,代表匹配失败
	if (StackPop(stack, &c))
	{
		printf("匹配失败\n");
		return;
	}
	printf("匹配成功\n");
}




3. 后缀表达式求值

//根据操作符的不同进行不同的计算
int caculate(int first, int second, char operator) {
	if (operator == '+') {
		return first + second;
	}
	else if (operator == '-') {
		return first - second;
	}
	else if (operator == '*') {
		return first * second;
	}
	else if (operator == '/') {
		return first / second;
	}
}

//后缀表达式求值
int caculateNifixExpression(char* str) {

	//用来放整形操作数
	PStack operator = (PStack)malloc(sizeof(struct Stack));
	StackInit(operator);

	if (str == NULL)
		return;

	char* p_str = str;
	char* p_preStr = str;

	int StackPopData = 0;
	//弹出的第一个操作数,右操作数
	int first = 0;
	//弹出的第二个操作数, 左操作数
	int second = 0;
	while (*p_str) {
		if (*p_str == ' ') {
			//空格前是数字,压栈
			if (*p_preStr >= '0' && *p_preStr <= '9')
				StackPush(operator, atoi(p_preStr));
			p_preStr = p_str + 1;
		}
		if (*p_str == '+' || *p_str == '-' || *p_str == '*' || *p_str == '/') {

			StackPop(operator, &StackPopData);
			second = StackPopData;
			StackPop(operator, &StackPopData);
			first = StackPopData;

			//计算结果
			StackPopData = caculate(first, second, *p_str);

			//结果入栈
			StackPush(operator, StackPopData);
		}
		p_str++;
	}
	StackPop(operator, &StackPopData);
	printf("计算结果为%d\n", StackPopData);
	return StackPopData;
}




4.中缀表达式转后缀表达式

思路:

    (1).初始化两个栈(一个用来运算operand,一个用来存放结果operand)

    (2).扫描字符串

  • 遇到操作数入栈
  • 遇到'('直接入栈
  • 遇到')',一直出栈,直到遇到(,将左括号出栈
  • 遇到其他操作符,先将这个操作符和用来运算的栈(operator)的栈顶元素比较,如果栈顶运算符的优先级较高,将栈顶运算符入存放结果的栈(operand),并将当前运算符入(operator)栈。



//中缀转后缀
char* SuffixToNifix(char* str) {

	//操作数栈
	PStack operand = (PStack)malloc(sizeof(struct Stack));
	StackInit(operand);

	//操作符栈
	PStack operator = (PStack)malloc(sizeof(struct Stack));
	StackInit(operator);

	char buffer;
	while (*str) {
		//判断是操作数还是操作符
		if (*str >= '0' && *str <= '9') {
			//操作数入栈		
			StackPush(operand, *str);
			if(*(str + 1) < '0' || *(str + 1) > '9')
				if (StackTop(operand) != 0)
					StackPush(operand, 0);
		}
		else {
			/*if (StackTop(operand) != 0)
				StackPush(operand, 0);*/
			//扫描到操作符
			//判断操作符栈是否为空,为空直接入栈
			if (1 == StackEmpty(operator)) {
				StackPush(operator, *str);
			}
			else if ('(' == *str) {
				//遇到左括号,直接入栈,左括号的优先级最高
				StackPush(operator, *str);
			}
			else if (')' == *str) {
				//遇到右括号,一直出栈,并压栈到操作数栈,直到把括号弹出
				while (1) {
					StackPop(operator, &buffer);
					if (buffer == '(')
						break;
					StackPush(operand, buffer);
					/*if (StackTop(operand) != 0)
						StackPush(operand, 0);*/
				}		
			}
			else {
				
				//非空,则比较当前栈顶运算符和遇到的运算符的优先级
				char operatorTop = StackTop(operator);
				if (cmpPriority(operatorTop, *str) > 0 && operatorTop != '(') {
					//栈顶运算符优先级较高,弹出栈顶运算符,并压到操作数栈
					StackPop(operator, &buffer);
					StackPush(operand, buffer);
					StackPush(operator, *str);
				}
				else {
					//栈顶运算符优先级较低,当前运算符直接入栈
					StackPush(operator, *str);
				}
			
			}
			
		}
		
		str++;
	}
	char buf;
	while (StackPop(operator, &buf)) {
		StackPush(operand, buf);
	}
	while (StackPop(operand, &buf)) {
		StackPush(operator, buf);

	}
	while (StackPop(operator, &buf)) {
		printf("%c", buf);
	}
	printf("\n");
	return NULL;
}



5. 迷宫问题

思路:

非递归:

(1)入口点压栈。

(2)以左上右下的顺序判断周围有哪个坐标可以到达(数组的值设置为1)。

(3)遇到可以到达的坐标就把当前坐标设置为这个坐标,将当前坐标设置为不可达(数组的值设置为2),并把这个坐标压栈

(4)重复(2)(3)步。

(5)遇到了死胡同(周围都不可达),栈顶坐标出栈,并将弹出的坐标设置为当前坐标。

(6)判断是否到达了起点(无出口),判断是否到达了边界位置(非起点),到达边界跳出。未到达边界。

(7)重复(2)(3)步

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define INIT_STACKSIZE 100
#define WIDTH 10

//设置当前坐标的状态,将当前坐标入栈
#define SET_WENTSTATE_AND_PUSHSTACK \
map[y][x] = 2;\
loc._x = x;\
loc._y = y;\
StackPush(s, loc);\
continue


typedef struct Location {
	//坐标
	int _x;
	int _y;

}Location;

typedef Location DataType;

typedef struct Stack {
	DataType* _array;
	int _top;
	//最大容量
	int _capacity;
}*PStack, Stack;

void StackInit(PStack s) {
	s->_array = (PStack)malloc(sizeof(Stack)*INIT_STACKSIZE);
	if (s->_array == NULL)
		exit(0);
	s->_capacity = INIT_STACKSIZE;
	s->_top = 0;
}

void StackPush(PStack s, DataType loc) {
	assert(s);
	if (s->_top >= s->_capacity) {
		s->_array = (PStack)realloc(s->_array, ((s->_capacity)+10)*sizeof(Stack));
		if (s->_array == NULL)
			exit(0);
		s->_capacity = s->_capacity + 10;
	}
	*(s->_array + s->_top) = loc;
	s->_top++;
}

int StackPop(PStack s, DataType* loc) {
	assert(s);
	if (!s->_top) {
		return 0;
	}
	--s->_top;
	*loc = *(s->_array + s->_top);
	return 1;
}

DataType StackTop(PStack s) {
	return *(s->_array + s->_top);
}

//判断是否到达边界
int isBorder(int x, int y) {
	if (x == WIDTH - 1 || x == 0 || y == 0 || y == WIDTH - 1)
		return 1;
	return 0;
}

//判断是否回到了起点
int isStart(int start_x, int start_y, int x, int y) {
	if (start_x == x && start_y == y)
		return 1;
	return 0;
}

//打印地图
void showMap(int map[WIDTH][WIDTH]) {
	for (int i = 0; i < WIDTH; i++) {
		for (int j = 0; j < WIDTH; j++) {
			if (map[i][j] == 0) {
				printf("█");
			}
			else if(map[i][j] == 1){
				printf("  ");
			}
			else {
				printf(" *");
			}
			//printf("%d ", map[i][j]);
		}
		printf("\n");
	}
}

void maze(int map[WIDTH][WIDTH], int start_x, int start_y) {
	
	PStack s = (PStack)malloc(sizeof(Stack));
	StackInit(s);

	//入口
	int x = start_x;
	int y = start_y;

	//将入口位置设置为已走过的状态
	map[y][x] = 2;
	//将当前坐标压栈,

	showMap(map);

	Location loc = { x, y };
	StackPush(s, loc);

	while (1) {
		int oldx = x;
		int oldy = y;

		//左
		if (map[y][x - 1] == 1) {
			--x;
			SET_WENTSTATE_AND_PUSHSTACK;//宏:入栈和赋值当前坐标操作。
		}
		//上
		if (map[y - 1][x] == 1) {
			--y;
			SET_WENTSTATE_AND_PUSHSTACK;
		}
		//右
		if (map[y][x + 1] == 1) {
			++x;
			SET_WENTSTATE_AND_PUSHSTACK;
		}
		//下
		if (map[y + 1][x] == 1) {
			++y;
			SET_WENTSTATE_AND_PUSHSTACK;
		}

		Location loc1;
		if (!StackPop(s, &loc1)) {
			if (isStart(start_x, start_y, oldx, oldy)) {
				printf("迷宫无出口\n");
				break;
			}
			break;
		}

		//判断是否到达边界
		if (isBorder(x, y)) {
			//判断是否遇到起始位置
			showMap(map);
			break;
		}
		
		//遇到分叉点的处理,因为第二次到达分叉点的时候分叉点已经弹出。所以要求出分叉点的坐标
		if (oldx + 2 == loc1._x || oldy + 2 == loc1._y ||
			oldx - 2 == loc1._x || oldy - 2 == loc1._y
			) {
			x = (oldx + loc1._x) / 2;
			y = (oldy + loc1._y) / 2;
			StackPush(s, loc1);
		}
		else {
			x = loc1._x;
			y = loc1._y;
		}

	}
}

//迷宫问题递归解法
int ENDX = 0;
int ENDY = 8;
int mazeRecursion(int map[][WIDTH], int x, int y) {
	map[y][x] = 2;
	int isEnd = 0;//判断是不是终点
	if (ENDX == x && ENDY == y) {
		isEnd = 1;
	}
	//左
	if (isEnd != 1 && x >= 0 && map[y][x - 1] == 1) {
		if(mazeRecursion(map, x - 1, y) == 1)
			return 1;
	}
	//上
	if (isEnd != 1 && y >= 0 && map[y - 1][x] == 1) {
		if (mazeRecursion(map, x, y - 1) == 1)
			return 1;
	}
	//右
	if (isEnd != 1 && x < WIDTH && map[y][x + 1] == 1) {
		if (mazeRecursion(map, x + 1, y) == 1)
			return 1;
	}
	//下
	if (isEnd != 1 &&  y < WIDTH && map[y + 1][x] == 1) {
		if (mazeRecursion(map, x, y + 1) == 1)
			return 1;
	}

	//未到终点,但是没有路可以走了。
	if (isEnd == 0) {
		map[y][x] = 1;
	}
	//未到终点返回0。
	return isEnd;
}


int main() {
	int map[WIDTH][WIDTH] = {
		{ 0,0,0,0,0,0,1,0,0,0 },
		{ 0,0,1,1,1,0,1,0,1,0 },
		{ 0,0,1,0,1,0,1,0,1,0 },
		{ 0,0,1,0,1,0,1,0,1,0 },
		{ 0,0,1,0,0,0,1,0,1,0 },
		{ 0,0,1,1,1,0,1,0,1,0 },
		{ 0,0,1,0,1,0,1,1,1,0 },
		{ 0,0,1,0,1,0,0,0,1,0 },
		{ 1,1,1,0,1,1,1,1,1,0 },
		{ 0,0,0,0,0,0,0,0,0,0 }
	};
	
	printf("非递归:\n");
	maze(map, 6, 0);
	/*printf("递归:\n");
	mazeRecursion(map, 6, 0);
	showMap(map);*/
	return 0;
}


运行结果:






详细代码在:Niceug



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值