数据结构学习笔记_栈和队列(C++实现)

本文详细介绍了栈和队列的数据结构,包括它们的基本操作和在括号匹配、表达式转换、广度优先搜索等场景的应用。同时讨论了矩阵的压缩存储方法,如对称矩阵和稀疏矩阵的表示与存储优化。
摘要由CSDN通过智能技术生成

栈(stack)

  1. 也是一种线性表,但只能在栈顶进行插入或删除,先进后出LIFO
  2. 基本操作:压栈push,弹出pop,判空empty,获取大小size,获取栈顶top
  3. 按照存储方式可以分为顺序栈和链式栈,链式栈(用头插的方式入栈),链式栈可改变容量
//c语言顺序栈
typedef struct {
	int data[10];//假设大小为10
	int top;
}SqStack;
//共享栈
typedef struct {
	int data[10];
	int top0;
	int top1;
}ShStack;
bool pushStack(SqStack &s, int x) {
	if(s.top == 10 - 1)
		return false;
	s.data[s.top++] = x;
	return true;
}

//C++
#include<stack>
stack<int> s;//有模板
s.push(1);//压栈
s.pop();//弹栈,删除栈顶元素
s.empty();//判空,空为1
s.size();//获取大小
s.top();//获取栈顶(不删除栈顶元素)

共享栈:
在这里插入图片描述
栈(stack)的应用

  1. 括号匹配(左括号入栈,遇右括号出栈与之匹配)
  2. 表达式求值:中缀表达式a+b,后缀表达式ab+(若a+b-c,后缀ab+c-,从左到右入栈),前缀表达式+ab(若a+b-c,前缀-+abc,从右往左入栈),中缀表达式直接求值需要两个栈分别放数和运算符。中缀转后缀适合左优先(a+b-c,先+再-),中缀转前缀适合右优先
  3. 中缀转后缀:一个栈存储运算符,一个字符串存储后缀。遍历中缀,遇数放入后缀字符串,遇运算符压入栈,保持栈顶的运算符优先级最高,如果遇到的运算符(假设“-”)优先级小于栈顶(假设“/”),则不断弹出优先级高的栈顶,先拿去运算(即放入后缀字符串),直到遇到的“-”可以放入栈为止。遍历中缀后依次弹出栈放入后缀字符串
  4. 中缀表达式求值:中缀转后缀 + 后缀表达式求值
  5. 递归的本质是函数调用栈(递归工作栈),函数先进后出
//括号匹配
bool bracketCheck(char str[], int length) {
	stack<char> s;
	for(int i = 0; i < length; ++i) {
		if(str[i] == '(' || str[i] == '[' || str[i] == '{')
			s.push(str[i]);
		else {
			if(s.empty()) // 
				return false;
			char tmp = s.pop();
			if(str[i] == ')' && tmp != '(' )
				return false;
			if(str[i] == ']' && tmp != '[' )
				return false;
			if(str[i] == '}' && tmp != '{' )
				return false;
		}
	}
	return s.empty();
}

//中缀转后缀
string infix2suffix(string& infix) {
	stack<char> symb;	//存放运算符
	string suffix;		//存放后缀表达式
	int len = infix.length();
	for(char c: infix) {	//依次遍历中缀字符
		if('0' <= c && c <= '9') {	//遇数字直接放入后缀
			suffix.push_back(c);
		}
		else if(c == '+' || c == '-'){	//遇运算符入栈,保持栈顶的优先级最高
			while(!symb.empty() && symb.top() != '(') {
				suffix.push_back(symb.top());	//高优先级的直接参与运算放入后缀
				symb.pop();
			} 
			symb.push(c);
		}
		else if(c == '*' || c == '/'){
			while(!symb.empty() && symb.top() != '+' && symb.top() != '-' && symb.top() != '(') {
				suffix.push_back(symb.top());
				symb.pop();
			} 
			symb.push(c);
		}
		else if(c == '('){	//括号优先级更高
			symb.push(c);
		}
		else if(c == ')'){	//遇右括号,将括号内的参与运算放入后缀
			while(symb.top() != '(') {
				suffix.push_back(symb.top());
				symb.pop();
			} 
			symb.pop();
		}
	}
	while(!symb.empty()) {	
		suffix.push_back(symb.top());
		symb.pop();
	} 
	return suffix;
}

//计算后缀表达式(示例只适用于个位数的后缀表达式)
int calcuSuffix(string& suffix) {
	stack<int> calcu;
	int len = suffix.length();
	for(char c: suffix) {
		if('0' <= c && c <= '9') {	//数字
			calcu.push(c-'0');
		}
		else {	//运算符
			int y = calcu.top(); calcu.pop();	//后弹出的才是左操作数(如被除数)
			int x = calcu.top(); calcu.pop();
			switch(c) {
				case '+': calcu.push(x+y); break;
				case '-': calcu.push(x-y); break;
				case '*': calcu.push(x*y); break;
				case '/': calcu.push(x/y); break;
				default : break;
			}
		}
	}
	return calcu.top();
}

note:

  1. 后缀表达式计算时,先弹出的是右操作数(如除数),后弹出的是左操作数(如被除数),应该用后弹出运算先弹出,前缀反之。
  2. 同样,中缀转后缀中,遍历时若遇到的运算符“-”,则需要弹出栈中优先级 ≥ “-”的运算符,而不仅仅是>。
  3. 十位以上的中缀转后缀以及表达式计算需要用符号分隔开标记,或者用字符栈存储后缀

队列(queue)

  1. 也是一种线性表,只在队头出队,队尾入队,先进先出FIFO
  2. 基本操作:入队push,出队pop,判空empty,获取大小size,获取队头front,获取队尾back
  3. 双端队列deque,队首和队尾均可以出队和入队,push_front、push_back
//C语言顺序队列
typedef struct {
	int data[10]; //假设大小为10
	int front, rear; //队首和队尾指针
}SqQueue;
bool pushQueue(SqQueue q, int x){
	if((q.rear+1)%10 == q.front)//队尾指针差一点追上队首指针
		return false;//队满
	q.data[q.rear] = x;
	q.rear = (q.rear + 1) % 10;//难点,环状存储空间(循环队列)
}
bool emptyQueue(SqQueue q){
	if(q.rear == q.front)
		return true;//空
	else
		return false;
}
bool sizeQueue(SqQueue q){
	return (rear + 10 - front) % 10;
}

//满队列存储思路,结构体中建立一个size标志位
//插入时
size++;
//删除时
size--;
//判满
size == 10;
//结构体中建立一个tag标志位
//插入时
tag = 1;
//删除时
tag = 0;
//判空
front == rear && tag = 0;
//判满
front == rear && tag = 1;

//C++
#include<queue>
queue<int> q;
q.push(1);//放入队尾
q.pop();//弹出队头元素
q.empty();//判空,空为1
q.size();//获取元素个数
q.front();//获取队头元素
q.back();//获取队尾元素

//双端队列
#include <deque>
deque<int> dq;
dq.push_front(1);//放入队首
dq.push_back(1);//放入队尾
dq.pop_front();//弹出队首
dq.pop_back();//弹出队尾
dq.empty();//判空,空为1
dq.size();//获取元素个数
dq.front();//获取队头元素
dq.back();//获取队尾元素

队列的环状式存储(循环队列):
在这里插入图片描述

note:

  1. 变为环形的原因,使得插入和删除不用增加额外空间,在固定空间进行。
  2. 没有size标志位时,判空:队尾指针和队首指针指向同处。判满:队尾指针差一点追上队首指针(中间相差一个位置,因为要与判空有所区别,会浪费一个空间)。
  3. 队列满空间存放思路:建立size标志位或tag标志位。
  4. 链队列:许多个包含数据域和next指针的节点结构体,一个包含链表头指针、尾指针和size标志位的结构体,插入更新尾指针,弹出更新头指针,判空,头指针和尾指针指向同一处。不用判满。

队列(queue)的应用

  1. 广度优先搜索(或树的层次遍历,BFS),处理每个节点时将节点的子节点都放入队尾,依次处理队首的节点。
  2. 操作系统中分配有限的系统资源,比如先来先服务策略(FCFS)、缓冲区。

矩阵的压缩存储:

  1. 对称矩阵,可按行优先的原则,用一维数组存储下三角矩阵,映射:a(i,j)=a(j,i) <=> b( i(i-1)/2+j-1 )。三角矩阵类似。
  2. 三对角矩阵(带状矩阵),行优先一维存储,映射:a(i,j) <=> b( 2i+1-j )
  3. 稀疏矩阵,顺序存储三元组,将有值的位置记录下来(i, j, v)。另一种方法为十字链表法,有两条分别代表x轴和y轴的链表数组,每个值作为一个节点,内部存储具体值、指向该行下一个具体值的指针以及指向该列下个具体值的指针。

十字链表法:
在这里插入图片描述

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值