数据结构
- 逻辑结构
- 线性结构:线性表,栈,队列,串,数组
- 非线性结构:树结构,图结构
- 物理存储结构
- 顺序结构
- 链式结构
- 索引结构
- 散列结构
树
节点拥有的子树数目称为节点的度;
度为0则是叶子节点或者终端节点;
树的度是树内各节点的度的最大值;
- 结点的层次:根为第一层,根的孩子为第二层;节点的最大层次称为树的深度或者高度
- 树的存储
- 双亲表示法:假设以一组连续空间存储树的节点,同时在每个节点中,附设一个指示器指示其双亲结点到的链表中的位置;也就是说每个结点除了知道自己是谁,还知道自己的双亲是谁
data parent 数据域,存储节点数据信息 指针域,指示存储该节点的双亲在数组中的下标
#define MAX_TREE_SIZE 100
typedef int TElemType;
typedef struct PTNode {
TElemtype data; //结点数据
int parent; //双亲位置
}PTNode;
typedef struct { //树结构
PTNode nodes[MAX_TREE_SIZE]; //结点数组
int r, n; //根的位置和结点数
}PTree;
- 孩子表示法:每个结点有多个指针域,其中每个指针指向一个树的根结点,我们把这种方法叫做多重链表表示法
- 把每个结点的孩子结点排列起来,以单链表作存储结构,则n个节点有n个孩子链表,如果是叶子结点则此单链表为空。然后n个头指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中;
设计2种结点数据
- 孩子结点
-
child next 数据域,存储某个节点在表头数组中下标 指针域,用来存储指向该节点的下一个孩子结点的指针 - 表头数组的表头结点
child firstchild 数据域,存储节点数据信息 头指针域,用来存储该节点的孩子链表的头指针 - 双亲孩子表示法
- ![孩子双亲表示法](https://img-blog.csdn.net/20170508231452384?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTEM5MDA3MzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
满二叉树
所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层,这样的二叉树称为满二叉树; ![满二叉数](https://img-blog.csdn.net/20170508232149950?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvTEM5MDA3MzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)完全二叉树
一棵具有n个节点的二叉树,如果编号i(1typedef int TElemtype; typedef struct BiTNode { TElemtype data; struct BiTNode *lchild, *rchild; }BiTNode,*BiTree;
二叉树遍历
指的是从根结点遍历,按照某种顺序依次访问二叉树中的所有结点,使得每个结点被访问依次且被访问一次。
这里有2个关键词:次序和访问;我们用图形形式来表现树的结构,但是对计算机来说只有循环和判断等来处理;
- 前序遍历:先访问根结点,在前序遍历左子树,在右字树
- 中序遍历:先左子树,然后根结点,然后是右子数
- 后序遍历:先左子树,然后右结点,然后是根结点
- 层序遍历:第一层开始访问,从上到下
-
前序遍历算法
二叉树的定义是递归,所以实现遍历算法也可以实现递归;
typedef int TElemtype; typedef struct BiTNode { TElemtype data; struct BiTNode *lchild, *rchild; }BiTNode,*BiTree; void PreOrderTraverse(BiTree T) { if (T == NULL) { return; } printf("%c", T->data); //显示结点数据,可以更改为对其他结点操作 PreOrderTraverse(T->lchild);//先序遍历左子树 PreOrderTraverse(T->rchild);//最后先序遍历右子树 }
中序遍历算法
//二叉树中序遍历算法 typedef int TElemtype; typedef struct BiTNode { TElemtype data; struct BiTNode *lchild, *rchild; }BiTNode,*BiTree; void InOrderTraverse(BiTree T) { if (T == NULL) { return; } PreOrderTraverse(T->lchild);//中序遍历左子树 printf("%c", T->data); //显示结点数据,可以更改为对其他结点操作 PreOrderTraverse(T->rchild);//最后中序遍历右子树 }
二叉树的建立
//建立二叉树,也是利用了递归的原理。
typedef int TElemtype; typedef struct BiTNode { TElemtype data; struct BiTNode *lchild, *rchild; }BiTNode,*BiTree; void CreateBiTree(BiTree *T) { TElemtype ch; scanf("%c", &ch); if (ch == '#'){ *T = NULL; } else { *T = (BiTree)malloc(sizeof(BiTNode)); if (!*T) { exit(OVERFLOW); } else { (*T)->data = ch; CreateBiTree(&(*T)->lchild); //构造左子树 CreateBiTree(&(*T)->rchild); //构造右子树 } } }
线索二叉树
指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树就称为线索二叉树(Threaded Binary Tree)
我们如何知道某一结点的lchild是指向左孩子还是直接前驱?rchild是指向右孩子还是指向后驱?因此我们需要在每个结点在增加2个标志位ltag和rtag,其中ltag和rtag只存放0或者1数字的布尔型变量;结构如下
lchild ltag data rtag rchild ltag 为0时候指向结点左孩子,为1指向结点的前驱 rtag 为0时候指向结点右孩子,为1时候指向结点后驱 /*Link==0表示指向左右孩子指针;Thread==1表示指向前驱后后驱的线索 typedef enum(Link,Thread) PointerTag; typedef struct BiThrNode(){ TElemType data; //结点数据 struct BiThrNode *lchild,*rchild; //左右孩子指针 PointerTag LTag; //左标志 PointerTag RTag; //右标志 }BiThrNode,*BiThrTree;
线索化的实质上就是将二叉链表中的空指针改为指向前驱或者后继的线索。由于前驱和后继的信息只有在遍历该二叉树的时候才能得到,所以线索化的过程就是在遍历的过程中修改空指针的过程。
BiThrTree pre;//全局变量,始终指向刚刚访问过的结点 /*中序遍历进行中序线索化*/ void InThreading(BiThrTree p){ if(p){ InThreading(p->lchild); /*递归左子树线索化*/ if(!p->lchild){ /*没有左孩子*/ p->LTag=Thread; //前驱线索 p->lchild=pre; /*左孩子指针指向前驱*/ } if(!pre->rchild){ /*前驱没有右孩子*/ pre->RTag=Thread; pre->rchild=p; /*前驱右孩子指针指向后继(当前结点p)*/ } pre=p; InThreading(p->rchild); /*递归右子树线索化*/ } }