树的初步了解及堆,堆的topk问题,堆排序

目录

前言 

一:树

1.树的概念

2.树的基础概念知识

3.在树中孩子节点和父节点知一求一 

4.树的表示方法

二:二叉树

1.二叉树的概念

2.二叉树的特性

3.满二叉树 

4.完全二叉树 

三:堆 

1.堆的定义

2.堆的实现:数组实现 

1.堆的结构定义

2.堆的实现 

3.堆排序 

第一种:直接排序

第二种:建堆实现 

 4.堆排序中向下调整建堆和向上调整建堆时间复杂度比较

1.向下调整建堆

2.向上调整建堆 

5.堆实现TopK 问题

TopK问题就是从N个数中找出最大或最小的前K个

1.正常思路

2.改进思路

3.TopK问题的实现(用文件进行操作)

四:结束语 


接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧

前言 

数据结构的树是一种较复杂的结构,包含多种分支结构,如完全二叉树,满二叉树,堆,以及更难理解的AVL树,B数,B+树等等,这些结构共同组成了树的庞大结构体系,接下来我们将初步了解关于树,堆等结构

一:树

1.树的概念

树是一种非线性的数据结构,是由N个节点连接而成,当N为0时称为空树

2.树的基础概念知识

3.在树中孩子节点和父节点知一求一 

知孩子节点算父亲节点:parent=(child-1)/2;

知父节点求孩子节点:child=parent*2+1(假设只有一个孩子)

4.树的表示方法

左孩子右兄弟表示法

二:二叉树

1.二叉树的概念

是n个节点连接而成的有限集合,分为根节点和根节点的左右子树,且两棵子树不相交

2.二叉树的特性

1.每个节点最多两棵子树,即不存在度为2以上的节点

2.二叉树的左右子树是有顺序,不能颠倒的,为有序树

3.满二叉树 

每一层都是满结点的二叉树

4.完全二叉树 

前h-1层都是满的,最后一层可以不满,但是从左往右是连续的

三:堆 

1.堆的定义

1.堆属于完全二叉树的一种,分为大根堆和小根堆

2.大根堆:树的任何一个父节点都大于或等于孩子节点

3.小根堆:树的任何一个父节点都小于或等于孩子节点

2.堆的实现:数组实现 

1.堆的结构定义

2.堆的实现 

#include "Heap.h"
//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->a = NULL;
	php->capacity = php->size = 0;//指向最后一个有效数据的下一个
}
//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}
//堆中插入数据
void HeapPush(Heap* php, HeapDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HeapDataType* tmp = realloc(php->a, sizeof(HeapDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("malloc fail\n");
			return;
		}
		php->a = tmp;
		php->capacity = newcapacity;
	}
	php->a[php->size++] = x;
	

	//向上调整保持堆结构
	AdjustUp(php->a, php->size-1);

}

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

void AdjustUp(HeapDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child>0)
	{
		//小堆
		if (a[parent] > a[child])
		{
			//交换
			Swap(&a[parent], &a[child]);

			//迭代往上
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
//删除堆顶元素
void HeapPop(Heap* php)
{
	assert(php);
	assert(!HeapEmpty(php));
	Swap(&php->a[0], &php->a[php->size - 1]);

	php->size--;

	//向下调整
	AdjustDown(php->a, php->size, 0);
}

void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && a[child] > a[child + 1])
		{
			child++;
		}

		if (a[parent] > a[child])
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//判空
bool HeapEmpty(Heap* php)
{
	return php->size == 0;
}
//堆顶元素
HeapDataType HeapTop(Heap* php)
{
	return php->a[0];
}

3.堆排序 

堆排序作为一种排序方法,时间复杂度为O(N*log N),排序效率极佳,接下来我们将详细介绍

第一种:直接排序
void HeapSort(int* a, int n)
{
	Heap hp;
	HeapInit(&hp);
	int b[] = { 25,10,45,47,30,50 };
	int sz = sizeof(b) / sizeof(b[0]);
	for (int i = 0; i < sz; i++)
	{
		//插入数据调整为堆
		HeapPush(&hp, b[i]);
	}
	int i = 0;
	while (!HeapEmpty(&hp))
	{
		//再插入回数组
		int top = HeapTop(&hp);
		a[i++] = top;
		HeapPop(&hp);
	}
	printf("\n");
}

弊端:需要先写一个堆,太复杂

第二种:建堆实现 

 4.堆排序中向下调整建堆和向上调整建堆时间复杂度比较

1.向下调整建堆

2.向上调整建堆 

5.堆实现TopK 问题

TopK问题就是从N个数中找出最大或最小的前K个
1.正常思路

如果找N个数中最大的前K个,就得把N个数建为大堆,每Pop一次找到次大的数(Pop函数在每次Pop时会往下调整找出次大的数),Pop K次,即可找到前K个

如果当数据很大时,上面的思路就处理不了,比如N为10亿个整数,就得用4G的存储空间,此时数据就会存储在磁盘文件中,不允许随机访问等操作

2.改进思路

1.建立K个数的小堆

2.后N-K个数,依次与堆顶数据比较,大的话就替换入堆覆盖堆顶值,向下调整

3.最后这个小堆的值就是最大的前K个

3.TopK问题的实现(用文件进行操作)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void AdjustDown(int* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (a[child + 1] < a[child])
		{
			child++;
		}

		if (a[parent] > a[child])
		{
			int tmp = a[parent];
			a[parent] = a[child];
			a[child] = tmp;
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void CreateNData()
{
	//造数据
	int n = 1000;
	srand( (unsigned int) time(0) );
	const char* file = "data.txt";
	FILE* fin = fopen(file, 'w');
	if (fin == NULL)
	{
		perror("fopen error\n");
		return;
	}
	for (size_t i = 0; i < n; i++)
	{
		int x = rand() % 1000000;
		fprintf(fin,"%d",x);
	}
	fclose(fin);
}
void PrintTopK(int k)
{
	const char* file = "data.txt";
	FILE* fout = fopen(file, 'r');
	if (fout == NULL)
	{
		perror("fopen error\n");
		return;
	}

	//建立k个数的小堆
	int* kminheap = (int*)malloc(sizeof(int) * k);
	if (kminheap == NULL)
	{
		perror("malloc fail\n");
		return;
	}

	for (int i = 0; i < k; i++)
	{
		fscanf(fout, "%d", &kminheap[i]);
	}

	//建小堆
	for (int i = (k - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(kminheap, k, i);
	}

	//堆顶元素和N-K个数据比较,大的话替换入堆
	int val = 0;
	while (!feof(fout))
	{
		fscanf(fout, "%d", &val);
		if (val > kminheap[0])
		{
			kminheap[0] = val;
			AdjustDown(kminheap, k, 0);
		}
	}
	//打印数据
	for (int i = 0; i < k; i++)
	{
		printf("%d ",kminheap[i]);
	}
	printf("\n");
}
int main()
{
	CreateNData();
	PrintTopK(5);
	return 0;
}

四:结束语 

在初步了解这些二叉树和堆的结构和实现后,可以更方便为以后学习打下基础

低头往前冲吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值