数据结构之堆及实现其基本操作

1.基本概念及性质

  • 概念:如果有一个关键码的集合K = (k0, k1, k2, …, kn - 1),把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i + 1且Ki <= K2i + 2(i = 0, 1, 2, …),则称为小堆,根节点最小的堆叫做最小堆或者小根堆。若所有元素满足:Ki >= K2i + 1且Ki >= K2i + 2(i = 0, 1, 2, …),则称为大堆,根节点最大的堆叫做最大堆或者大根堆。
  • 性质:① 堆中某个节点的值总是不大于或者不小于其父节点的值。 ② 堆总是一棵完全二叉树。
  • 大小根堆的示意图:
    在这里插入图片描述

2.堆的实现

(1.)堆的结构

// 堆的结构

#define int HPDataType;
typedef struct Heap
{
	HPDataType* _a;
	int _size;
	int _capacity;
}Heap;

void HeapInit(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);// 获取堆的大小

bool HeapEmpty(Heap* hp);// 判断堆是否为空

void HeapSort(HPDataType* a, int n);// 堆排序

(2.)堆向下调整算法

  • 通过从根节点开始的向下调整算法可以把一棵完全二叉树调整成一个小堆或者大堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

  • int arr[] = {27, 15, 19, 18, 28, 34, 65, 49, 25, 37};以建一个小堆为例:
    在这里插入图片描述

  • 实现代码

// 堆的向下调整算法实现代码

void Swap(int* p, int* q)
{
	int* tmp = p;
	p = q;
	q = tmp;
}
void AdjustDown(HPDataType* a, size_t n, size_t parent)
{
	size_t child = parent * 2 + 1;
	while(child < n)
	{
	    // 获取比较值比较小的孩子
		if(child + 1 < n && a[child + 1] < a[child])
			++child;
		// 如果父节点值大于孩子节点中值较小者,
		// 将父节点与孩子节点中值较小者交换
		if(a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

(3.)堆的构建

  • 构建堆是从倒数第一个非叶子节点的子树开始调整,一直调整到根节点的树,就就可以调整成堆。

  • int arr[] = {9, 8, 7, 2, 6, 4};以构建小根堆为例:
    在这里插入图片描述

  • 实现代码

  • 最后一个叶子节点,即最后一个节点的父节点。其下标为:(n - 1 - 1) / 2。

// 构建堆的实现代码

void HeapInit(Heap* hp, HPDataType* a, int n)
{
	hp->_a = malloc(sizeof(HPDataType) * n);
	memcpy(hp->_a, a, sizeof(HPDataType) * n);
	hp->_size = n;
	hp->_capacity = n;
	
	// 构建成堆
	for(size_t i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdjustDown(hp->_a, hp->_size, i);
	}
}

(5.)堆的向上调整算法以及堆的插入

  • 先将要插入的值插入到数组的尾上,再进行向上调整算法,直到满足堆。
  • 以上面为例,插入数据0,且依旧是一个小根堆。示意图如下:
    在这里插入图片描述
  • 堆的向上调整算法实现代码
// 堆的向上调整算法实现代码

void Swap(int* p, int* q)
{
	int* tmp = p;
	p = q;
	q = tmp;
}
void AdjustUp(HPDataType* a, size_t child)
{
	size_t 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(Heap* hp, HPDataType)
{
	if(hp->_size == hp->_capacity)
	{
		size_t newcapacity = hp->_capacity == 0 ? 2 : hp->_capacity * 2;
		hp->_a = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);
		hp->_capacity = newcapacity;		
	}
	hp->_a[hp->_size] = x;
	++hp->_size;
	AdjustUp(hp->_a, hp->_size - 1);
}

(6.)堆的删除

  • 删除堆是删除堆顶的数据,将堆顶的数据和最后 一个数据一交换,然后删除最后一个数据,最后再进行向下调整算法。

  • 比如,上面的如果要删除0,示意图如下:
    在这里插入图片描述

  • 实现代码

// 堆的删除的实现代码

void Swap(int* p, int* q)
{
	int* tmp = p;
	p = q;
	q = tmp;
}
void HeapPop(Heap* hp)
{
	assert(hp->_size > 0);
	Swap(&hp->_a[hp->_size - 1], &hp->_a[0]);
	--hp->_size;
	AdjustDown(hp->_a, hp->_size, 0);
}

(7.)堆排序

  • 升序就是建大堆,要是降序就是建小堆。
  • 详情见博客数据结构之八种排序方式。

(8.)实现堆的其它接口的代码。

void HeapDestory(Heap* hp)
{
	// 堆的销毁
	if(hp->_a)
	{
		free(hp->_a);
		hp->_a = NULL;
	}
	hp->_size = hp->_capacity = 0;
}

HPDataType HeapTop(Heap* hp)
{
	// 获取堆的根节点
	assert(hp->_size > 0);
	return hp->_a[0];
}
int HeapSize(Heap* hp)
{
	// 获取堆的大小
	return hp->_size;
}

bool HeapEmpty(Heap* hp)
{
	// 判断堆是否为空
	return hp->_size == 0;
}
### 使用深度优先搜索(DFS)算法生成全排列 为了理解如何使用深度优先搜索来生成全排列,可以先了解该方法的核心原理。深度优先搜索是一种用于遍历或搜索树或图的算法,沿着树的深度遍历节点,尽可能深地搜索分支[^3]。 对于全排列问题而言,可以通过构建一棵决策树来进行求解,在每一步选择一个未使用的元素加入当前序列,并继续递归处理剩余部分直至完成全部选择。当达到叶子节点时,则获得了一个完整的排列方案;之后应回溯至上一状态并尝试其他可能性,确保每个位置上的候选都能得到充分探索[^4]。 下面是一个具体的Python实现例子: ```python def dfs_permutations(nums, path, result): if not nums: result.append(path) return for i in range(len(nums)): # 选择当前数作为下一个字符 current_num = nums[i] # 剩余可用的选择 remaining_nums = nums[:i] + nums[i+1:] # 继续向下一层递归调用 dfs_permutations(remaining_nums, path + [current_num], result) def get_all_permutations(nums): result = [] # 开始执行dfs函数 dfs_permutations(nums, [], result) return result if __name__ == "__main__": test_case = [1, 2, 3] all_perms = get_all_permutations(test_case) print(all_perms) ``` 此代码片段展示了通过DFS方式获取给定列表`nums`内所有不同顺序组合的方法。每次迭代过程中都会选取一个新的数字添加至现有路径中形成新的子集,同时移除已选中的数值以便后续操作能够基于剩下的选项展开进一步计算。最终结果保存在一个名为`result`的结果集中返回给用户查看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值