树与图
文章目录
树
特性
度
- 整棵树的度定义为最大的度
- 节点数=度之和+1
- 每个节点的度特指往字数的个数
路径长度
特指边的个数
和深度不一样哦
树的高度(深度)
特指在第几层。
根节点在第一层,理论上高度比(到根节点的)长度多一个。
二叉树
n次树 n叉树 与度为n的树
- "n"次树,注意定义度为n,隐含的条件是有这样一个节点的度是n。与“叉”树不一样。
- 二叉树与度为2的树不一样,区分左右子树且可以度为0;
- 二叉树的定义:二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。
二叉树有五种形态
- 空子树
- 只有一个根节点
- 右子树为空
- 左子树为空
- 都非空
区分-特例
满二叉树
确实是满的
完全二叉树
若二叉树中最多只有最下面两层的结点的度数可以小于2,并且最下面一层的叶子结点都依次排列在该层最左边的位置上,则这样 的二叉树称为完全二叉树。
最后一层最右边可以差几个
性质
- 非空二叉树上叶结点数等于双分支结点数加1。即n0=n2+1。
- 还有基于高度的节点数计算
- 若完全二叉树的根结点编号为1(如果是0就减一个,也不难,实在不行考试的时候自己画一个图),对于编号为i(1≤i≤n)的结点有:
(1) 2i≤n,则编号为i的结点为分支结点,否则为叶子结点,也就是说,最后一个分支结点的编号为n/2。
(2) 若n为奇数,则n1=0,每个分支结点都是双分支结点;若n为偶数,则n1=1,只有一个单分支结点。
(3) 若编号为i的结点有双亲结点,其双亲结点的编号为i/2(向下取整)。
还有很显然的就懒得看了
二叉树的存储
顺序存储
有无效节点
存两个儿子的指针
//开摆了,没写怎么建树
#include <iostream>
using namespace std;
class BTNode{
int data;
public:
BTNode * lson;
BTNode * rson;
BTNode():lson(nullptr),rson(nullptr){};
BTNode(int d){
data=d;
lson=rson=nullptr;
}
~BTNode() {
if(lson!= nullptr)
delete lson;
if(rson!= nullptr)
delete rson;
}
void output() {
printf("%d ",data);
}
};
int prt[1005];
int x[1005],y[1005];
class BTree{
public:
BTNode * root;
BTree():root(nullptr){};
BTree(BTNode *r):root(r){};
void creatTree(int rt) {
BTNode * p=new BTNode(rt);
root=p;
}
void buildTree() {
}
};
void DestroyTree(BTNode *s) {//注意是指针,因为每一层都是指针
if(s==nullptr) return;
DestroyTree(s->lson);
DestroyTree(s->rson);
delete s;
}
void dfs(BTNode *s) {
if(s== nullptr) return ;
s->output();
dfs(s->lson);
dfs(s->rson);
}
int main() {
int n;
for(int i=1;i<n;i++) {
//int x,y;
//scanf("%d%d",&x,&y);//prt
//prt[y]=x;
}
int rt=0;
for(int i=1;i<=n;i++) {
if(!prt[i]) {
rt=i;
break;
}
}
BTree tree;
tree.creatTree(rt);
tree.buildTree();
dfs(tree.root);
return 0;
}
树的基本运算
查找
略
插入/删除
略
遍历
先/后/中序遍历
层次遍历
反确认
(节点不确定)
由先序遍历序列和中序遍历序列能够唯一确定一棵二叉树。
由后序遍历序列和中序遍历序列能够唯一确定一棵二叉树。
由先序遍历序列和后序遍历序列不能唯一确定一棵二叉树。
反例:链,左链和右链
线索二叉树
ltag rtag 为0 的时候表示有孩子,不然就指向对应序列(前中后序遍历)的下一个后继®,或者前驱(l)
哈夫曼树
定理7.3 对于具有n0个叶子结点的哈夫曼树,共有2n0-1个结点。
儿子合并,没啥好说的
三叉树合并需要一点啸寄巧()
哈夫曼编码
左0右1
在一组字符的哈夫曼编码中,任一字符的哈夫曼编码不可能是另一字符哈夫曼编码的前缀。
森林与二叉树
可以类比长子兄弟链存储。。。左子树都是长子,右子树都是兄弟。
并查集
prt[]
Kruscal 要用
- 初始化
prt[i]=i
- 寻找父亲:路径压缩
prt[x]=Find(prt[x])
- 合并:
fx=Find(x),fy=Find(y),prt[fy]=fx;
- 优化:高度较小的可以拿来优化(秩)
存储结构
- 双亲存储
找父亲(相当于存了一个prt)
好像老师没用指针,全部是数组(相当于最简单的方法)
class PNode{
int parent;
int data;
public:
PNode(int p,int d):parent(p),data(d){};
PNode(){};
};
- 孩子链存储
用vector存储指向孩子Node的指针
class SonNode{
int data;
vector<SonNode*> sons;
public:
SonNode(){};
SonNode(int d):data(d){};
};
- 长子兄弟链存储结构
指针,父亲指向长子,长子指向兄弟。
class EBNode{
int data;
EBNode * eson;
EBNode * brother;
public:
EBNode(){};
EBNode(int d){
data=d;
eson=nullptr;
brother=nullptr;
}
}
- 前向星(邻接表)
(图里面看吧)
图
###概念
-
邻接点
-
度:
入度
出度 -
完全图:
完全有向图
完全无向图 -
稠密图
稀疏图 -
子图
-
路径
路径长度
简单路径 -
回路/环
简单回路/环 -
连通图
连通分量
强连通图
强连通分量 -
权-> 网
存储结构
邻接矩阵
略
邻接矩阵的n次方可以用于求路径种数
邻接表(俗称前向星)
//简单版
int h[1005];
struct mapp{
int next,to,weight;
}t[1005];
int cnt=0;
void Addedge(int x,int y,int w) {
cnt++;
t[cnt].next=h[x];
h[x]=cnt;
t[cnt].to=y;
t[cnt].weight=w;
}
//类版
图的遍历
- 连通图
dfs bfs - 非连通图
- visit数组
:if !visit[x]
- 回溯法
- bfs找最短路(暴力咯)
最小生成树
生成树:遍历一次节点得到的树,分为bfs和dfs得到的
Kruscal
先排序,再并查集解决。
Prim
和dijkstra类似,都是选择“距离最短”的结点加入集合,Prim的“距离最短”是指未访问的结点到已经访问的所有结点距离最小,即将已经访问的结点视为一个整体,将距离最小的结点加入到已访问的集合中。
Dijkstra是指到目标。
但是都会用到优先队列。
总结
这俩真的很像。。。
区别就是Kruscal先排序再合并,Prim一边合并一边排序,前者需要有辅助确认是否已经合并过该节点,厚点需要辅助计算某一节点的最短边。
最短路
本质:进行路径的松弛操作
dijkstra
不能负权图求
O(n^2)-> O(e*log2(e))
priority_queue <inQue> q;
int d[1005];
int n,m;//n nodes in the map
bool bj[1005];
int Dijkstra(int st,int ed) {//
for(int i=1;i<=n;i++) d[i]=inf;
d[st]=0;
inQue tmp;
tmp.d=0;
tmp.id=st;
q.push(tmp);
while(!q.empty()) {
tmp=q.top();
q.pop();
int x=tmp.id;
if(bj[x]) continue;
bj[x]=true;
for(int i=head[x];i;i=t[i].next) {
int y=t[i].to;
if(d[y]>d[x]+t[i].v) {
d[y]=d[x]+t[i].v;
tmp.d=d[y];
tmp.id=y;
tmp.prt=x;
q.push(tmp);
}
}
}
if(d[ed]!=inf) return d[ed];
else return -1;
}
Floyd
不适合负权回路,但是可以负权。
O(N^3)
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
for(int k=1;k<=n;k++) {
if(a[i][k]+a[k][j]<=a[i][j])
a[i][j]=a[i][k]+a[k][j];
}
}
}
拓扑排序
找入度为0的,一直找。
O(n+e)
AOE网与关键路径
源点(唯一)
汇点(唯一)
在AOE网中,从源点到汇点的所有路径中,具有最大路径长度的路径称为关键路径。完成整个工程的最短时间就是网中关键路径的长度。
关键路径上的活动称为关键活动,或者说关键路径是由关键活动构成的。只要找出AOE网中的全部关键活动,也就找到了全部关键路径了。
求解:
- 事件的最早开始时间:从左向右推取max
事件的最晚开始时间:从右向左推取min - 活动的最早开始/最晚开始时间
- 关键活动:不存在富余时间
省流:正跑、反跑