【数据结构】二叉树的结构与堆

本文详细介绍了二叉树的顺序结构,特别是堆(小堆和大堆)的概念、性质、操作,如堆的构造、调整算法以及相关题目解析。还涵盖了堆的实现,包括初始化、插入、删除和堆顶数据的获取等关键步骤。
摘要由CSDN通过智能技术生成

一、二叉树的顺序结构

        普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把(一种二叉树)使用顺序结构的数组来存储。

        需要注意的是这里的操作系统 虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段

二、堆

1.堆的概念

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

2.堆的性质

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

3.堆相关题目 

1、下列关键字序列为堆的是( )
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 之后需重建堆,在此过程中,关键字之间的比较次数是( )
A. 1
B. 2
C. 3
D. 4  

 

3、一组记录排序码为 (5 11 7 2 3 17),则利用堆排序方法建立的初始堆为( )
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)

 所以选择C项  

 所以选择C项   

4、最小堆 [0, 3, 2, 5, 7, 4, 6, 8],在删除堆顶元素0之后,其结果是( )
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]

 

4.堆的实现 

向下调整算法
向下调整算法有一个前提:左右子树必须是一个堆 (包括大堆和小堆),才能调整。

现在我们给出一个数组,逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。

int array[] = {27,15,19,18,28,34,65,49,25,37};

 从根开始与左右孩子比较,如果孩子比父亲小,则两两交换位置,再继续往下调,直到左右孩子都比父亲大或者调到叶子

1.堆的初始化

堆的初始化与队列相同,首先判断传入指针非空后,将其置空,并将数据置零即可。 

2.堆的插入

这里使用的是向上调整算法

因为堆的存储在物理层面上数组,但是在逻辑层面上二叉树。并且由于只有小堆和大堆,所以在插入数据之后要想保证其仍然是堆,就需要进行适当的调整。

插入时从尾部插入,而是否为堆取决于子节点和父节点的关系,若为小堆则子节点要比父节点要大,否则就需要交换子节点和父节点,大堆则相反。

void AdjustUp(HPDataType* 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, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int tmp = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* arr = (HPDataType*)realloc(php->a, tmp * sizeof(HPDataType));
		if (arr == NULL)
		{
			perror("HPPush");
			return;
		}
		
		php->a = arr;
		php->capacity = tmp;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a,php->size-1);
}
3.堆的删除

这里使用的是向下调整算法

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

在这里选择的调整方法是:将根节点与它的孩子中的较小值交换,然后再将交换后的节点作为父节点继续与它的子节点交换,直到该节点小于它的子节点,或者成为叶节点。

使用这个方法有一个前提:根节点的两个子树也得是堆才行。

void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && 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);
	assert(!HPEmpty(php->a));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size - 1, 0);
}
4.取堆顶数据
HPDataType HPTop(HP* php)
{
	assert(php);
	return php->a[0];
}
5.查看堆中数据个数
int Heapsize(HP* php)
{
	assert(php);
	return php->size;
}
6.堆的判空
bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}
7.堆的销毁
void HPDestroy(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}
Heap.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

void Swap(HPDataType* p1, HPDataType* p2);
void AdjustDown(HPDataType* a, int n, int parent);
void AdjustUp(HPDataType* a, int child);
void HPInsert(HP* php);
void HPPush(HP* php, HPDataType x);
void HPPop(HP* php);
bool HPEmpty(HP* php);
HPDataType HPTop(HP* php);
void HPDestroy(HP* php);
int Heapsize(HP* php);
Heap.c
#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"

void HPInsert(HP* php)
{
	php->a = NULL;
	php->capacity = 0;
	php->size = 0;
}

bool HPEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

void Swap(HPDataType* p1,HPDataType* p2)
{
	HPDataType tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

void AdjustUp(HPDataType* 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 AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && 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 HPPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		int tmp = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* arr = (HPDataType*)realloc(php->a, tmp * sizeof(HPDataType));
		if (arr == NULL)
		{
			perror("HPPush");
			return;
		}
		
		php->a = arr;
		php->capacity = tmp;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a,php->size-1);
}

void HPPop(HP* php)
{
	assert(php);
	assert(!HPEmpty(php->a));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size - 1, 0);
}

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

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

int Heapsize(HP* php)
{
	assert(php);
	return php->size;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"Heap.h"


void test1()
{
	HP hp;
	HPInsert(&hp);
	int arr[] = { 10,20,5,15,9,11 };
	int i = 0;

	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		HPPush(&hp, arr[i]);
	}

	while (!HPEmpty(&hp))
	{
		int top = HPTop(&hp);
		printf("%d ", top);
		HPPop(&hp);
	}

	HPDestroy(&hp);
}

int main()
{
	test1();

	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值