树、二叉树、堆

目录

一、树的表示

二、二叉树的概念及结构

1、概念:

2、特殊的二叉树

3、二叉树的存储结构

 三、堆

1、大\小根堆

2、堆的实现

1)结构及初始化

 2)插入

3)删除

4)取堆顶数据

5)判空

6)大小

7)销毁

四、堆排序


一、树的表示

常用:孩子兄弟表示法

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

二、二叉树的概念及结构

1、概念:

一颗二叉树是结点的一个有限集合,该集合:

1)为空

2)由一个根节点加上左子树和右子树的二叉树组成。

注意:二叉树子树有左右之分,次序不能颠倒,因此二叉树是有序树。

2、特殊的二叉树

1)满二叉树:每一层的结点数都达到最大值。如果层数为k,则该满二叉树的结点总数为2^k-1

2)完全二叉树:最后一层可以不满,但必须从左到右连续。

        如果满二叉树的高度为h,则它的结点数为2^h-1

        反之,如果满二叉树的结点有N个,则它的高度h为logN+1,可以看成logN。 

        高度为h的完全二叉树的结点数量范围是【2^(h-1),2^h-1】

对于任意一棵二叉树,度为0的永远比度为2的多一个。(每增加一个度为2的,就会增加一个度为0的)

3、二叉树的存储结构

父子下标关系:

parent=(child-1)/2

child左=parent*2+1

child右=parent*2+2

但是,当二叉树不是满二叉树或完全二叉树时,会浪费空间,不适合用数组存储表示二叉树。

 三、堆

1、大\小根堆

小根堆:堆中某个结点总是不小于其父结点。

大根堆:堆中某个结点总是不大于其父结点。

堆一定是完全二叉树。

2、堆的实现

1)结构及初始化

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

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

 2)插入

插入数据的关键是在数组中插入数据后调整数据位置,这里我们实现的是大堆,所以当孩子大于父亲的时候,就应该交换位置。

void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a, sizeof(HPDataType) * php->capacity * 2);
		if (tmp == NULL)
		{
			perror("realloc failed");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size] = x;//size就是最后一个数据的下一个数据的下标
	php->size++;
	AdjustUp(php->a,php->size-1);
}
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 Swap(HPDataType* p1, HPDataType* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

3)删除

要删除堆顶的数据,可以让顶数据与最后一个数据先交换位置,再向下调整顶数据的位置

如果它小于它的较大孩子,就让他们换位置

void HeapPop(HP* php)//删堆顶
{
    assert(php);
    assert(!HeapEmpty(php));
    Swap(&php->a[php->size-1], &php->a[0]);
    php->size--;
    AdjustDown(php->a,php->size,0);
}
void AdjustDown(HPDataType*a,int n,int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//选出左右孩子中大的那一个
		//a[child + 1] > a[child] && child + 1 < n 不能这样写!
		//a[child + 1]已经越界访问了
		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;
		}

	}
}

4)取堆顶数据

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

5)判空

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

6)大小

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

7)销毁

void HeapDestroy(HP* php)
{
	assert(php);
	free(php->a);
	free(php);
}

四、堆排序

要想利用堆来给一组数排序

第一步:建堆(利用堆插入数据的过程,一个个插入)

第二步:排升序,建大堆;排降序,建小堆。

以排升序为例:

end一开始指向3, 当3和20互换且3的位置调整好后,20的位置就定下来了,

如何再让end=end-1,此时end指向5,再让5与第一个数交换调整……

void HeapSort(int* a,int n)
{
	//向上调整建堆
	for (int i = 0; i < n; i++)
	{
		AdjustUp(a, i);
	}

	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[end], &a[0]);
		AdjustDown(a, end, 0);
		end--;
	}
}

扩展:建堆也可以用向下调整建堆

要从倒数第一个非叶子结点开始调,6->7->5->1->……

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值