0
点赞
收藏
分享

微信扫一扫

树的储存形势&代码实现(跑路人笔记)

文章目录

🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀🏀

前言

此篇文章是使用完全二叉树实现小堆然后用小堆进行堆排序,堆排序的时间复杂度为logN空间复杂度为O(N);后续还会优化吧空间复杂度改一下
请添加图片描述

正文

先讲一下二叉树的储存结构吧

二叉树的储存结构

顺序结构

链式结构

typedef int BTDataType;
// 二叉链
struct BinaryTreeNode
{
struct BinTreeNode* _pLeft; // 指向当前节点左孩子
 struct BinTreeNode* _pRight; // 指向当前节点右孩子
 BTDataType _data; // 当前节点值域
};
// 三叉链
struct BinaryTreeNode
{
 struct BinTreeNode* _pParent; // 指向当前节点的双亲
 struct BinTreeNode* _pLeft; // 指向当前节点左孩子
 struct BinTreeNode* _pRight; // 指向当前节点右孩子
 BTDataType _data; // 当前节点值域
};

堆的概念

我先讲一下自己的理解:

二叉树的顺序结构的实现&堆的实现

堆的实现其实就是用二叉树的顺序结构实现的所以我干脆放在一起讲🤪.

我的思路还是先看整体代码再讲解代码实现难点.

来看一眼我们的代码们吧
Heap.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HPDataType;
//通过顺序表实现树的堆  这个为小堆
typedef struct Heap
{
	HPDataType* data;
	size_t size;
	size_t capacity;
}Heap;
//创建堆
void HeapCreat(Heap* obj);
//摧毁堆
void HeapDestory(Heap* obj);
//插入数据
void HeapPush(Heap*obj,HPDataType x);
//数据打印
void HeapPrint(Heap* obj);
//删除数据
void HeapPop(Heap* obj);
//堆顶数据
HPDataType HeapTop(Heap* obj);
//堆的个数
size_t HeapSize(Heap* obj);
//堆的判空
bool HeapEmpty(Heap* obj);

Heap.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"

void HeapCreat(Heap* obj)
{
	assert(obj);
	obj->data = (HPDataType*)malloc(sizeof(HPDataType)*4);
	assert(obj->data);
	obj->size = 0;
	obj->capacity = 4;
}
void HeapDestory(Heap* obj)
{
	assert(obj);
	free(obj->data);
	obj->size = obj->capacity = 0;
}
void AdJustUp(Heap* obj)
{
	size_t child = obj->size-1;
	size_t parent = (child - 1) / 2;
	while (child)
	{
		if (obj->data[parent] > obj -> data[child])
		{
			HPDataType tmp = obj->data[child];
			obj->data[child] = obj->data[parent];
			obj->data[parent] = tmp;
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}


}
void HeapPush(Heap* obj, HPDataType x)
{
	assert(obj);
	if (obj->size == obj->capacity)
	{
		obj->capacity *= 2;
		HPDataType* tmp = (HPDataType*)realloc(obj->data, obj->capacity*sizeof(HPDataType));
		assert(tmp);
		obj->data = tmp;
	}
	obj->data[obj->size] = x;
	++obj->size;
	//进行向下调整以保证堆的格式
	AdJustUp(obj);
}

void HeapPrint(Heap* obj)
{
	for (size_t i = 0; i < obj->size; i++)
	{
		printf("%d ", obj->data[i]);
	}
}
void AdJustDown(Heap* obj)
{
	size_t father = 0;
	size_t child = father*2+1;
	while (child < obj->size)
	{
		//选出最小的孩子
		if (child+1<obj->size && obj->data[child] > obj->data[child + 1])
		{
			child++;
		}

		if (obj->data[child] < obj->data[father])
		{
			HPDataType tmp = obj->data[child];
			obj->data[child] = obj->data[father];
			obj->data[father] = tmp;
			father = child;
			child = father * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapPop(Heap* obj)
{
	assert(obj);
	assert(obj->size > 0);
	obj->data[0] = obj->data[obj->size - 1];
	obj->size--;
	AdJustDown(obj);
}
HPDataType HeapTop(Heap* obj)
{
	assert(obj);
	assert(obj->size);
	return obj->data[0];
}
size_t HeapSize(Heap* obj)
{
	assert(obj);
	return obj->size;
}
bool HeapEmpty(Heap* obj)
{
	assert(obj);
	return obj->size == 0;
}

main.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Heap.h"
//堆的测试
void HeapTest()
{
	Heap hp = {0};
	HeapCreat(&hp);
	HeapPush(&hp, 10);
	HeapPush(&hp, 18);
	HeapPush(&hp, 19);
	HeapPush(&hp, 25);
	HeapPush(&hp, 28);
	HeapPush(&hp, 43);
	HeapPush(&hp, 65);
	HeapPush(&hp, 49);
	HeapPush(&hp, 27);
	HeapPush(&hp, 37);
	HeapPop(&hp);
	HeapPrint(&hp);
	HeapDestory(&hp);

}
//堆排序
void HeapSort(int* a, int size)
{
	Heap hp;
	HeapCreat(&hp);
	for (int i = 0; i < size; ++i)
	{
		HeapPush(&hp, a[i]);
	}

	size_t j = 0;
	while (!HeapEmpty(&hp))
	{
		a[j] = HeapTop(&hp);
		j++;
		HeapPop(&hp);
	}

	HeapDestory(&hp);
}

int main()
{
	//TestHeap();
	int a[] = { 4, 2, 7, 8, 5, 1, 0, 6 };
	HeapSort(a, sizeof(a) / sizeof(int));

	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");

	return 0;
}

咱主要讲Heap.c所需要讲解的部分

主要讲解部分

下方是结构题的内容

typedef struct Heap
{ 
	HPDataType* data;//用于储存数据
	size_t size; //大小
	size_t capacity;//容量
}Heap;

实话实说需要重点讲解的主要是两个函数.

其中

void AdJustUp(Heap* obj)
{
	size_t child = obj->size-1;
	size_t parent = (child - 1) / 2;
	while (child)
	{
		if (obj->data[parent] > obj -> data[child])
		{
			HPDataType tmp = obj->data[child];
			obj->data[child] = obj->data[parent];
			obj->data[parent] = tmp;
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

这个函数是嵌套在HeapPush中的一个用于调整数据位置的函数,我们在HeapPush的时候是将数据直接放在我们顺序表的末尾处,然后通过AdJustUp函数对数据进行调整.将本来插入后可能不是堆情况调整成堆的格式.

我们通过图来了解这种情况: 比如我们的数值原来有着: 2 3 5 6 7 8

image-20220407133011488

这时我们插入一个1来看看我们的AdJustUp函数是如何将顺序表的储存情况重新换成堆形势.

image-20220407134809683

其实很简单我们通过while循环和if语句来判断末尾部的父亲是否大于我们要改变位置的节点如果大于就交换他们的位置如果小于就直接进行break语句跳出循环即可.

image-20220407134855068

然后使我们的向下调整函数.

void AdJustDown(Heap* obj)
{
	size_t father = 0;
	size_t child = father*2+1;//得到的是左孩子
	while (child < obj->size)
	{
		//选出最小的孩子
		if (child+1<obj->size&&obj->data[child] > obj->data[child + 1])
		{
			child++;
		}
		if (obj->data[child] < obj->data[father])
		{
			HPDataType tmp = obj->data[child];
			obj->data[child] = obj->data[father];
			obj->data[father] = tmp;
			father = child;
			child = father * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapPop(Heap* obj)
{
	assert(obj);
	assert(obj->size > 0);
	obj->data[0] = obj->data[obj->size - 1];
	obj->size--;
	AdJustDown(obj);
}

我们的向下调整函数也是在HeapPop函数内作为内嵌(至于为啥要把向下调整专门分装成一个函数,我个人认为是为了代码的整洁性吧).

HeapPop 的功能是删除顺序表的定元素也就是下标为0的元素,但是我们如果先将下标为0的元素换成我们在尾的元素然后进行对size进行--即可.

然后我们即可进行向下调整的操作.

老样子我们使用图文调整的形式给大家展示.

image-20220407175455600

我们先比较左右还在的大小选一个最小的与父亲节点交换在只有一个孩子的情况至于左孩子比较即可👍.

结尾

这部分的代码实现其实也挺简单的,我们想用堆排序也只需要从得到头元素然后删头只要堆为空即可👍.

举报

相关推荐

0 条评论