【数据结构】二叉树的顺序结构实现以及堆的实现应用

1 二叉树的顺序结构

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

2 堆的概念及结构

总结:

1.首先要是完全二叉树

2.完全二叉树中的结点必须满足:

         a 如果任意节点都比其孩子节点小,将其称作小堆(小根堆)

         b 如果任意节点都比其孩字节点大,将其称作大堆(大根堆)

(根节点要不是最小的,要不是最大的)

  堆的性质:

堆中某个节点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

练习

1. 下列关键字序列为堆的是:()A
A 100 , 60 , 70 , 50 , 32 , 65
B 60 , 70 , 65 , 50 , 32 , 100
C 65 , 100 , 70 , 32 , 50 , 60
D 70 , 65 , 100 , 32 , 50 , 60
E 32 , 50 , 100 , 70 , 65 , 60
F 50 , 100 , 70 , 65 , 60 , 32
2. 已知小根堆为 8 , 15 , 10 , 21 , 34 , 16 , 12 ,删除关键字 8 之后需重建堆,在此过程中,关键字之间的比较次数是(3)。  15比10,  12比10, 12比16
A 1
B 2
C 3
D 4
3. 一组记录排序码为 ( 5 11 7 2 3 17 ), 则利用堆排序方法建立的初始堆为 C
A ( 11 5 7 2 3 17 )
B ( 11 5 7 2 17 3 )
C ( 17 11 7 2 3 5 )
D ( 17 11 7 5 3 2 )
E ( 17 7 11 3 5 2 )
F ( 17 7 11 3 2 5 )
4. 最小堆 [ 0 , 3 , 2 , 5 , 7 , 4 , 6 , 8 ], 在删除堆顶元素 0 之后,其结果是(C) 自己画图
A [ 3 2 5 7 4 6 8 ]
B [ 2 3 5 7 4 6 8 ]
C [ 2 3 4 5 7 8 6 ]
D [ 2 3 4 5 6 7 8 ]

3 堆的实现

       堆的基本操作有许多,建堆、删除、插入、获取堆顶元素、元素个数、判空、销毁等,具体看vs上的代码

3.1 堆向下调整算法

       现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整

初始 27为parent    15为child

向下调整法

void AdjustDown(int array[], int size, int parent)
{
int child  = parent * 2  + 1; //优先让child标记左孩子
//举要检测parent是否满足堆的特性
//与其孩子进行比较,如果不满足则交换;一定要对两个孩子中较小的孩子进行交换
while(child < size)
{
    if(child + 1 < size && array[child + 1] < array[child])
           child = child + 1;                                 
    //检测右孩子有无,假设如果右孩子更下
    //将右孩子定为child
    //再去检测双亲和较小孩子的关系,不满足情况则交换

    if(array[child] < array[parent]) //孩子比双亲小
    {
        Swap(&array[child], &array[parent]);
        //大的元素往下移动会导致子树不满足情况
        //往下定义
        parent = child;
        child = parent * 2 + 1;
    }
}
//依次将子树这边全部用上述方法进行调整成为堆

}

3.2堆的创建

拿到一个数组,逻辑上是一颗完全二叉数,但不是堆,需要把它构建一个堆

 

 

 3.3 建堆时间复杂度

        因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明 ( 时间复杂度本来看的就是近似值,多几个节点不影响最终结果)

 所以时间复杂度为  o(n)

3.4 堆的插入

先插入一个 10 到数组的尾上,再进行向上调整算法,直到满足堆

// 堆的插入
void HeapPush(Heap* hp, DataType x)
{
	CheckHeapCapacity(hp);

	hp->array[hp->size] = x;
	hp->size++;

	AdjustUp(hp, hp->size - 1);
}

3.5 堆的删除

堆的删除,删除的是堆顶的元素

// 堆的删除
void HeapPop(Heap* hp)
{
	if (HeapEmpty(hp))
	{
		return;
	}

	// 堆中有元素
	// 1. 将堆顶元素与堆中最后一个元素进行交换
	Swap(&hp->array[0], &hp->array[hp->size - 1]);

	// 2. 将堆中有效元素个数减少1个
	hp->size--;

	// 3. 将堆顶元素往下调整
	AdjustDown(hp, 0);
}

3.6堆的优化

借助函数指针方式让用户自己可以选择创建大堆还是小堆

4 堆的应用

4.1堆排序

堆排序即利用堆的思想来进行排序,总共分为两个步骤:
1. 建堆
要升序:建大堆
要降序:建小堆
2. 利用堆删除思想来进行排序
建堆和堆删除中都用到了向下调整,因此掌握了向下调整,就可以完成堆排序
删除的那个元素就是最大的元素,放在数组中,依次放入,直到堆中剩余最后2个元素
下图中蓝色就代表已经删除了,也就是排序成功了

调整堆的代码
void AdjustHeap(int array[], int size, int parent)
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		//右孩子存在,找两个中较大的孩子
		if (child + 1 < size && array[child + 1] > array[child])
		{
			child = child + 1;
		}
		if (array[child] > array[parent])
		{
			Swap(&array[parent], &array[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			return;
		}
	}
}
void HeapSort(int array[], int size)
{
	//1.建堆:升序--》建大堆
	//找到倒数第一个非叶子节点,siez-1 是最后一个节点,
	//注意:是非叶子节点,是最后一个节点的双亲
	int lastNotLeaf = (size - 2) / 2;
	for (int root = lastNotLeaf; root >= 0; --root)
	{
		AdjustHeap(array, size, root);
	}
	//2.利用堆删除的思想进行排序
	int end = size - 1;
	while(end)
	{
		Swap(&array[0], &array[end]);
		AdjustHeap(array, end, 0);
		end--;
	}
}

4.2 TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大
对于 Top-K 问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了 ( 可能 数据都不能一下子全部加载到内存中)
用堆来解决,基本思路如下:
1. 用数据集合中前 K 个元素来建堆
前k个最大的元素,则建小堆
前k个最小的元素,则建大堆
2. 用剩余的 N-K 个元素依次与堆顶元素来比较,不满足则替换堆顶元素
将剩余 N-K 个元素依次与堆顶元素比完之后,堆中剩余的 K 个元素就是所求的前 K 个最小或者最大的元素
时间复杂度“
1.用前K个元素建堆 --》建堆的时间复杂度 o(k)
2.用剩余的n-k 元素 和堆顶元素比较   o  ((n-k)logk)
总时间复杂度: o(k + (n-k)logk)    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值