n(n >= 0)个节点的有限集合,n=0 为空树
n>0 时,集合满足以下条件
1.有且仅有一个称为根的节点
此节点无前驱,有0~多个后继
2.除根节点外的n-1个结点可划分成m(m>=0)个互不相交的有限集(T1,T2,T3,…,Tm)
每个节点有为一个树,称为根的子树,每颗子树的根节点有且仅有一个直接前驱,其前驱为根结点,同时可有0~多个直接后继节点。
基本结构
public class SeqBiTree {
private int MAX_SIZE;//最大可容纳节点数,用于申请空间
private int DEFAULT_SIZE = 20;//默认可容纳节点数
private Object[] seqBiTree;//存放所有节点,0号单元不用
private int nodeMAX = 0;//最后一个节点下标
public SeqBiTree() {
this.MAX_SIZE = DEFAULT_SIZE;
this.seqBiTree = new Object[this.MAX_SIZE+1];
}
public SeqBiTree(int MAX_SIZE) {
this.MAX_SIZE = MAX_SIZE;
this.seqBiTree = new Object[this.MAX_SIZE+1];
}
}
树的表现及对应到数据结构
树的表现
1
/ \
2 3
/ \ / \
4 3 6 7
/ \
8 9
对应到数据结构
为便于计算其父子关系等0号单元不使用
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|---|
值 | /\ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
应用
链式结构基础代码
public class LinkedBiTree {
class BiNode{
Object data;
BiNode Lchild;
BiNode Rchild;
public BiNode(Object data) {
this.data = data;
this.Lchild = null;
this.Rchild = null;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public BiNode getLchild() {
return Lchild;
}
public void setLchild(BiNode lchild) {
Lchild = lchild;
}
public BiNode getRchild() {
return Rchild;
}
public void setRchild(BiNode rchild) {
Rchild = rchild;
}
}
//整个二叉树的根,后续的节点扩展以此为基础
private BiNode root;
public LinkedBiTree(Object data) {
this.root = new BiNode(data);
this.Lchild = null;
this.Rchild = null;
}
}
策略
遍历顺序
示例:
树结构:
A
/ \
B C
\ / \
D E F
/ /
G H
遍历结果:
基本代码显示
public class LinkedBiTree {
private BiNode root;
public LinkedBiTree() {
this.root.data = null;
this.root.Lchild = null;
this.root.Rchild = null;
}
public LinkedBiTree(Object data) {
this.root = new BiNode();
this.root.data = data;
this.root.Rchild = null;
this.root.Lchild = null;
}
//先序遍历
public void PreOrder(BiNode tRoot) {
if (null != tRoot) {
visit(tRoot.data);
PreOrder(tRoot.Lchild);
PreOrder(tRoot.Rchild);
}
}
//中序遍历
public void InOrder(BiNode tRoot) {
if (null != tRoot) {
PreOrder(tRoot.Lchild);
visit(tRoot.data);
PreOrder(tRoot.Rchild);
}
}
//后序遍历
public void PostOrder(BiNode tRoot) {
if (null != tRoot) {
PreOrder(tRoot.Lchild);
PreOrder(tRoot.Rchild);
visit(tRoot.data);
}
}
public void visit(Object data) {
System.out.print(" " + data);
}
public BiNode getRoot() {
return root;
}
public void setRoot(BiNode root) {
this.root = root;
}
//基本结点
class BiNode {
Object data;
BiNode Lchild;
BiNode Rchild;
public BiNode() {
}
public BiNode(Object data) {
this.data = data;
this.Lchild = null;
this.Rchild = null;
}
}
代码示例:
//先序遍历
void PreOrder(BiTree root){
SeqStack *S;
BiTree p;
InitStack(S);
p = root;
while(p != NULL || !IsEmpty(s)){
while (p != NULL){
Visit(p->data);
Push(S, p);
p=p->LChild;
}
if (!IsEmpty(S)){
Pop(S, &p);
p=p->RChild;
}
}
}
//中序遍历
void InOrder(BiTree root){
SeqStack *S;
BiTree p;
InitStack(S);
p=root;
while (p != NULL || !IsEmpty(S)){
while (p != NULL){
Push(S, p);
p=p->LChild;
}
if (!IsEmpty(S)){
Pop(S, &p);
Visit(p->data);
p=p->RChild;
}
}
}
//中序遍历
void InOrder2(BiTree root){
SeqStack *s;
InitStack(S);
p=root;
while (p != NULL || !IsEmpty(S)){
if (p != NULL){
Push(S, p);
p=p->LChild;
}else {
Pop(S, &p);
Visit(p->data);
p=p->RChild;
}
}
}
//后序遍历
void PostOrder(BiTree root){
SeqStack *S;
BiTree p, q;
InitStack(S);
p=root;
q=NULL;
while (p != NULL || !IsEmpty(S)){
while (p != NULL){
Push(S, p);
p=p->LChild;
}
if (!IsEmpty(S)){
Top(S, &p);
if (p->RChild == NULL || p->RChild == q){
Pop(S, &p);
Visit(p->data);
q=p;
p=NULL;
}else{
p=p->RChild;
}
}
}
}
C写法
//统计结点个数
void PreOrder(BiTree root){
if (root){
Count++;
PreOrder(root->LChild);
PreOrder(root->RChild);
}
}
C写法
//输出叶结点
void InOrder(BiTree root){
if (root){
inPreOrder(root->LChild);
if (root->LChild==NULL && root->Rchild==NULL){
print(root->data);
InOrder(root->EChild);
}
}
}
C写法
//统计叶子结点数目
int leaf(Bitree root){
int nl, nr;
if (root==NULL) return 0;
if ((root->LChild==NULL) && (root->RChild==NULL)){
nl=leaf(root->LChild);
nr=leaf(root->RChild);
return (nl+nr);
}
}
C写法
//数高度,全局变量
int depth;
//二叉树高度
void TreeDepth(BiTree root, int h){
if (root){
if (h>depth) depth=h;
TreeDepth(root->LChild, h+1);
TreeDepth(root->RChild, h+1);
}
}
//二叉树高度
int PostTreeDepth(BiTree root){
int hl, hr, h;
if (root==NULL) return 0;
else {
hl=PostTreeDepth(root->LChild);
hr=PostTreeDepth(root->RChild);
h=(hl>hr ? hl : hr) + 1;
return h;
}
}
C写法
//结点双亲
BiTree parent(BiTree root, BiTree current){
BiTree * p;
if (root==NULL) return NULL;
if (root->LChild==current || root->RChild==current){
return root;
}
p=parent(root->LChild, current);
if (p!=NULL) return p;
else return(parent(root->RChild, current));
}
C写法
//二叉树相似判定
int like(BiTree t1, BiTree t2){
int like1, link2;
if (t1==NULL && t2==NULL) return 1;
else if(t1==NULL || t2 == NULL) return 0;
else {
like1=like(t1->LChild, t2->LChild);
like2=like(t1->RChild, t2->Rchild);
return (like1 && like2);
}
}
C写法
//树状打印二叉树
void PrintTree(BiTree root, int h){
if (root==NULL) return;
PrintTree(root->RChild, h+1);
for (int i=0; i<h; i++){
printf(" ");
}
printf("%c\n", root->data);
PrintTree(root->LChild, h+1);
}
先序先访问根,再左子树,再右子树,故先序=>根(第一个结点)
中序先访问左子树,再根,再右子树,故中序=>左子树(根的左侧)
后序先访问左子树,再右子树,再根,故后续=>根(最后一个结点)
先序第一个结点=>根
由根拆分中序=>左子树+右子树
由左子树结点=>先序=根节点+左子树先序+右子树先序
后序最后一个结点=>根
由根=>分割中序=>中序=左子树中序+根+右子树中序
由左子树结点个数分割后序=>后序=左子树后序+右子树后序+根
结点有左子树,LChild指向左孩子,否则LChild指向某种遍历序列中的直接前驱结点
结点有右子树,RChild指向右孩子,否则RChild指向某种遍历序列的直接后继节点
定义:
| LChild | LTag | Data | RTag | RChild |
中序线索化
//中序线索化
void Intread(BiTree root){
if (root!=NULL){
Inthread(root->LChild);
if (root->LChild==NULL){
root->LChild=pre;
root->LTag=1;
}
if (pre!=NULL && pre->RChild==NULL){
pre->RChild=root;
pre->RTag=1;
}
pre=root;
Inthread(root->RChild);
}
}
中序线索树找前驱
//中序找结点前驱
BiThrTree InPre(BiThrTree p){
if (p->Ltag=1) pre=p->LChild;
else{
for (q=q->LChild; q->Rtag==0; q=q-RChild);
pre=q;
}
return (pre);
}
中序找结点后继
//中序找结点后继
BiThrTree InNext(BiTree p){
if (p->Rtag == 1) next=p->RChild;
else{
for (q=p->Rchild; q->Ltag==0; q=q->LChild);
next=q;
}
return (next);
}
中序找遍历的第一个结点
//中序线索树中求遍历的第一个结点
BiThrTree InFirst(BiThrTree bt){
BiThrTree p=bt;
if (p!=NULL) return (NULL);
while (p->Ltag==0) p=p->LChild;
return p;
}
遍历中序二叉线索树
//遍历中序线索树
void TinOrder(BiThrTree root){
BiThrTree p;
p=InFirst(root);
while (p!=NULL){
Visit(p->data);
p=InNext(p);
}
定义
#define MAX 100
typedef struct TNode{
DataType data;
int parent;
}TNode;
//树的定义
typedef struct{
TNode tree[MAX];
int root;
int num;
}PTree;
存储
Data | Parent |
---|---|
结点数据 | 双亲索引 |
缺点:查找某个孩子时需要在整个数组中寻找
定义
typedef struct ChildNode{
int Child;
Struct ChildNode * next;
}ChildNode;
//顺序表结构定义
typedef struct {
DataType * FirstChild;
}DataNode;
//树定义
typedef struct{
DataNode nodes[MAX];
int root;
int num;
}CTree;
结构
Data | firstChild |
---|---|
数据 | 无孩子=>NULL,有孩子,此处指向孩子组成的链表(头结点) |
缺点
改进
在每个结点中设立Parent域,形成带双亲的孩子的表示法
结构
Data | Parent | firstChild |
---|---|---|
数据 | 双亲索引地址 | 无孩子=>NULL,有孩子,此处指向孩子组成的链表(头结点 |
定义
//孩子兄弟表示法
typedef struct CSNode{
DataType data;
Struct CSNode * FirstChild;
Struct CSNode * NextSibling;
}CSNode, * CSTree;
操作
树非空
路径:从树的一个结点到另一个结点之间的分支序列构成两个结点见的路径
路径长度:路径上分支的条数
树的路径长度:从树根到每个结点的路径长度之和
结点的权:节点所带有的数值
带权路径长度:结点到树根的路径长度与结点的权的乘积
树的带权路径长度:树中所有叶子结点的带权路径长度之和
WPL = Wk x Lk(1<= k <=n)
最优二叉树:叶子个数确定,叶子权值确定,带权长度WPL值最小的二叉树
存储结构
概念
weight | Parent | Lchild | Rchild |
---|---|---|---|
权值 | 双亲结点数组中下标 | 左孩子数组中下标 | 右孩子数组中下标 |
定义
#define N 30
#define M 2*N-1
typedef struct {
int weight;
int parent, Lchild, Rchild;
}HTNode, HuffmanTree[M+1]; //0号单元不用
算法
C实现
//建立哈夫曼树
void CrtHuffmanTree(HuffmanTree ht, int w[], int n){
m=2*n-1;
for (i=1; i<=n; i++){
ht[i] = {w[i], 0, 0, 0}
}
for (i=n+1; i<=m; i++){
ht[i] = {0, 0, 0, 0}
}
for (i=n+1; i<=m; i++){
select(ht, i-1, &s1, &s2);
ht[i].weight = ht[s1].weight + ht[s2].weight;
ht[i].Lchild = s1;
ht[i].Rchild = s2;
ht[i].parent = i;
ht[s2].parent = i;
}
}
顶点结构
vexdata | head |
---|---|
图的边结构
adjvex | next |
---|---|
网的边结构
adjvex | weight | next |
---|---|---|
边表结点结构
tailvex | headvex | hnextarc | tnextarc | info |
---|---|---|---|---|
弧尾的顶点序号 | 弧头顶点序号 | 同一弧头顶点的下一条弧 | 同一弧尾的下一条弧 | 弧权值等信息 |
顶点表结点结构
vexdata | head | tail |
---|---|---|
顶点相关数据信息 | 以该顶点为弧头的边表头指针 | 以该顶点为弧尾的边表头指针 |
边表结点结构
mark | jvex | inext | jvex | jnext | weight |
---|---|---|---|---|---|
顶点表结点结构
vexdata | head |
---|---|