【数据结构】树与二叉树(中)

目录

前言:

一、顺序存储结构:

二、堆:

1.堆的性质:

2.堆的性质:

3.堆的实现:

Ⅰ.堆的初始化:

 Ⅱ.堆的插入(含向上调整):

 Ⅲ.堆的删除(含向下调整算法):

Ⅳ.取堆顶的数据:

Ⅴ.堆中的数据个数:

Ⅵ.堆的判空:

 Ⅶ.堆的销毁:

总结:


前言:

        上篇文章中,我们认识了树与二叉树相关概念以及两种常见的存储结构,而今天我们将对顺序存储结构进行实现的讲解。

一、顺序存储结构:

        普通的二叉树是是不适合用数组来存储的,因为可能会存在大量的空间浪费,但是完全二叉树不会存在此问题,所以更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来进行存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆完全没有关系。今天我们关于顺序存储结构的讲解,就以堆的形式进行。

二、堆:

1.堆的性质:

        堆分为小根堆和大根堆,根节点始终小于子节点称为小根堆,相反根节点大于子节点则称为大根堆。换句话说,根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

2.堆的性质:

①.堆中的某个节点的值总是小于或不小于其父节点的值。

②.堆总是一颗完全二叉树

3.堆的实现:

        Heap.h:存放函数声明、包含其他头文件、定义宏。

        Heap.c:书写函数定义,书写函数实现。

        test.c:书写程序整体执行逻辑。

Ⅰ.堆的初始化:

首先判断传入指针是否为空,然后为其开辟空间,并对相应数据进行赋值。

void HeapInit(HP* p)
{
	assert(p);
	HPDataType* new = (HPDataType*)malloc(sizeof(HPDataType)*4);
	if (new == NULL)
	{
		perror("malloc fail");
		return;
	}
	p->a = new;
	p->size = 0;
	p->capacity = 4;
}

 Ⅱ.堆的插入(含向上调整):

        因为堆的存储在物理层面上是数组,但是在逻辑层面上是二叉树。并且由于只有大根堆和小根堆,所以在插入数据之后要保准仍然是堆,就需要进行适当的调整。插入时从尾部插入,,而是否为堆取决于子节点和父节点的关系,所以插入的数据要与其父节点进行比较,若为小根堆则子节点要比父节点要大,否则就需要交换子节点和父节点,大根堆则相反。这种调整方式就叫做向上调整。

执行操作前进行非空判断,防止对空指针进行操作;

插入前判断空间是否足够,若不足则进行扩容;

插入后为了保证数组仍然为堆,所以需要在插入后进行向上调整。

//向上调整
void Adujustup(HPDataType* a, int child)
{
    assert(a);
	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 HeapPush(HP* p, HPDataType x)
{
	assert(p);

	if (p->size == p->capacity)
	{
		HPDataType* new = (HPDataType*)realloc(p->a,sizeof(HPDataType) * p->capacity*2);
		if (new == NULL)
		{
			perror("realloc fail");
			return;
		}
	}
	
		p->a[p->size] = x;
		p->size++;

	Adujustup(p->a, p->size - 1);

}

 Ⅲ.堆的删除(含向下调整算法):

        堆删除的实质时删除堆顶元素,但是如果我们直接删除堆顶元素,就会破坏堆的结构,所以这种方法不可取;于是我们这里采用将堆顶的数据于最后一个数据交换,再删除最后一个数据的方法,这样就实现了堆顶数据的删除。接着我们再调整以下堆顶数据的位置就可以了。

        而在这里我们就需要向下调整,所以我们选择的调整方法就是:将根节点与它的孩子中的较小的值交换,然后再将交换后的节点作为父节点继续与他的子节点交换,直到该节点小于他的子节点,或者成为叶节点。要注意的是,使用这个方法有一个前提:根节点的两个子树也得是堆才行。

执行操作前进行非空判断,防止对空指针进行操作;

删除过程同样与队列近乎一致,不同点是在删除过后为了保证删除堆顶数据后仍为堆,于是需要使用向下调整对删除后的对进行调整。

//向下调整
void Adujustdown(HPDataType* a, int size, int parent)
{
	int child = parent * 2 + 1;
	
	while (child < size-1)
	{
		if (a[child] < a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
		
	}


}
//堆删除
void HeapPop(HP* p)
{
	assert(p);
	if (p->size == 0)
	{
		return;
	}
	Swap(&p->a[0], &p->a[p->size - 1]);
	p->size--;
	Adujustdown(p->a, p->size, 0);

}

Ⅳ.取堆顶的数据:

HPDataType HeapTop(HP* p)
{
	assert(p);
	return p->a[0];
}

Ⅴ.堆中的数据个数:

int HeapSize(HP* p)
{
	assert(p);
	return p->size;
}

Ⅵ.堆的判空:

bool HeapEmpty(HP* p)
{
	assert(p);
	return p->size == 0;
}

 Ⅶ.堆的销毁:

void HeapDestroy(HP* p)
{
	assert(p);
	free(p->a);
	p->a = NULL;
	p->capacity = 0;
	p->size = 0;
}

总结:

        这篇文章我们完整的认识、了解、学习了二叉树顺序存储结构的相关知识,并且对二叉树顺序存储进行实现——堆的各接口实现。至此,关于二叉树的顺序存储的知识我们就全部学习完毕了。 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您设计一个简单的菜单数据结构,包含二叉树的实现。 首先,我们可以定义一个节点类,用于表示菜单项。每个节点包含一个名称和一个指向其子节点的指针。对于二叉树,还需要包含指向其左右子节点的指针。 ```python class MenuItem: def __init__(self, name): self.name = name self.children = [] class BinaryMenuItem: def __init__(self, name): self.name = name self.left = None self.right = None ``` 接下来,我们可以定义一个菜单类,用于管理整个菜单。对于二叉树,菜单类都包含一个指向根节点的指针。 ```python class Menu: def __init__(self): self.root = None class BinaryTreeMenu: def __init__(self): self.root = None ``` 对于,我们可以实现添加子节点的方法。我们可以通过遍历来找到要添加子节点的节点,然后将新节点添加到其子节点列表。 ```python class Menu: ... def add_child(self, parent, child): if not self.root: self.root = child else: node = self._find_node(self.root, parent) if node: node.children.append(child) def _find_node(self, node, target): if node.name == target: return node for child in node.children: found = self._find_node(child, target) if found: return found return None ``` 对于二叉树,我们可以实现插入节点的方法。我们可以从根节点开始遍历,找到一个没有左子节点或右子节点的节点,然后将新节点插入到该位置。 ```python class BinaryTreeMenu: ... def insert_node(self, name): new_node = BinaryMenuItem(name) if not self.root: self.root = new_node else: self._insert_node(self.root, new_node) def _insert_node(self, node, new_node): if not node.left: node.left = new_node elif not node.right: node.right = new_node else: self._insert_node(node.left, new_node) ``` 这样,我们就设计出了一个简单的菜单数据结构,包含了二叉树的实现。您可以根据具体需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值