数据结构复习
数据结构
第5章 树
5.1 树的基本概念
树的度: 是节点的度的最大值
树深度: 节点层次的最大值
节点的度:子树的个数
-
树的性质:
-
- n 个结点,每个节点\[度为\ d_i,则 \ n=\sum_{i=1}^{n}d_i +1 \]
- n 个结点,每个节点
-
- 度为 k 的树第 i 层的结点个数最多为\[k^{i-1} \]
- 度为 k 的树第 i 层的结点个数最多为
-
-
深度为h的k叉树,最多结点为
\[\frac{k^{h}-1}{k-1} \] -
具有n个结点的k叉树深度最小为
\[\lceil log_k(n(k-1))+1 \rceil \]
-
-
-
树的基本操作
bool CreateTree(position &t);
bool DelTree(position &t);
void DelSubTree(position &t,position p);
TElelmType GetData(position p);
-
//基本操作的代码实现 InitTree(&T) DestroyTree(&T) CreateTree(&T,definition) TreeEmpty(T) TreeDepth(T) Root(T) Parent(T, x) FirstChild(T,x) Nextsibling(T,x) InsertChild(&T,x,i,p) DeleteChild(&T,x,i) Traverse(T,visit())
-
设计算法
-
递归计算树的深度
若树空则返回
0
或者递归到树的叶子节点,则返回1
否则递归遍历其他子树,取其最大值加上1 则是树的高度
-
算法描述:
-
//递归计算树的深度
int height(Tree T){
if(IsEmpty(T)) return 0;
else if(Isleaf(T)) return 1;
else {
int h = 0,k;
for (Tree p=firstchild(T);P != NULL;p = Nextsibling(T,p))
if((k = height(P)) > h) h = k;
return h+1;
}
}
5.2 二叉树的基本概念
结点的度: 非空子树的个数
叶子结点 : 左右子树均空,度为0
完全二叉树: 深度为h,h-1为满二叉树,h层的结点都集中在左侧
//二叉树的操作
InitBiTree(&T)
DestroyBiTree(&T)
CreateBiTree(&T,definition)
BiTreeEmpty(T)
BiTreeDepth(T)
Parent(T,e)
LeftChild(T,e)
RightChild(T,e)
LeftSibling(T,e)
RightSibling(T,e)
InsertChild(&T,p,LR,C)
DeleteChild(&T,p,LR)
Traverse(T)
//二叉树的结构
typedef struct Node{
char data;
struct Node *lchild,*rchild;
}*BiTree,BiNode;
void InitBitree(BiTree &T){
char ch;
cin >> ch;
if(ch == '#'){
T = NULL;
}
else{
T = new BiNode;
T -> data = ch;
InitBitree(T->lchild);
InitBitree(T->rchild);
}
}
-
二叉树的几个基本性质
-
在 二叉树的第\(i\)层的结点个数最多为
\[2^{i-1} \] -
深度为\(k\)的二叉树的最大结点数为
\[2^k-1 \] -
任一二叉树\(T\),如果其叶子结点数为\(n_0\), 度为2的结点数为\(n_2\),则
\[n_0=n_2+1 \\n_0+n_1+n_2=n=2n_2+n_1+1 \] -
具有\(n\)个结点的完全二叉树深度为
\[\lceil log_2(n+1) \rceil 或\lfloor log_2n\rfloor+1 \] -
如果对一个有\(n\)个结点的完全二叉树\(T\)的结点按层序(从第一层到第\([logn]+1\)层,层内从左到右从1开始编号,则对任意一个编号为\(i(1<=i<=n)\)的结点有:
-
如果\(i=1\),则该结点是二叉树的根,无双亲;如果\(i>1\)则其双亲结点\(Parent(i)\)的编号为\([i/2]\)
-
如果\(2i>n\),则编号为\(i\) 的结点没有左孩子,为叶子结点;否则其左孩子\(LChild(i)\)的编号为2i
-
如果\(2i+1>n\),则编号为\(i\) 的结点没有右孩子;否则其右孩子\(RChild(i)\)的编号为\(2i+1\)
-
-
第6章 图
图遍历运用——迷宫问题
如何得到路径?
- 广度遍历时,队列元素增加一个 指向“队头”元素的指针
//代码实现
typedef struct {
int xpos;
int ypos;
}PosType;//迷宫每个单元信息
typedef struct DQNode{
PosType seat;
struct DQNode *next, *pre;//pre用于反向指向,找得到邻结点
}DQNode,*Dqueueptr;
typedef struct {
Dqueueptr front;
Dqueueptr rear;
}DlinkQueue;
//入队列
void EnQueue(DLinkQueue &Q,PosType e){
p=new DQNode;
p->seat.xpos=e.xpos;
p->seat.ypos=e.ypos;
p->next=NULL;
if(!Q.rear){ //首个结点
p->pre=NULL;//必须要写这句话
Q.rear=p;
Q.front=p;
}else{
p->pre=Q.front;
Q.rear->next=p;
Q.rear=p;
}
//出队列 自己写一下 不销毁结点
/*因Dequeue时找出邻接点, 如果先GetHead,并且Dequeue不销毁,
这样可以建立当前结点与队首结点关系*/
void DeQueue(){
}
//下一个结点
PosType NextPos(PosType cur, int v)
{
Postype npos;
npos.xpos=cur.xpos+di[v];
npos.ypos=cur.ypos+dj[v];
return npos;
}
bool Pass(Postype npos)
{
return(0<=npos.xpos && npos.xpos<=m-1 &&
0<=npos.ypos && npos.ypos<=n-1 &&
maze[npos.xpos][npos.ypos]= =0 &&
visited[npos.xpos][npos.ypos]= =FALSE)
}
bool ShortestPath(int maze[][], int m, int n, Stack &s){
//Stack起反向作用
DLinkQueue Q; bool visited[m][n]; InitQueue(Q);
for(i=0;i<m;i++)
for(j=0;j<n;j++)visited[i][j]=FALSE;
EnQueue(Q,(0,0)); visited[0][0]=TRUE;found=FALSE;
while(!found&&!QueueEmpty(Q)){//广度优先遍历可以中途停止当作指标
GetHead(Q,curp); //GetHead函数写出来一下,获得Q队首元素curp
for(v=0;v<8 &&!found;v++){
npos=NextPos(curp,v);
if(Pass(npos)){
EnQueue(Q,npos);//入队列
visited[npos.xpos][npos.ypos]=TRUE;
if(npos.xpos==m-1 && npos.ypos==n-1)//!!!!!!
found=TRUE;
}
}//for
DeQueue(Q,curp);
}//while
if(found){
InitStack(S);
p=Q.rear;
while(!p){
Push(S,p->seat); //自栈顶至栈底为路径
p=p->pre;
}//while
return TRUE;
}//if
else return FALSE;
}//ShortestPath
-
总结:
把问题抽象为图问题,怎么保存路径,销毁结点
6.4 最小生成树(MST)
-
相关概念:
极小连通子图:
n个结点的连通图中,包涵n个结点和n-1个边构成的连通子图
连通图的生成树:即极小连通子图
连通网的最小生成树:权值和最小的生成树
求连通网最小生成树的算法
– 克鲁斯卡尔(Kruskal)算法 复杂度:O(\(eloge\))
– 普里姆(Prim)算法 复杂度:O( \(n^2\) )
– 算法比较:当e(边)与\(n^2\) 差不多时,采用Prim算法快;当e远小于\(n^2\) 时,采用Kruskal算法快
-
Kruskal算法
- 算法思想
- 构造只含n个结点的森林。
-
按权值从小到大选择边加入到森林中,并使森林不产生回路。
-
重复2直到森林变成一颗树
- 算法描述
-
设\(G(V,E)\),把V={1,2,......n}看成孤立的n个连通子图。边按照权的非递减次序排列。
-
顺序查看边。对于第k条边(v,w),如果v,w分别属于两个连通字图\(T_1\)、T2,则用边(v、w)将T1、T2连成一个连通字图。
-
重复2,直至n个结点同属于一个连通图
太过复杂,本课不要求
-
Prim算法
算法思想 复杂度O(\(n^2\))
-
将所有结点分为两类集合:一类是已经落在生成树上的结点集合,另一类是尚未落在生成树上的结点集合。
-
在图中任选一个结点v构成生成树的树根,形成生成树结点集合。
-
在连接两类结点的边中选出权值最小的边,将该边所连接的尚未落在生成树上的结点加入到生成树上。同时保留该边作为生成树的树枝。
-
重复3直至所有结点都加入生成树
算法描述
\[\begin{align} &1.设G=(V,E),权A[m,n],令U=\{1\}。\\ &2.if(U<V) 取min(A[i,j]),使i∈U,j∈V-U。\\ &3.将j加入U\\ &4.重复2、3,直至U=V\\ \end{align} \] -
//代码实现
//利用数组记录权值然后遍历比较出最小权
void Prim(MGraph G, int v0, int adjvex[]){
//从序号为v0的顶点出发,构造连通网G的最小生成树
int lowcost[MAX_VERTEX_NUM]; //定义辅助数组
for(j=0;j<G.vexnum;j++) //用邻接矩阵的v0行初始化lowcost
if(j!=v0){lowcost[j]=G.arcs[v0][j];adjvex[j]=v0;}
lowcost[v0]=0; //将v0标记为红点
for(i=1;i<G.vexnum;i++){
k=MinEdge(lowcost, G.vexnum); //lowcost[]中非零最小值
printf("(%d,%d),%d\n",k,adjvex[k],lowcost[k]);
lowcost[k]= 0; //vk变成红点
for(j=0;j<G.vexnum;j++){ //调整紫边集
if(G.arcs[k][j]<lowcost[j]){
adjvex[j]=k;//将vj的候选紫边所关联的红点改成vk
lowcost[j]=G.arcs[k][j]; //调整vj候选紫边的权
}
}
}//Prim