目录
树的概念和结构
树的概念
树的表示
二叉树概念及结构
二叉树的概念
数据结构中的二叉树
特殊的二叉树
二叉树性质
二叉树的存储结构
顺序存储
链式存储
二叉树顺序结构及概念
二叉树的顺序结构
堆的概念及结构
堆的实现
heap.h
heap.c
heap-test.c
二叉树链式结构及实现
二叉树链式结构的遍历
二叉树的链式实现
binarytree
树是一种数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。
typedef int DataType;
struct Node
{
struct Node* firstChild1;
struct Node* pNextBrother;
DataType data;
};
二叉树(binary tree)是指树中节点的度不大于2(<=2)的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树 [2] 。
二叉树是递归定义的,其节点有左右子树之分,逻辑上二叉树有五种基本形态:
要注意的是满二叉树是一种特殊的完全二叉树。
高度为h的满二叉树有多少字节:2*h-1
前h-1层都是满的,最后一层要求从左到右是连续的
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
2. 若2i+1=n否则无左孩子
3. 若2i+2=n否则无右孩子
二叉树一般可以使用两种存储结构,一种顺序结构,一种链式结构。
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
二叉树的链式存储结构是指:
用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链.
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* _pLeft; // 指向当前节点左孩子
struct BinTreeNode* _pRight; // 指向当前节点右孩子
BTDataType _data; // 当前节点值域
}
// 三叉链
struct BinaryTreeNode
{
struct BinTreeNode* _pParent; // 指向当前节点的双亲
struct BinTreeNode* _pLeft; // 指向当前节点左孩子
struct BinTreeNode* _pRight; // 指向当前节点右孩子
BTDataType _data; // 当前节点值域
};
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储
这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树(二叉树具体概念参见——二叉树详解)的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<= K2i+2 (Ki >= K2i+1 且 Ki >= K2i+2) i = 0,1,2…,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
堆的性质:
#pragma once
#include
#include
#include
#include
//大堆
//父亲大于孩子
typedef int HPDaTaType;
typedef struct heap
{
HPDaTaType* a;
int size;
int capacity;
}HP;
//堆的初始化
void HeapInit(HP* php);
//堆的插入
void HeapPush(HP* php,HPDaTaType x);
//堆的删除
void HeapPop(HP* php);
//取堆顶元素
HPDaTaType Heaptop(HP* php);
//判空
bool HeapEmpty(HP* php);
//堆的数据个数
int HeapSize(HP* php);
//向上调整法
void ADjustUP(HPDaTaType* a, int child);
//向下调整法
void AdjustDown(HPDaTaType* a, int n, int parent);
//堆的销毁
void HeapDestory(HP* php);
//用数组进行堆排序
void heapsort(int* a, int n);
#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
//堆的初始化
void HeapInit(HP* php)
{
assert(php);
php->a = (HPDaTaType*)malloc(sizeof(HPDaTaType)*4);
if (php->a== NULL)
{
perror("malloc fail:");
return NULL;
}
php->size = 0;
php->capacity = 4;
}
//交换元素
void swap(HPDaTaType* p1, HPDaTaType* p2)
{
HPDaTaType x = *p1;
*p1 = *p2;
*p2 = x;
}
//向上调整法
void ADjustUP(HPDaTaType* a,int child)
{
int parent = (child - 1) / 2;
while (child>0)
{
if (a[parent]>a[child])
{
break;
}
else
{
swap(&a[child], &a[parent]);
child = parent;
parent = (child - 1) / 2;
}
}
}
//堆的插入
void HeapPush(HP* php,HPDaTaType x)
{
assert(php);
//插入数据
if (php->size == php->capacity)
{
HPDaTaType*tmp= (HPDaTaType*)realloc(php->a, sizeof(HPDaTaType) * php->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail:");
return NULL;
}
php->a = tmp;
php->capacity *= 2;
}
php->a[php->size] = x;
php->size++;
ADjustUP(php->a, php->size - 1);
}
//向下调整法
void AdjustDown(HPDaTaType* a,int n, int parent)
{
//先假设,如果假设错误,就交换
int child = parent *2+1;//默认左孩子大
while (child < n)
{
选出左右孩子中大的那一个
if (child+1 a[child])
//&&表达式,左表达式不满足,右表达式就不进行判断了
{
++child;
}
if (a[child] > a[parent])
{
swap(&a[child], &a[parent]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
//堆的删除
void HeapPop(HP* php)
{
assert(php);
assert(!HeapEmpty(&php));
//最后一个树与堆顶元素交换
swap(&php->a[0], &php->a[php->size - 1]);
//删除数据
php->size--;
//向下调整
AdjustDown(php->a, php->size,0);
}
//取堆顶元素
HPDaTaType Heaptop(HP* php)
{
assert(php);
return php->a[0];
}
//判空
bool HeapEmpty(HP* php)
{
assert(php);
return php->size == 0;
}
//堆的数据个数
int HeapSize(HP* php)
{
assert(php);
return php->size;
}
//堆的销毁
void HeapDestory(HP* php)
{
assert(php);
free(php->a);
php->a = NULL;
php->capacity = php->size = 0;
}
void heapsort(int* a, int n)
{
assert(a);
/*建堆,向下调整法时间复杂度是O(N)*/
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
AdjustDown(a, n, i);//n-1最后一个数据的下标,它是孩子,然后找到父亲需要(child-1)/2
//最后一个叶子结点的父亲就是倒数第一个非叶子结点的人
}
//排成升序
//N*logN
int end = n - 1;
while (end > 0)
{
swap(&a[end], &a[0]);
//然后向下调整找到第二大的数
AdjustDown(a, end, 0);
end--;
}
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
int main()
{
HP hp;
HeapInit(&hp);
HeapPush(&hp, 4);
HeapPush(&hp, 18);
HeapPush(&hp, 42);
HeapPush(&hp, 12);
HeapPush(&hp, 2);
HeapPush(&hp, 3);
HeapPush(&hp, 50);
while (!HeapEmpty(&hp))
{
printf("%d ",Heaptop(&hp));
HeapPop(&hp);
}
printf("\n");
return 0;
}
//int main()
//{
// int arr[10] = { 2, 1, 5, 7, 6, 8, 0, 9, 4, 3 };
// heapsort(arr,10);
//
// return 0;
//}
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。
前序/中序/后序的递归结构遍历:是根据访问结点操作发生位置命名
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
由于实现二叉树需要队列代码,我们将(队列)文件现有项复制到源文件中,
需要队列代码的请点击队列的代码
- 二叉树节点个数=左子树+右子树+1(根)
- 二叉树的深度=左右子树最大的其中一个+1(根)
- 二叉树第k层的节点个数=第k-1层的左子树个数+第k-1层的右子树个数
- 二叉树查找值为x的结点
- 层序遍历
- 判断二叉树是不是完全二叉树
- 二叉树的销毁
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include"queue.h"
//二叉树的结构
typedef struct BinaryTreeNode
{
int data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//创建结点
BTNode* BuyNode(int x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail:");
return NULL;
}
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
//构建二叉树
BTNode* CreatTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
return node1;
}
//前序遍历
void PreOrder(BTNode*root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
InOrder(root->right);
printf("%d ", root->data);
}
/*int size = 0;*///定义全局变量不会使每次调用的时候size归为0
//int TreeSize(BTNode* root)
//{
// //static int size=0
// //局部的静态size是无法改的,如果下次再进行一个树,就还是再这个基础上进行
// if (root == NULL)
// {
// return;
// }
// ++size;
// TreeSize(root->left);
// TreeSize(root->right);
//}
//结点个数
int TreeSize(BTNode* root)
{
return root == NULL ?0 :
TreeSize(root->left)
+ TreeSize(root->right)
+ 1;
}//计数=左子树个数+右子树个数+1
//树的高度
int TreeHeight(BTNode* root)
{
//当前树的高度=左右子树高度大的那个+1
if (root == NULL)
{
return 0;
}
int l = TreeHeight(root->left);
int r = TreeHeight(root->right);
return l < r ? r + 1 : l + 1;
}
//一个大树分治成每个小树,每个小树里面有左右子树,这里比较左子树和右子树的高度,然后高的带回
int TreeKLevel(BTNode* root, int k)//求第k层结点的个数
{
//根的第k层个数=左子树的第k-1层个数+右子树的第k-1层个数
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
int leftK = TreeKLevel(root->left, k - 1);
int rightK = TreeKLevel(root->right, k - 1);
return leftK+rightK;
}
//二叉树查找值为x的结点
BTNode* BinaryTreeFind(BTNode* root, int x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
BTNode* lret= BinaryTreeFind(root->left, x);
if (lret)
{
return lret;
}
BTNode* rret = BinaryTreeFind(root->right, x);
if(rret)
{
return rret;
}
return NULL;
}
//层序遍历
void LevelOrder(BTNode* root)
{
Queue q;
QInit(&q);
if (root)
{
Qpush(&q, root);
}
while (!Qempty(&q))
{
BTNode* front = Qfront(&q);//取对头的数据
Qpop(&q);
printf("%d ", front->data);
if (front->left)
{
Qpush(&q, front->left);
}
if (front->right)
{
Qpush(&q, front->right);
}
}
QDestory(&q);
}
//判断二叉树是不是完全二叉树
bool TreeComplete(BTNode* root)
{
Queue q;
QInit(&q);
if (root)
{
Qpush(&q, root);
}
while (!Qempty(&q))
{
BTNode* front = Qfront(&q);//取对头的数据
Qpop(&q);
if (front == NULL)
{
break;
}
else
{
Qpush(&q, front->left);
Qpush(&q, front->right);
}
}
//判断是不是完全二叉树
while (!Qempty(&q))
{
BTNode* front = Qfront(&q);//取对头的数据
Qpop(&q);
//后面有非空。说明非空结点不是完全连续
if (front)
{
QDestory(&q);
return false;
}
}
QDestory(&q);
return true;
}
//二叉树销毁
void TreeDestory(BTNode* root)
{
if (root == NULL)
{
return;
}
TreeDestory(root->left);
TreeDestory(root->right);
free(root);
//后序
}
int main()
{
BTNode* root = CreatTree();
PreOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
LevelOrder(root);
printf("\n");
int size=TreeSize(root);
printf("TreeSize:%d\n",size);
int height = TreeHeight(root);
printf("TreeHeight:%d\n", height);
int num=TreeKLevel(root,3);
printf("TreeKLevel:%d\n",size);
printf("BinaryTreeFind:%d\n", BinaryTreeFind(root, 4));
printf("BinaryTreeFind:%d\n", BinaryTreeFind(root, 50));
TreeComplete(root);
printf("TreeComplete:%d\n", TreeComplete(root));
TreeDestory(root);
return 0;
}
勇敢一点吧,我们不会再比今天更年轻了。