二叉树和堆

1.二叉树的概念

一棵二叉树是结点的一个有限集合,该集合:
1. 或者为空
2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

1. 二叉树不存在度大于2的结点。
2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树注意:对于任意的二叉树都是由以下几种情况复合而成的: 

 1.1特殊的二叉树:

 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k-1,则为满二叉树

2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。要注意的是满二叉树是一种特殊的完全二叉树

2.二叉树的结构

2.1顺序存取

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。

2.2链式存取

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程学到高阶数据结构如红黑树等会用到三叉链。

 

typedefintBTDataType;// 二叉链
structBinaryTreeNode
{
structBinTreeNode*_pLeft;   // 指向当前节点左孩子
structBinTreeNode*_pRight; // 指向当前节点右孩子
BTDataType_data; // 当前节点值域
}

3.堆的结构

普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。

如果有一个关键码的集合:K={k1,k2,k3.....kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:K<=K2*i+1且K¡<= K2*i+2 (Ki >= K2*i+1 且K¡ >= K2*i+2)i=0,1,2...则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

堆的性质:

1.堆中某个节点的值总是不大于或不小于其父节点的值;

2.堆总是一棵完全二叉树。

4.堆的实现

.h文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<string.h>
#include<time.h>
typedef int HeDdatatype;
typedef struct Heap {
	HeDdatatype* a;
	int size;
	int capacity;
}HP;
//堆的结构用数组来实现,物理上是完全二叉树。
void HPInit(HP* php);
void HPDestroy(HP* php);
void HPPush(HP* php,HeDdatatype x);
void AdjustUp(HeDdatatype* a, int child);
HeDdatatype HpTop(HP* php);
void HPPop(HP* php);//不能使用覆盖删除:1.时间复杂度o(n) 2.破坏堆结构
bool HPEmpty(HP* php);
void HPInitArray(HP* php, HeDdatatype* a, int n);
void HeapSort(HeDdatatype* a, int size);
void AdjustUp(HeDdatatype* a, int child);
void AdjustDown(HeDdatatype* a, int size, int parent);
void AdjustDown(HeDdatatype * a, int size, int parent);

.c文件

#include"heap.h"
//初始化
void HPInit(HP* php) {
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;
}
//销毁
void HPDestroy(HP* php) {
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;

}
//交换
void Swap(HeDdatatype* px, HeDdatatype* py) {
	HeDdatatype tmp = *px;
	*px = *py;
	*py = tmp;
}
//向上调整
void AdjustUp(HeDdatatype* a, int child){

	int parent = (child - 1) / 2;//求父亲的下标
	while (child > 0) {
		if (a[child] > a[parent]) {//求小堆,将符号换为>即可求大堆
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			break;
		}
	}

}
//插入数据
void HPPush(HP* php,HeDdatatype x) {
	assert(php);
	if (php->size == php->capacity) {
		size_t newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HeDdatatype* tmp = realloc(php->a, sizeof(HeDdatatype) * newcapacity);
		if (tmp == NULL) {
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size++] = x;//将数值插入到数组最后一个位置中,并将个数加一
	AdjustUp(php->a, php->size - 1);
	
}

HeDdatatype HpTop(HP* php) {//拿到堆顶元素,也就是最大值或者最小值
	assert(php);

	return php->a[0];//根
}
//向下调整
void AdjustDown(HeDdatatype*a,int size,int parent) {
	int child = parent * 2 + 1;
	while (child < size) {
		//假设法,求出左右孩子中最小的孩子
		if (child + 1 < size && a[child+1 ] > a[child]) {
			++child;
		}
		if (a[child] < a[parent]) {//在进行堆排序的时候,需要注意对应的顺序,对应的堆(升大堆,降小堆),可大幅度提高效率。
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}

		else {
			break;
		}
	}
}
//删除堆顶元素
void HPPop(HP* php){
	assert(php&&php->size>0);
	Swap(&php->a[0], &php->a[php->size - 1]);//首先让其首尾调换
	php->size--;//删除尾元素
	AdjustDown(php->a, php->size, 0);

}
//判断是否为空
bool HPEmpty(HP* php) {
	assert(php);
	return php->size == 0;
}
//更高效的创建堆
void HPInitArray(HP* php, HeDdatatype* a, int n) {
	assert(php);
	php->a = (HeDdatatype*)malloc(sizeof(HeDdatatype) * n);
	if (php->a == NULL) {
		perror("malloc fail");
		return;
	}
	memcpy(php->a, a, sizeof(HeDdatatype) * n);
	php->capacity = php->size = n;
	//建堆,时间复杂度O(N*logN)
	/*for (int i = 0; i < n; i++) {
		AdjustUp(php->a, i);
	}*/
	for (int i = (php->size - 2) / 2; i >= 0; i--) {//用向下调整,对数组a进行调整为堆,我们从最后一个数据的
		//父亲进行调整,然后逐个调整。
		AdjustDown(php->a, php->size, i);//时间复杂度为O(N),更优
	}
}
//堆排序,升序,建大堆,降序,建小堆,这样的话只需要将首位对掉,然后以此调整即可。只是将相掉的数据后的
//尾数据不看即可。
void HeapSort(HeDdatatype* a, int size) {
	for (int i = (size - 2) / 2; i >= 0; i--) {
		AdjustDown(a, size, i);

	}
	int end = size - 1;
	while (end >0) {
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);//因为调转后我们只需要从根开始调整即可。
		--end;//通过调整end的大小,来控制
	}
}//时间复杂度为O(N*logN)
谢谢
  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

c23856

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值