目录
1.树概念及结构
1.1树的概念( 定义)
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集
合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
注意:树形结构中,子树之间不能有交集,否则就不是树形结构
1.2 树的相关概念
节点的度 | 一个节点含有的子树的个数称为该节点的度 |
叶节点或终端节点 | 度为0的节点称为叶节点 |
非终端节点或分支节点 | 度不为0的节点 |
双亲节点或父节点 | 若一个节点含有子节点,则这个节点称为其子节点的父节点 |
孩子节点或子节点 | 若一个节点含有父节点,则这个节点称为其父节点的子节点 |
兄弟节点 | 具有相同父节点的节点互称为兄弟节点 |
树的度 | 一棵树中,最大的节点的度称为树的度 |
节点的层次 | 从根开始定义起,根为第1层,根的子节点为第2层,以此类推 |
树的高度或深度 | 树中节点的最大层次(节点层次可以从0或1开始,这边认为从1开始) |
堂兄弟节点 | 双亲在同一层的节点互为堂兄弟 |
节点的祖先 | 从根到该节点所经分支上的所有节点 |
子孙 | 以某节点为根的子树中任一节点都称为该节点的子孙 |
森林 | 由m(m>0)棵互不相交的树的集合称为森林 |
1.3 树的表示
孩子兄弟表示法
typedef int DataType;
struct Node
{
struct Node* _firstChild1; // 第一个孩子结点
struct Node* _pNextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};
1.4 树在实际中的运用(表示文件系统的目录树结构)
2.二叉树概念及结构
2.1二叉树的概念
一棵二叉树是结点的一个有限集合,该集合:
1. 为空
2. 或由一个根节点加上两棵别称为左子树和右子树的二叉树组成
2.2二叉树的特点
1. 二叉树不存在度大于2的结点
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
2.3 特殊的二叉树
1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉
树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。
2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。
对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编
号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉
树。
2.4 二叉树的性质
1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点.
2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h - 1.
3. 如果度为0其叶结点个数为n0, 度为2的分支结点个数为n2 ,则有 n2= n0+1.
(结点数 = 度数之和 - 1) -> (n0 + n1 + n2 = 2 * n2 + n1 - 1) -> (n0 = n2 - 1)
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log2(n + 1).
(h= log2(n + 1) : 是log以2为底,n+1为对数)
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开
始编号,则对于序号为i的结点有:
1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0;若i为根节点编号,无双亲节点
2. 若2i+1<n,左孩子序号:2i+1;若2i+1>=n,无左孩子
3. 若2i+2<n,右孩子序号:2i+2;若2i+2>=n,无右孩子
2.5 二叉树的存储结构
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。
顺序结构:
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完
全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在
物理上是一个数组,在逻辑上是一颗二叉树。
链式结构:
二叉树的链式存储结构是指,用链表来表示一棵二叉树,指示元素的逻辑关系。
通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。
链式结构又分为二叉链和三叉链。
typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* _pLeft; // 指向当前节点左孩子
struct BinTreeNode* _pRight; // 指向当前节点右孩子
BTDataType _data; // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
struct BinTreeNode* _pParent; // 指向当前节点的双亲
struct BinTreeNode* _pLeft; // 指向当前节点左孩子
struct BinTreeNode* _pRight; // 指向当前节点右孩子
BTDataType _data; // 当前节点值域
};
3.二叉树的顺序结构及应用(堆)
3.1 二叉树的顺序结构
一般用顺序结构存储完全二叉树,而实际运用中,一般把堆用顺序结构的数组进行存储。
3.2 堆的概念及结构
堆的概念:
如果有一个关键码的集合K,把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: 父结点都小于(或大于)它的子结点,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值。
2.堆总是一棵完全二叉树。
3.3 堆的实现
3.3.1 堆向下调整算法
现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法:父节点大于较小的子节点,将两个节点进行交互。可以把它调整成一个小堆。
前提:左右子树必须是一个堆,才能调整。
3.3.2堆的创建
从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。
3.3.3 建堆时间复杂度
n - log2(n + 1) ~ O(N)
3.3.4 堆的插入
将要插入的数插入到数组的尾上,再进行一次向上调整算法。
3.2.5 堆的删除
删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。
3.2.6 堆的接口
typedef int HPDataType;
typedef struct Heap
{
HPDataType* _a;
int _size;
int _capacity;
}Heap;
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);
3.4 堆的应用
1.堆排序
堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆:
升序:建大堆。
降序:建小堆。
2.利用堆删除思想进行排序
以升序为例,建大堆(n个)完成后,在根节点位置(下标为0处)是最大的元素。
a.将 i0 处元素和最后一个元素 in 交换。
b.此刻对 i0 位置的新元素进行向下调整。能重新得到大堆(n - 1个)。
c.重复a,b直到堆的元素个数为1。
2.TOP-K问题(游戏排行榜)
TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。
最佳的方式就是用堆来解决,基本思路如下:
1. 用数据集合中前K个元素来建堆
前k个最大的元素,则建小堆。
前k个最小的元素,则建大堆。2.用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
替换之后,对堆顶元素进行向下调整以重新获得大(小)堆。
4.二叉树链式结构的实现
4.1二叉树的遍历
二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。
有四种遍历方式:
1. 前序遍历(Preorder Traversal )——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
4. 层序遍历:层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推。
4.2二叉树的接口
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);
5.堆和链式二叉树的接口实现(代码)
5.1堆
#include<assert.h>
typedef int hpdatatype;
typedef struct heap
{
hpdatatype* _a;
int _size; //是最后一个的下标
int _capacity;
}heap;
const int isbig = 0;//isbig 为1表示建大堆 为0表示建小堆
//========工具函数=========
//向下调整
void moveDown(Heap* hp, int now_rootnodei)
{
int lnodei = now_rootnodei * 2 + 1;
int rnodei = now_rootnodei * 2 + 2;
HPDataType put_node = 0;
if (lnodei > hp->_size)
{
return;
}
if (rnodei <= hp->_size)
{
if ((isbig == 1 && hp->_a[lnodei] < hp->_a[rnodei]) || (isbig == 0 && hp->_a[lnodei] > hp->_a[rnodei]))
{
lnodei ^= rnodei;
rnodei ^= lnodei;
lnodei ^= rnodei;
}
}
if ((isbig == 1 && hp->_a[now_rootnodei] < hp->_a[lnodei]) || (isbig == 0 && hp->_a[now_rootnodei] > hp->_a[lnodei]))
{
put_node = hp->_a[now_rootnodei];
hp->_a[now_rootnodei] = hp->_a[lnodei];
hp->_a[lnodei] = put_node;
moveDown(hp, lnodei);
}
}
//向上调整
void moveUp(Heap* hp, int now_nodei)
{
int rootnodei = (now_nodei - 1) / 2;//最小值为0
HPDataType put_node = 0;
if (now_nodei == 0 || rootnodei < 0)
{
return;
}
if ((isbig == 1 && hp->_a[rootnodei] < hp->_a[now_nodei]) || (isbig == 0 && hp->_a[rootnodei] > hp->_a[now_nodei]))
{
put_node = hp->_a[rootnodei];
hp->_a[rootnodei] = hp->_a[now_nodei];
hp->_a[now_nodei] = put_node;
moveUp(hp, rootnodei);
}
}
//=========================
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
int i = 0;
int last_rootnodei = (n - 2) / 2;
hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n * 2);
if (hp->_a == NULL)
{
perror("Create:");
return;
}
hp->_size = n - 1;
hp->_capacity = n * 2;
for (i = 0; i < n; i++)
{
hp->_a[i] = a[i];
}
for (i = last_rootnodei; i >= 0; i--)
{
moveDown(hp, i);
}
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
free(hp->_a);
hp->_capacity = 0;
hp->_size = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
HPDataType* new_heap = NULL;
hp->_size++;
if (hp->_size >= hp->_capacity)
{
new_heap = (HPDataType*)realloc(hp->_a, hp->_capacity * 2);
hp->_capacity *= 2;
}
hp->_a[hp->_size] = x;
moveUp(hp, hp->_size);
}
// 堆的删除
void HeapPop(Heap* hp)
{
HPDataType* move_heap = NULL;
move_heap = hp->_a[hp->_size];
hp->_a[hp->_size] = hp->_a[0];
hp->_a[0] = move_heap;
hp->_size--;
moveDown(hp, 0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
return hp->_a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
return hp->_size + 1;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{
return hp->_size <= -1 ? 1 : 0;
}
5.2链式二叉树
#include<stdio.h>
#include<assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
//==队列==
typedef BTNode QDataType;
typedef struct QueueNode
{
struct QueueNode* _nextnode;
BTNode* _data;
}QueueNode;
typedef struct Queue
{
QueueNode* _front;
QueueNode* _rear;
}Queue;
//========
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)//pi第一次进来是0,传地址是为了在递归中保持动态一致
{
BTNode* newnode = NULL;
if (a[*pi] == '#' || *pi >= n)
{
(*pi)++;
return NULL;
}
newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL)
{
perror("Creat:");
return NULL;
}
newnode->_data = a[(*pi)++];
newnode->_left = BinaryTreeCreate(a, n, pi);
newnode->_right = BinaryTreeCreate(a, n, pi);
return newnode;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory(&(*root)->_left);
BinaryTreeDestory(&(*root)->_right);
free(*root);
*root = NULL;
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->_left == NULL && root->_right == NULL)
{
return 1;
}
return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
BTNode *l_node = NULL, *r_node = NULL;
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
l_node = BinaryTreeFind(root->_left, x);
r_node = BinaryTreeFind(root->_right, x);
if (l_node != NULL && l_node->_data == x)
{
return l_node;
}
if (r_node != NULL && r_node->_data == x)
{
return r_node;
}
return NULL;
}
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("# ");
return;
}
printf("%c ", root->_data);
BinaryTreePrevOrder(root->_left);
BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("# ");
return;
}
BinaryTreeInOrder(root->_left);
printf("%c ", root->_data);
BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("# ");
return;
}
BinaryTreePostOrder(root->_left);
BinaryTreePostOrder(root->_right);
printf("%c ", root->_data);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
BTNode* put_root = NULL;
Queue examp_que, *put_examp_que = NULL;
QueueNode* newnode = NULL;
examp_que._front = (QueueNode*)malloc(sizeof(QueueNode));
if (examp_que._front == NULL)
{
return;
}
examp_que._rear = (QueueNode*)malloc(sizeof(QueueNode));
if (examp_que._rear == NULL)
{
return;
}
examp_que._front->_nextnode = examp_que._rear;
examp_que._rear->_nextnode = NULL;
examp_que._rear->_data = root;
while (examp_que._front->_nextnode != NULL)
{
put_root = examp_que._front->_nextnode->_data;
put_examp_que = examp_que._front;
examp_que._front = examp_que._front->_nextnode;
free(put_examp_que);
if (put_root)
{
printf("%c ", put_root->_data);
}
else
{
printf("# ");
}
if (put_root != NULL)
{
newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
return;
}
newnode->_nextnode = NULL;
newnode->_data = put_root->_left;
examp_que._rear->_nextnode = newnode;
examp_que._rear = newnode;
newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
return;
}
newnode->_nextnode = NULL;
newnode->_data = put_root->_right;
examp_que._rear->_nextnode = newnode;
examp_que._rear = newnode;
}
}
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)//0不是,1或是其他是
{
int f_empty = 0;//用于记录是否遇到NULL
BTNode* put_root = NULL;
Queue examp_que, * put_examp_que = NULL;
QueueNode* newnode = NULL;
examp_que._front = (QueueNode*)malloc(sizeof(QueueNode));
if (examp_que._front == NULL)
{
return -1;
}
examp_que._rear = (QueueNode*)malloc(sizeof(QueueNode));
if (examp_que._rear == NULL)
{
return -1;
}
examp_que._front->_nextnode = examp_que._rear;
examp_que._rear->_nextnode = NULL;
examp_que._rear->_data = root;
while (examp_que._front->_nextnode != NULL)
{
put_root = examp_que._front->_nextnode->_data;
put_examp_que = examp_que._front;
examp_que._front = examp_que._front->_nextnode;
free(put_examp_que);
if (put_root)
{
if (f_empty == 1)
{
return 0;
}
}
else
{
f_empty = 1;
}
if (put_root != NULL)
{
newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
return -1;
}
newnode->_nextnode = NULL;
newnode->_data = put_root->_left;
examp_que._rear->_nextnode = newnode;
examp_que._rear = newnode;
newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
return -1;
}
newnode->_nextnode = NULL;
newnode->_data = put_root->_right;
examp_que._rear->_nextnode = newnode;
examp_que._rear = newnode;
}
}
return 1;
}
6.结语
用于复习,如有错误欢迎指出。