c++库函数版数据结构
基于stl库中vector的栈
栈的实现代码
栈属于序列的特例,故可直接基于向量或列表派生
template<typename T> class Stack public Vector<T>
{
public: //size()、empty()以及其它开放接口均可直接沿用
void push(T const & e)//入栈
{
insert(size(),e;
}
T pop()//出栈
{
return remove(size() - 1);
}
T & top()//返回栈顶元素
{
return (*this)[size() - 1];
}
};
栈的应用
1、逆序输出之进制转换
![image-20210802141846313](https://raw.githubusercontent.com/huangguangchao/MAG/main/20210802141900.png)
![image-20210802141855689](https://raw.githubusercontent.com/huangguangchao/MAG/main/20210802141857.png)
void convert(Stack<char> & S,_int64 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){//由低到高,逐一计算出新进制下的各数位
S.push(digit[n % base]);//余数(对应的数位)入栈
n/=base;//n更新为其对base的除商
}
}
2、递归嵌套之括号匹配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRAsisHt-1630206361817)(https://raw.githubusercontent.com/huangguangchao/MAG/main/20210802151234.png)]
bool paren(const char exp[],int 1o,int hi)//exp[1o,hi]
{
Stack<char>S//使用栈记录已发现但尚未匹配的左括号
for(int i=1o;i< hi;i++)//逐一检查当前字符
if('('==exp[i])
s.push(exp[i]);//遇左括号:则进栈
else if(!S.empty())
S.pop();//遇有括号:若占非空,则弹出左括号
else return false;//否则(遇右括号时钱已空),必不匹配
return s.empty();//最终,栈空当且仅当匹配
}
为什么不直接使用计数器,遇到左括号加一遇到右括号减一这样?
因为如果出现中括号或方括号混搭的时候这样就行不通了,必须使用栈才能达到那个目的。
栈混洗总数:
判断是否为栈混洗:
对于任何1<= i<j<k<=n,[…,[k],…,[i],…,[j],.…)(必非栈混洗)
3、延迟缓冲之中缀表达式求值
![image-20210803143021635](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210803143022.png)
![image-20210803143045806](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210803143047.png)
4、栈式计算之逆波兰表达式
在由运算符(operator)和操作数(operand)组成的表达式中不使用活号(parenthesis-free),即可表示带优先级的运算关系
遇到操作数就入栈,遇到单目运算符就弹出一个数,遇到双目运算符就弹出两个数,双目运算符对弹出的数字运算时先弹出的数字在运算符的右边,后弹出的数字在运算符左边进行运算。
基于stl库中list函数的队列
只能在队尾插入(查询):enqueue()+rear()
只能在队头删除(查询):dequeue()+front()
template <typename T>class Queue:public List<T>
{ //由列表派生的队列模板类public: //size()与empty()直接沿用
void enqueue(T const&e)
{
insertasLast(e);
}//入队
T dequeue()
{
return remove(first());
}//出队
T&front()
{
return first()->data;
}//队首
};//以列表首/末端为队列头/尾——颠倒过来呢?
基于stl库的树
树的结点包含一个数据元素及若干指向其子树的分支。结点拥有的子树称为结点的度(Degree)。度为0的结点称为叶结点(Leaf)或终端结点;度不为0的结点称为非终端结点或分支结点,除根结点之外,分支结点也称为内部结点。树的度是树内各结点的度的最大值。
所有叶子深度中的最大者称作(子)树的高度
BinNode模板类
![image-20210804150422697](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804150424.png)
BinNode接口实现
BinTree模板类
![image-20210804150843376](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804150844.png)
高度更新
节点插入
![image-20210804151946960](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804151948.png)
递归实现先中后根遍历(以先根为例)
迭代实现
![image-20210804154330593](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804154333.png)
迭代二:
![image-20210804155916422](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804155917.png)
![image-20210804155944120](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210804155946.png)
基于stl库的图
template <typename(Tv),typename(Te)class Graph{//顶点类型、边类型
private:
void reset(){ //所有顶点、边的辅助信息复位
for(int i = e;i < n;i++){ //顶点
status(i) = UNDISCOVERED;
dTime(i) = fTime(i) = -1; //时间标签
parent(i) = -1;
priority(i) = INT_MAX;
for(int j = e;j<n;j++)//边
if(exists(i,j))
status(i,j) = UNDETERMINED;
}
}
public:/*...顶点操作、(边操作、图等法:|无论如何实现,接口必须统一..*/
}
顶点类
![image-20210805154753369](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210805154754.png)
边类
![image-20210805154854146](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210805154855.png)
临接矩阵
![image-20210805155040661](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210805155042.png)
顶点操作
Tv & vertex(int i){return V[i].data;}//数据
int inDegree(int i){return V[i].inDegree;}//入度
int outDegree(int i){return V[i].outDegree;}//出度
Vstatus & status(int i){return V[i].status;}//状态
int & dTime(int i){return V[i].dTime;}//时间标签dTime
int & fTime(int i){return V[i].fTime;}//时间标签fTime
int & parent(int i){return V[i].parent;}//在遍历树中的父亲
int & priority(int i){return V[i].priority;}//优先级数
对于任意顶点 i,如何枚举其所有的邻接顶点neighbor?
int nextNbr(int i,int j){//若已枚举至邻居j,则转向下一邻居
while((-1 < j) && !exists(i,--j));//逆向顺序查找,0(n)
return j;
}//改用邻接表可提高至0(1+outDegree(i))
首个邻居:
如何插入一个顶点?
![image-20210805160347764](https://gitee.com/hgchshs/markdown-table/raw/master/img/20210805160348.png)
边操作
判断边是否存在?
边如何实现插入?
边删除?
遍历算法
广度优先遍历
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G2ws9YZ8-1630206407975)(C:\Users\ASUS-PC\AppData\Roaming\Typora\typora-user-images\image-20210805170219879.png)]
广度优先算法
图的具体实践(和上述方法不同)
#include<iostream>
#include<queue>
#define MAXVEX 10
#define INF 0
using namespace std;
class MGraph
{
public:
void init()//初始化
{
for (int i = 0; i < MAXVEX; i++)
for (int j = 0; j < MAXVEX; j++)
arc[i][j] = INF;
}
void InitVisited()
{
for (int i = 0; i < MAXVEX; i++)
Visited[i] = 0;
}
void Creat()
{
int i, j, k, w;
cout << "请输入定点数和边数:" << endl;
cin >> vexnum >> edgenum;
cout << "请输入顶点信息:" << endl;
for (i = 0; i < vexnum; i++)
{
Vertex[i] = i;//存储序号
cin >> Vername[i];//输入顶点名字
}
for (k = 0; k < edgenum; k++)
{
cout << "请输入边(vi,vj)的下标i,j和权w:";
cin >> i >> j >> w;
arc[i][j] = arc[j][i] = w;
}
}
void Print()
{
cout << "临接矩阵:" << endl;
for (int i = 0; i < vexnum; i++) {
for (int j = 0; j < vexnum; j++)
printf("%4d", arc[i][j]);
cout << endl;
}
}
int LocateVex(char ch)//返回顶点在图中的位置
{
int i;
for (i = 0; i < vexnum; i++)
if (ch == Vername[i])
break;
if (i == vexnum)
return -1;
else
return i;
}
void InsertVex(char ch)//增加某个顶点
{
Vertex[vexnum] = vexnum;
Vername[vexnum] = ch;
vexnum++;//顶点数自增
}
void InsertEdge(int i, int j, int w)//增加某条边
{
arc[i][j] = w;
edgenum++;
}
void DeleteEdge(int i, int j)//删除某条边
{
arc[i][j] = INF;
edgenum--;
}
void DeleteVex(char ch)//删除某个顶点
{
int i, j, k;
for (i =0;i < vexnum; i++)//遍历寻找顶点位置
if (ch == Vername[i])
break;
for (j = i; j < vexnum - 1; j++)//Vername数组循环左移
Vername[j] = Vername[j + 1];//覆盖
//arc数组循环左移和上移
//上移
for (j = i; j < vexnum - 1; j++)
for (k = 0; k < vexnum; k++)
arc[j][k] = arc[j + 1][k];
for (k = 0; k < vexnum; k++)
//左移
for (j = 0; j < vexnum - 1; j++)
for (k = i; k < vexnum - 1; k++)
arc[j][k] = arc[j][k + 1];
vexnum--;
}
//返回第一个邻接顶点
char FirstADjVex(char ch)
{
int i,j;
for (i = 0; i < vexnum; i++)
if (ch == Vername[i])
break;
for (j = 0; j < vexnum; j++)
if (arc[i][j] != INF)
break;
if (j == vexnum)
return'\0';
else
return Vername[j];
}
//深度优先遍历
void DFS(char ch)
{
int i, j, k;
int l = LocateVex(ch);
cout << Vername[l] << " ";//输出当前顶点
Visited[l] = 1;//表示访问过该顶点
for (i = 0; i < vexnum; i++)
if (Visited[i] == 0 && arc[l][i] != INF)
DFS(Vername[i]);
}
//广度优先遍历
void BFS(char ch)
{
int i;
queue<int>q;//定义队列
int l = LocateVex(ch); //获取顶点位置
cout << Vername[l] << " ";//输出当前顶点信息
InitVisited();//Visited队列初始化
q.push(l);//入队
Visited[l] = 1;
while (q.size())//一直循环直至队列为空
{
l = q.front();//获取队头信息
q.pop();//出队
for( i = 0;i<vexnum;i++)
if (Visited[i] == 0 && arc[l][i] != INF)
{
cout << Vername[i] << " ";//访问顶点
q.push(i);//入队
Visited[i] = 1;//表示已访问
// cout << "i = " << i << endl;
//cout << "Visited[i] = " << Visited[i]<<endl;
}
}
}
private:
int Vertex[MAXVEX];//结点序号
char Vername[MAXVEX];//结点名字
int arc[MAXVEX][MAXVEX];//邻接矩阵
int vexnum;//顶点数
int edgenum;//边数
int Visited[MAXVEX];
};
int main()
{
MGraph mygraph;
mygraph.init();
mygraph.Creat();
mygraph.Print();
mygraph.InitVisited();
char ch;
cout << "请输入要增加的顶点信息";
cin >> ch;
mygraph.InsertVex(ch);
mygraph.Print();
cout << "请输入要删除的顶点信息";
cin >> ch;
mygraph.DeleteVex(ch);
mygraph.Print();
cout<<"请输入要查找的顶点:";
cin >> ch;
ch = mygraph.FirstADjVex(ch);
if (ch == '\0')
cout << "该顶点不存在" << endl;
else
cout << "该顶点的第一个邻接顶点是" << ch;
cout << "请输入起点顶点的信息";
cin >> ch;
cout << "深度优先遍历:" << endl;
mygraph.DFS(ch);
//广度优先遍历
cout << "请输入起点顶点信息";
cin >> ch;
cout << "广度优先遍历"<<endl;
mygraph.BFS(ch);
cout << endl;
return 0;
}