数据结构——树
树是有限集合: 有一个根。
root 根 subtree 子树
节点拥有的子树数成为结点的度;度为0 的节点称为叶节点
树结构: 根结点 无双亲 唯一, 叶节点: 无孩子 可以多个 中间节点 一个双亲多个节点。
抽象数据:
ADT:树
Data: 一个结点和若干个字数 树中节点句用相同的数据类型
operation:
InitTree(*T):
DestoryTree(*T)
createTree(*T)
CleareTree(*T)
TreeEmpty(T);
TreeDepth();
Root(T)
Value(T,cur_e); 返回结点的值
Assign(T,cur_e,value);给树的节点赋值
Parent(T,cur_e);
LeftChild(T,cur_e);
RightChild(T,cur_e);
InsertChild(T,*p,i,c); P指向T的某个节点 i为指向P的度加1
DeleteChild(*T,*p,i); p指向T的某个节点,i为节点p的度 删除T中P所指向的第i棵子树
endData
data : parent
#define MAX_TREE_SIZE 100
typedef int elemtype;树的数据类型
typedef struct PTNode 树的节点
{
elemtype data;
int parent;
}PTNode;
typedef struct 树的结构
{
PTNode node[MAX_TREE_SIZE]; 节点数组
int r,n; 根的位置和节点数
} Ptree;
表示双亲,根结点的位置域为-1
下表 data parent
0 A -1
1 B 0
2 C 0
3 D 1
4 E 2
5 F 2
6 G 3
7 H 3
8 I 3
9 J 4
很容易找到双亲时间复杂度 O(1)
找孩子节点 要便利整个结构
设置孩子域:
index data parent fristchild
0 A -1
把每个结点的孩子排列起来,以单链表作为i存储结构。则n个节点有n个孩子链表
data fristchild(指针域)
#defiine MAX_TREE_SIZE 100
typedef struct CTNode 节点
{
int child;
struct CTNode *next;
}*ChildPtr;
typedef struct 表头结构
{
elemtype data;
ChildPtr fristchild;
}CTBox;
typedef struct
{
CTBox nodes[MAX_TREE_SIZE];
int r,n;
}CTree;
方便查找子节点 双亲节点麻烦
#defiine MAX_TREE_SIZE 100
typedef struct CTNode 节点
{
int child;
int parent;
struct CTNode *next;
}*ChildPtr;
typedef struct 表头结构
{
elemtype data;
ChildPtr fristchild;
}CTBox;
typedef struct
{
CTBox nodes[MAX_TREE_SIZE];
int r,n;
}CTree;
双亲孩子表示法
typedef struct CSNode
{
elemtype data;
struct CSNode *fristchild , *rightsid;
} CSNode,*CSTree;
二叉树:
1 度不大于2 有顺序
状态:
空二叉树
只有一个个根结点
二叉树的链表表示:
typedef struct BitNode
{
elemtype data;
struct BitNode *lchild,*rchild;
}BitNode,*BiTree;
前序遍历: 先访问根结点 再访问左节点 后访问右节点
void preOrderTraverse(BiTree T)
{
if(T == NULL)
return;
printf("%c",T->data)
preOderTraverse(T->lchild);
preOderTraverse(T->rchild);
中序遍历: 从根开始(不访问)先访问左字数 在访问根 最后访问右子树
void InOderTraverse(BiTree T)
{
if(T == NULL)
return;
InorderTraverse(T->lchild);
printf("%c",T->data);
InorderTraverse(T->rchild);
}
后序遍历: 先访问左右子节点, 最后访问根结点
void PostOrderTravese(BiTree T)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild);
PostOrderTreavese(T->rchild);
printf("%c",T->data);
}
层序遍历: 逐层访问
二叉树的建立:
void CreateBiTree(BiTree *T)
{
elemtype ch;
scanf("%c",&ch);
if(ch == '#')
{
*T=NULL;
}
else
{
*T = (BiTree)malloc(sizeof(BiTNode);
if(!*T)
exit(OVERFLOW);
(*T)->data = ch;
CreateBigTree(&(*T)->lchild); //构建左子树
CreateBiTree(&(*T)->rchild);
}
return;
对于n个结点的二叉树链表,每个节点指向左右孩子的两个指针,所以共有2n个指针与,而n个结点的二叉树有n-1条分支线树 存在2n-(n-1) = n+1个空指针
}
线索二叉树结构的实现:
typedef enum {link, Thread} PointerTag; Link ==0 表示指向左右孩子
Thread 表示指向前驱或则后记
typedef struct 而茶线索存储结构
{
elemtype data;
struct BigThrNode *lchild,*rchild;
PointterTag Ltag; 标志
PointerTag rtag;
}BigThrNode,*BigThrTree;
实质是将空指针改编为前去或则后记
在便利中修改指针:
BiTree pre; 全局变量
void InThreading(BiThrTree p)
{
if(p)
{
InThreading(p->lchild);
if(!p->lchild)
{
p->Ltag=Thread; //前驱线索
p->lchild = pre;//左孩子指向前驱
}
if(!p->rchild) //设置前驱的后继
{
pre->RTag = Thread;
pre->rchild = p;
}
pre = p;//保持pre指向p的前驱
InThreading(p->rchild);第归右子树线索化
//中序遍历一样
}
}
遍历:
T 指向头结点 头结点左联lchild指向根结点 头结点右连rchild 指向中序遍历的最后一个结点 中序遍历二叉线索表 表示的二叉树
status InOrderTraverse_Thr(BiTree T)
{
BiThrTree p;
P=T->lchild; p 指向根结点
while(p!=T) 空树便利结束
{
while(p->LTag ==link) // 当LTag ==0 时循环到中序序列第一个节点
p=p->lchild;
printf("%c",p->data);
}
while(p->RTag == Thead && p->rhcild!=T) //p->rchild 不等于头结点 p是一个后记指针
{
p=p->rchild;//指向后继
printf("%c",p->data);
}
}return ok;
图:grap 有穷非空个顶点
G(V,E) G 是图 V顶点集合 E 边的集合
无向边 v1 到 v2 没有方向 无向边 (v1,v2);
任意两个顶点之间的边都是无向边, 陈为无向图 顶点的边无序对 (A,D)
V无向图 G1=(V1,{E1}), 顶点集合 V1={A,B,C,D} 边集合 E1={(A,B),(B,C),(D,A),(A,C)}
有向边: 有方向的边,也成为弧 <A,D> A弧尾 D为弧头 有向图 G2=(V2,{E2}); V2={A,B,C,D} E2 = {<A,D>,<B,A>,<C,A>,<B,C>};
与图的边相关的树: 权 带权的图称网
子图
顶点与边的关系: 相邻接 顶点的度是和A相关的边的数目
有相图的出度,《V,V1》v为弧尾弧的目 入度以v为头的弧的数目
path 是一个顶点序列
任意两个顶点,都是连同的 陈为连通图
极大连通子图陈为连通分量
任意两点都有路径 g是强连通图, 集强大连通子图成为有相图的强连通分量