算法学习笔记
图
概述
1.通路中边依次地首尾相连,其中沿途边的总数m,也称通路的长度
2.对于长度m>=1的通路Π,若起止顶点相同,则称为环路
3.经过图中各边一次且恰好一次的环路,称作欧拉环路(Eulerian tour)
经过图中各顶点一次且恰好一次的环路,称作哈密尔顿环路(Hamiltonian tour)
4.各边均带权重的图,称作带权图(weighted graph)或带权网络(weighted network)
5.对于无向图,每一对顶点至多贡献一条边,故总共不超过n(n-1)/2条边;对于有向图,每一对顶点都可能贡献(互逆的)两条边,因此至多可有n(n-1)条边。
总而言之,必有复杂度为n^2
6.操作接口:
图ADT支持的边操作接口
操作接口 | 功能描述 |
---|---|
e(u) | 边总数E |
exist(v,u) | 判断联边(v,u)是否存在 |
insert(v,u) | 引入从顶点v到u的联边 |
remove(v,u) | 删除从顶点v到u的联边 |
type(v,u) | 边在遍历树中所属的类型 |
edge(v,u) | 边所对应的数据域 |
weight(v,u) | 边的权重 |
图ADT支持的顶点操作接口
操作接口 | 功能描述 |
---|---|
n() | 顶点总数n |
insert(v) | 在顶点集v中插入新顶点v |
remove(v) | 将顶点v从顶点集中删除 |
inDegree(v) outDegree(v) | 顶点v的入度,出度 |
firstNbr(v) | 顶点vde首个邻接顶点 |
nextNbr(v,u) | 在v的邻接顶点中,u的后继 |
status(v) | 顶点v的状态 |
dTime(v),fTime(v) | 顶点v的时间标签 |
parent(v) | 顶点v在遍历树中的父节点 |
priority(v) | 顶点v在遍历树中的权重 |
最短路径算法
template<typename Tv, typename Te> struct DijkstraPu{//针对Dijkstra算法的顶点优先级更新器
virtual void operator(Graph<Tv, Te> *g, int uk, int v){
if(g->status(v) == UNDISCOVERED){//对于uk每一尚未被发现的邻接顶点v,按Dijkstra策略
if(g->priority(v) > g->priority(uk) + g->weight(uk, v)){//做松弛
g->priority(v) = g->priority(uk) + g->weight(uk, v);//更新优先级数
g->parent(v) = uk;//并同时更新父节点
}
}
}
}
PAT案例:
void dijk(int c){
dis[c]=0;//起点到起点的距离为0
for(int i=0;i<m;i++){//循环n次每次加一个点,每次加入的点都是已更新完成的点
int v=-1;
for(int j=0;j<m;j++){
//每次都遍历所有的点,从没加入图中的点中选一个到原点距离最小的点,加入图中
if(!vis[j]&&(v==-1||dis[j]<dis[v])){
v=j;
}
}
vis[v]=1;
for(int j=0;j<m;j++){//进行松弛操作
dis[j]=min(dis[j],dis[v]+map[v][j]);
}
}
return ;
}
深度优先搜索(dfs)
以顶点s为基点的DFS搜索,将首先访问顶点s;再从s所有尚未访问到的邻居中任取其一,并以之为基点,递归地执行DFS搜索。
template <typename Tv, typename Te> //深度优先搜索DFS算法(全图)
void Graph<Tv, Te>::dfs ( int s ) { //assert: 0 <= s < n
reset(); int clock = 0; int v = s; //初始化
do //逐一检查所有顶点
if ( UNDISCOVERED == status ( v ) ) //一旦遇到尚未发现的顶点
DFS ( v, clock ); //即从该顶点出发启动一次DFS
while ( s != ( v = ( ++v % n ) ) ); //按序号检查,故不漏不重
}
template <typename Tv, typename Te> //深度优先搜索DFS算法(单个连通域)
void Graph<Tv, Te>::DFS ( int v, int& clock ) { //assert: 0 <= v < n
dTime ( v ) = ++clock; status ( v ) = DISCOVERED; //发现当前顶点v
for ( int u = firstNbr ( v ); -1 < u; u = nextNbr ( v, u ) ) //枚举v的所有邻居u
switch ( status ( u ) ) { //并视其状态分别处理
case UNDISCOVERED: //u尚未发现,意味着支撑树可在此拓展
type ( v, u ) = TREE; parent ( u ) = v; DFS ( u, clock ); break;
case DISCOVERED: //u已被发现但尚未访问完毕,应属被后代指向的祖先
type ( v, u ) = BACKWARD; break;
default: //u已访问完毕(VISITED,有向图),则视承袭关系分为前向边或跨边
type ( v, u ) = ( dTime ( v ) < dTime ( u ) ) ? FORWARD : CROSS; break;
}
status ( v ) = VISITED; fTime ( v ) = ++clock; //至此,当前
PAT案例:
void dfs(int a){
visit[a] = 1;
for(int i = 1; i <= n; i++){
if(visit[i] == 0 && maps[a][i] == 1){
dfs(i);
}
}
return ;
}
二叉树
概述
1.沿每个节点v到根r的唯一通路所经过边的数目,称作v的深度,记作depth(v)
2.v的孩子总数,称作其度数或度(degree),记作deg(v)。无孩子的节点称作叶节点(leaf),包括根在内的其余节点皆为内部节点(internal node)。
3.v的所有后代及其之间的联边称作子树,记作subtree(v)
4.树T中所有节点深度的最大值称作该树的高度(height),记作height(T)
5.二叉树(binary tree)中每个节点的度数均不超过2
不含一度节点的二叉树称作真二叉树(proper binary tree)
6.完全二叉树的根节点序号为n,则其左右结点序号为2n和2n+1
二叉搜索树
1.顺序性:任一节点r的左(右)子树中,所有节点(若存在)均不大于(不小于)r
2.中序遍历序列:
二叉搜索树的中序遍历序列,必然单调非降
任何一棵二叉树是二叉搜索树,当且仅当其中序遍历序列单调非降
3.查找算法:
从根节点开始,逐步地缩小查找范围,直到发现目标(成功)或缩小至空树,依照右子树元素不小于根节点,左子树元素不大于根节点的原则层层缩小即可