【数据结构】---“二叉树”--“堆”

本文深入探讨了二叉树与堆的数据结构。详细介绍了树的基本概念、二叉树的特性,包括满二叉树和完全二叉树,以及二叉树的性质。接着讲解了堆的定义,包括大根堆和小根堆,以及堆的插入和删除操作,重点阐述了向上调整法和向下调整法的时间复杂度。最后提到了堆排序和解决TopK问题的方法,并概述了二叉树的链式结构及遍历策略。
摘要由CSDN通过智能技术生成

二叉树

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。

在这里插入图片描述

需要注意的是:子树之间不能有交集,否则就不是树形结构

树的相关概念

  1. 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为3
  2. 叶节点或终端节点:度为0的节点称为叶节点; 如上图:E、F、G、H、I 节点为叶节点
  3. 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
  4. 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点
  5. 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点
  6. 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为3
  7. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推
  8. 树的高度或深度:树中节点的最大层次; 如上图:树的高度为3
  9. 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先
  10. 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙
  11. 森林:由m(m>0)棵互不相交的树的集合称为森林

树的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,既然保存值域,也要保存结点和结点之间
的关系
我们这里就简单的了解其中最常用的孩子兄弟表示法

typedef int DataType;
struct Node
{
   
struct Node* _firstChild1; // 第一个孩子结点
struct Node* _pNextBrother; // 指向其下一个兄弟结点
DataType _data; // 结点中的数据域
};

二叉树

概念

二叉树是一种不存在度大于2的节点,并且二叉树的子树是由左右之分的,次序不能颠倒,所以二叉树也是一种有序树

在这里插入图片描述

特殊的二叉树

  1. 满二叉树:如果一个二叉树的层数为K,且结点总数是2^k-1,则它就是满二叉树

在这里插入图片描述

  1. 完全二叉树:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树

在这里插入图片描述

二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是**2^(h-1).
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为** n0 ,度为2的分支结点个数为n2,则有 n0=n2+1
  4. . 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h= log(n+1)
  5. 对于具有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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值