使用教材:《数据结构C++语言版》(第三版) 清华大学出版社
练习PAT过程中发现自己的C++和数据结构都很烂,是以决定先打好基础。
栈和队列
这两个数据结构都属于线性表,所以放在里面的元素也都具有线性次序。
(一)栈 Stack
1.栈的定义
栈是存放数据对象的一种特殊容器,其中的数据元素按照线性的逻辑次序排列,故也可定义首、末元素。
栈只有一段可以操作,即栈顶(stack top),另外一端不能,不能操作的一端被称为盲端,也叫栈底(stack buttom)。
栈是先进后出结构,LIFO(last in first out)
2.栈的接口功能
———— 栈的接口功能 ————
操作接口 | 功能 |
---|---|
size() | 报告栈的规模 |
empty() | 判断栈是否为空【返回布尔变量】 |
push(e) | 将e压入栈顶 |
pop() | 删除栈顶对象 |
top() | 取出栈顶对象 |
3.栈的典型应用
使用栈的头文件:
#include <stack>
定义栈:
stack<int> stk;
3.1 逆序输出
3.1.1 进制转换
例1.给定任意十进制整数n,将其转换为X进制的表示形式。
主要在这个例子中关注栈的调用方式:
1)头文件的正确引用;
#include <stack>
2)声明一个栈的方式;
stack<char> stk; //尖括号里是数据类型,一般为char ,因为char 比较容易向其他数据形式转换
3)当栈作为参数的时候如何书写;
stack<char> &S
————这就是教材上的代码,我加了主函数让它可以进行测试而已————
#include <iostream>
#include <stack>
using namespace std;
void convert(stack<char> &S,int n,int base);
//数位转换函数:将十进制的n转化为base进制的数
int main()
{
printf(" 请输入您想转换的进制: ");
int base;
scanf("%d",&base);
printf("请输入要被转换的数字: ");
int n;
scanf("%d",&n);
printf(" 转换结果为: ");
stack<char> S;
convert(S,n,base);
while(S.empty()==false)
{
printf("%c",S.top());//引用栈顶对象
S.pop();//删除栈顶对象
}
return 0;
}
void convert(stack<char> &S,int n,int base)
{
static char digit[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
while(n>0)
{
int remainder = (int)(n%base);//余数是第几位,就对应到哪里
S.push(digit[remainder]);//压入栈,这样就能做到最后输出个位
n/=base;//n除base
}
}
3.1.2 括号匹配
例2:使用栈来进行括号匹配的思路非常简单:遇到左括号压栈,遇到有括号弹栈,看弹出的是否匹配——如果弹出的括号不匹配,或者最后栈非空,则都不算能够匹配的上。
#include <iostream>
#include <stack>
#include <string>
using namespace std;
bool bean(char exp[],int lo,int hi)//表达式括号匹配检查,可以兼顾三种括号
{
stack<char> S;//使用栈记录已发现但尚未匹配的左括号
for(int i=lo;i<=hi;i++)//逐一检查当前字符
{
switch(exp[i])
{
case'(':case'[':case'{':S.push(exp[i]);break;
case')':if((S.empty())||(S.pop()!='(')) return false;break;
case']':if((S.empty())||('['!=S.pop())) return false;break;
case'}':if((S.empty())||('{'!=S.pop())) return false;break;
default:break;//非括号字符一律忽略
}
}
return S.empty();//若栈为空,则匹配;否则不匹配
}
int main()
{
printf("请输入字符串: ");
char exp[100];
scanf("%s",exp);
int hi=0;
int i=0;
while(exp[i]!='\0')
{
hi=hi+1;
i++;
}
bool result=bean(exp,0,hi);
if(bean==false)
printf("\n不匹配");
else
printf("\n匹配");
return 0;
}
3.1.3 表达式求值
先序后序表达式转换的问题,因为需要输入符号优先级表,懒得输入所以没做。
总归就是符号或数字压栈的问题。
4 试探回溯法与剪枝
4.1 N皇后问题
这个问题基于一个矩阵,放置皇后的过程就像是“占住一个格子”,皇后的势力范围是它所在的同一行,同一列,还有同一条对角线,请问在一个N*N矩阵里,最多可以放置多少个皇后,这就是经典的N皇后问题。
#include <iostream>
#include <stack>
using namespace std;
struct Queen{//皇后类
int x,y;//皇后在棋盘上的位置坐标
Queen(int xx=0,int yy=0):x(xx),y(yy){};
bool operator==(Queen const&q)const
{
return (x==q.x)//行冲突
||(y==q.y)//列冲突
||(x+y==q.x+q.y)//沿正对角线冲突
||(x-y==q.x-q.y) //沿反对角线冲突
}
bool operator!=(Queen const&q)const{return !(*this == q);}
}
};
void placeQueen(int N)//N皇后算法
{
stack<Queen> solu;//存放(部分)解的栈
Queen q(0,0);//从原点位置出发放置皇后
do{
if(N <= solu.size()||N<=q.y) //N出界,则
{
q=solu.pop());//回溯一行,继续试探下一列
q.y++;
}
else
{
while((q.y<N)&&(0<=solu.find(q)))//通过与已有皇后的比对
{
q.y++;
nCheck++;
}
if(N>q.y)
{
solu.push(q);
if(N <= solu.size())
nSolu++;
q.x++;
q.y=0;
}
}
} while((0<q.x)||(q.y<N))
}
int main()
{
}
3.1.3 迷宫寻径
(二) 队列 FIFO
定义:基本形式与栈相同。最重要的特征是“先进后出”,即,如果将队列的一端设置为可插入,则只能从另外一端删除。
队头: front
队尾: rear
队首出,队尾入。
为便于记忆,可以将这个过程记作排队打饭,加入队伍的时候排在队尾,分配资源的时候从队头开始分配。
头文件:
#include <queue>
定义队列:
queue <int> q;
———— 队列函数一览 ————
操作接口 | 功能 |
---|---|
size() | 报告队列的规模 |
empty() | 判断队列是否为空【返回布尔变量】 |
enqueue(e) | 将e加入队尾 |
dequeue() | 删除队首对象 |
front() | 取出队首对象 |
常见的队列应用
1.循环分配器
多用户排队等待资源分配时,队列适合处理这样的问题。先到先排队,先排队先得到服务。