数据结构有:逻辑结构、存储结构和数据的操作运算
其中逻辑结构包括线性结构和非线性结构
线性结构有 线性表 队列 栈 串
非线性结构有 树 图
数据的操作运算有 排序 查找
其实整本数据结构教材就是按照这个逻辑去编写的,在讨论逻辑结构的时候,会研究存储结构
树
树是结点与结点之前存在一种一对多的关系
最常见的就是二叉树,是指每个结点最多有两个子结点。子结点的个数称为结点的度
二叉树 度为0的结点个数=度为2的结点个数+1;
二叉树第i层上最多有2^i-1个结点
满二叉树:每一层都要放2^i-1个结点
完全二叉树:n-1层都要放2^i-1个结点,最后一层必须要有结点,但是可以不放满
树的存储结构:
双亲表示法:以一个数组的形式表示,每个元素包含两个数据:一个是自己本身的信息,还有一个是双亲结点的信息
孩子表示法:双亲表示法不足以了解孩子结点的信息,于是有了孩子表示法,每个结点的长度根据结点的度来决定,结点指针域的个数等于度的个数,并且还要再需要一个空间来存放指针域的个数,每个结点是一个链表,n个结点的头指针组成一个单链表,存放在一维数组中
孩子兄弟表示法:任何一个结点,如果存在长子结点或者有兄弟结点,这些结点一定是唯一的,于是有了孩子兄弟存储方式,这个结点有一个数据域,用于存放结点本身的信息,此外,还有两个指针域,用于存放结点的第一个子结点和兄弟结点
树的遍历
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
定义树:
typedef char ElementType;
typedef struct TNode *Node;
typedef Node BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
先序遍历:(采用了递归调用,用到了栈保留现场)
void fun(BinTree BT)
{
if(BT==NULL) return;
printf("%c",BT->Data);
fun(BT->Left);
fun(BT->Right);
}
中序遍历和后序遍历类似
已知前序遍历和后序遍历,不能得到唯一的二叉树
层次遍历:用队列实现
在这棵树中,层次遍历时,A先入队,A出队,访问A的子树,BC入队,BC出队,又访问B和C的子树
树和森林
(1)将树转换成二叉树
转换规则:
相邻的兄弟结点变成右孩子结点
(2)将森林变成二叉树
转换规则:
先将每棵树变成二叉树,因为第二棵树的根结点是第一棵树的兄弟结点,所以第二棵树是第一棵树的右子树,依次类推。
二叉树的线索化
什么是二叉树的线索化?为什么要对二叉树进行线索化?
(1)对于一棵二叉树,只能知道该结点的左右孩子的信息,而不知道直接的前驱结点和后继结点。
(2)二叉树在存储时,会存在叶子结点或者有一个左结点或者右结点为空的情况,一共会有n+1个空指针域,这样一来会导致一些空间无法合理的利用,于是就有了线索二叉树。
一般的二叉树的结点只有根结点和左子树结点以及右子树结点。
而线索二叉树又新引进了两个标志位,当左子树为空时,就让当前结点指向该结点的前驱。标志位用来判断该结点是存放左子树还是前驱结点。同样,当右子树为空时,存放的是该结点的后继结点。
哈夫曼树(也称为最优二叉树):带权路径长度最小的二叉树
例题:假设 A、B、C、D、E出现的频率是5 4 3 2 1
求哈夫曼树
构造规则:
找出出现频率最低的两个结点,放在最下层,小的在左边,大的在右边,并且将这两个结点的值之和组成的新结点加入到频率表中,这样又从里面找最小的两个构成结点,依次连起来,构成一个二叉树
接着就是编码,从上往下,从根结点开始,每个结点的左子树都是0,右子树是1.最终会得到每个结点的哈夫曼编码。
应用场景:在压缩和解压文件时得到应用,出现频率越高的字符,所编码的字节就越短,这样传输效率才高。
用非递归算法求二叉树的高度:
用层次遍历,利用队列,每遍历完一层,高度值增1.