文章目录
二叉树
树
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。
需要注意的是:子树之间不能有交集,否则就不是树形结构
树的相关概念
- 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为3
- 叶节点或终端节点:度为0的节点称为叶节点; 如上图:E、F、G、H、I 节点为叶节点
- 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
- 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
- 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为3
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
- 树的高度或深度:树中节点的最大层次; 如上图:树的高度为3
- 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
- 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
- 森林:由m(m>0)棵互不相交的树的集合称为森林
树的表示
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间
的关系 我们这里就简单的了解其中最常用的孩子兄弟表示法
typedef int DataType;
struct Node
{
struct Node* _firstChild1; // 第一个孩子结点
struct Node* _pNextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};
二叉树
概念
二叉树是一种不存在度大于2的节点,并且二叉树的子树是由左右之分的,次序不能颠倒,所以二叉树也是一种有序树
特殊的二叉树
- 满二叉树:如果一个二叉树的层数为K,且结点总数是2^k-1,则它就是满二叉树
- 完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树
二叉树的性质
- 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点
- 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是**2^(h-1).
- 对任何一棵二叉树, 如果度为0其叶结点个数为** n0 ,度为2的分支结点个数为n2,则有 n0=n2+1
- . 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log(n+1)
- 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对
于序号为i的结点有:若i > 0,i位置的父节点为:(i - 1)/2 ,i==0时,i为根节点,没有父节点;若2i+1 < n时,左孩子的序号:2i + 1;若2i+2 < n时,右孩子的序号:2i+2
堆
堆的概念
堆中的某个节点不能大于或者小于它的父节点的值,堆是一颗完全二叉树
某个节点不大于其父节点的值的堆叫做大根堆
某个节点不小于其父节点的值的堆叫做小根堆
堆的实现
堆的定义
堆是一种顺序存储的结构,所以我们以数组的形式存储,可以先定义一个堆结构
typedef int HPDataTypedef;
typedef struct HPNode
{
HPDataTypedef* a;
int size;//堆的元素个数
int capacity;//数组的空间容量
}HP;
堆的初始化
初始化堆比较简单,和之前的顺序表初始化一样
//堆的初始化
void HeapInit(HP* hp) {
assert(hp);
hp->a = NULL;
hp->size = hp->capacity = 0;
}
堆的销毁
//堆的销毁
void HeapDestroy(HP* hp) {
assert(hp);
hp->size = hp->capacity = 0;
free(hp->a);
hp->a = NULL;
}
堆的插入
向上调整法
堆的插入数据是在数组的尾插入的,但是这里需要注意的问题是插入数据后会破坏堆的结构,就不再是一个堆的。所以需要我们堆插入的数据进行调整,因为数据是在尾部插入,所以可以根据原本堆的性质进行往上的调整。
以大堆插入数据为例
//两数交换
void Swap(HPDataTypedef* a, HPDataTypedef* b) {
HPDataTypedef tmp = *a;
*a = *b;
*b = tmp;
}
//向上调整法
void Adjustup(HPDataTypedef* a, int child) {
//算出父节点的下标
int parent = (child - 1) / 2;
while (child > 0) {
//如果父节点小于子节点,两数交换
if (a[parent] < a[child]) {
Swap(&a[parent], &a[child]);
//交换完后父节点和子节点需要更新
child = parent;
parent = (child - 1) / 2;
}
//调整好后推出循环
else
break;
}
}
//堆的插入
void HeapPush(HP* hp, HPDataTypedef x){
assert(hp);
//判断容量是否充足,不够的话需要增容
if (hp->size == hp->capacity) {
int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
HPDataTypedef* tmp = (HPDataTypedef*)realloc(hp->a, sizeof(HPDataTypedef) * newcapacity);
if (tmp == NULL) {
perror("malloc fail");
return;
}
hp->a = tmp;
hp->capacity = newcapacity;
}
//将值赋给最后一个节点
hp->a[hp->size++] = x;
//进行向上调整
Adjustup(hp->a, hp->size - 1);
}
堆的删除
删除堆是删除堆顶的数据,也就是根的数据。由于堆是以数组的结构存储的,所以只需要把第一个元素(根节点元素)和最后一个元素交换,然后元素个数整体减1就可以删除掉。但是有一个问题就是,互换元素之后会导致堆的性质改变,根节点元素就不符合堆的性质,所以这里就需要往下去做调整,就要用到向下调整法
向下调整法
以大堆删除为例
//向下调整法
void Adjustdown(HPDataTypedef* a, int n